Definition
- Checkpoint – Automatic, periodic snapshot taken and owned by Flink purely for fault tolerance; optimized for low overhead, may use incremental format, and is typically deleted when superseded.
- Savepoint – User-triggered snapshot stored in a stable, portable format designed for operational actions such as upgrades, rescaling, and state migration; owned by the user and persisted until explicitly deleted.
- Portability – Savepoints work across Flink versions and topology changes as long as operators have stable unique IDs via
uid(...). - Lifecycle – Checkpoints are managed by Flink; savepoints are managed by the operations team.
- Format – Checkpoints may be backend-specific and incremental; savepoints use a canonical, self-contained format.
The National Bank of India's system takes two kinds of backups. A checkpoint is like the automatic, frequent autosave the system makes every few seconds so that if the power trips it can recover to a moment ago. Nobody asks for it, and old autosaves are discarded. A savepoint is like a deliberate, labeled full export the operations team requests before upgrading the software or adding more servers. It is a clean, portable archive the team names and keeps until they decide to delete it.
Understanding the Concepts
What is a Flink checkpoint?
A checkpoint is triggered automatically by the checkpoint coordinator on the interval you configure. It is owned and managed by Flink, which decides when to create and delete it. Checkpoints prioritize low overhead.
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
StreamExecutionEnvironment env =
StreamExecutionEnvironment.getExecutionEnvironment();
// Automatic checkpoints every 10 seconds
env.enableCheckpointing(10_000);
env.getCheckpointConfig().setCheckpointStorage(
"s3://nbi-flink-checkpoints/");
// Checkpoints are managed by Flink:
// - Created automatically on interval
// - Old ones deleted when newer completes
// - May be incremental (RocksDB)
What is a Flink savepoint?
A savepoint is triggered manually by an operator, stored in a portable format, and persists until explicitly deleted. It is designed for planned lifecycle operations.
# Take a savepoint
flink savepoint <jobId> s3://nbi-savepoints/fraud-scoring-v2
# Stop with final savepoint (graceful)
flink stop --savepoint s3://nbi-savepoints/pre-upgrade <jobId>
# Restore from savepoint
flink run -s s3://nbi-savepoints/pre-upgrade \
--allowNonRestoredState \
fraud-scoring-v3.jar
How are they different?
| Aspect | Checkpoint | Savepoint |
|---|---|---|
| Trigger | Automatic (periodic) | Manual (user-initiated) |
| Owned by | Flink | Operations team |
| Purpose | Crash recovery | Upgrades, rescaling, migration |
| Format | May be backend-specific, incremental | Canonical, portable, self-contained |
| Lifecycle | Deleted when superseded or job stops | Persists until explicitly deleted |
| Portability | Not guaranteed across versions | Designed for cross-version restore |
| Speed | Optimized for low overhead | May be heavier (full format) |
// Checkpoint: you never think about it
// Job crashes → Flink restores from latest checkpoint automatically
// Savepoint: deliberate operation
// Deploy new version → take savepoint → stop old → start new from savepoint
Why are operator UIDs essential for savepoints?
Savepoint restore matches state to operators by their assigned unique IDs. Without stable UIDs, a code change can orphan state and fail the restore.
import org.apache.flink.streaming.api.datastream.DataStream;
// CORRECT -- stable UIDs on every stateful operator
DataStream<String> result = txnStream
.keyBy(txn -> txn.getAccountId())
.process(new BalanceTracker())
.uid("balance-tracker") // stable UID
.name("Balance Tracker");
result
.keyBy(alert -> alert.getAccountId())
.process(new FraudScorer())
.uid("fraud-scorer") // stable UID
.name("Fraud Scorer");
// WRONG -- no UIDs, savepoint restore breaks on code change
txnStream.keyBy(txn -> txn.getAccountId())
.process(new BalanceTracker()); // no uid()!
// If code changes reorder operators, state cannot be matched
When do you use each?
Checkpoints are always on for crash recovery. Savepoints are for planned lifecycle events: code upgrades, parallelism changes, Flink version upgrades, state migration, and debugging.
# Scenario 1: Code upgrade
flink stop --savepoint s3://nbi-savepoints/v2-pre-upgrade <jobId>
flink run -s s3://nbi-savepoints/v2-pre-upgrade fraud-scoring-v3.jar
# Scenario 2: Rescale from 4 to 8 slots
flink stop --savepoint s3://nbi-savepoints/pre-rescale <jobId>
flink run -s s3://nbi-savepoints/pre-rescale -p 8 fraud-scoring.jar
# Scenario 3: Debug a production issue
flink savepoint <jobId> s3://nbi-savepoints/debug-snapshot
# Restore locally with small test data for debugging
What about checkpoint retention?
By default checkpoints are deleted when the job stops. You can configure retention so a checkpoint survives cancellation for emergency recovery.
import org.apache.flink.streaming.api.environment.CheckpointConfig;
// Retain checkpoint on cancellation
env.getCheckpointConfig().setExternalizedCheckpointCleanup(
CheckpointConfig.ExternalizedCheckpointCleanup
.RETAIN_ON_CANCELLATION);
// Now the last checkpoint survives job cancellation
// Useful for emergency recovery when savepoint was not taken
What are common mistakes?
The most common mistake is not assigning stable UIDs to stateful operators, which makes savepoint restore fail on code changes. Another is relying solely on checkpoints for planned upgrades, which may not be portable.
// WRONG -- no UIDs on stateful operators
// Savepoint taken, code refactored, restore fails!
// CORRECT -- always assign uid() to every stateful operator
stream.process(new BalanceTracker()).uid("balance-tracker");
stream.process(new FraudScorer()).uid("fraud-scorer");
// Savepoint restore matches state by UID, code changes are safe
What should a senior engineer know?
Test savepoint-restore as part of your deployment pipeline. Discovering a broken state migration in production during an upgrade is extremely costly for a bank. Assign stable UIDs from day one, take savepoints before every planned change, and retain at least one checkpoint on cancellation as a safety net.
Visual Overview
Checkpoint vs Savepoint Lifecycle
Checkpoints (automatic, Flink-owned):
──▶ CP1 ──▶ CP2 ──▶ CP3 ──▶ CP4 ──▶ CP5
│ │ │ │ │
✗ ✗ ✗ ✗ ✓ (latest kept)
Old ones automatically deleted
Used for: automatic crash recovery
Savepoints (manual, user-owned):
──▶ SP-v1 ────────── SP-v2 ────────── SP-v3
│ │ │
✓ (kept) ✓ (kept) ✓ (kept)
Persist until explicitly deleted
Used for: upgrades, rescaling, debugging
Upgrade workflow:
┌───────────┐ ┌───────────┐ ┌───────────┐
│ Job v2 │──▶ │ Savepoint │──▶ │ Job v3 │
│ running │ │ taken │ │ restored │
│ │ │ (portable)│ │ from SP │
└───────────┘ └───────────┘ └───────────┘
Requires uid() on every stateful operator
Key Takeaway
Checkpoints and savepoints are both consistent snapshots of a Flink job's state, but they serve different purposes. A checkpoint is automatic, periodic, owned by Flink, and optimized for low-overhead crash recovery. Old checkpoints are deleted when newer ones complete, and the format may be backend-specific and incremental. You never think about checkpoints during normal operation; if a task manager dies, Flink restores silently.
A savepoint is user-triggered, stored in a stable portable format, and persisted until you explicitly delete it. Its purpose is planned lifecycle operations: code upgrades, parallelism changes, Flink version migrations, and debugging. It works across versions and topology changes as long as operators carry stable UIDs assigned with uid(...), because savepoint restore matches state to operators by those IDs.
The critical practice is assigning a uid() to every stateful operator from day one. Without it, any code refactoring breaks savepoint restore. Test savepoint-restore as part of your deployment pipeline, because a broken state migration discovered during a production upgrade is extremely costly.
At the National Bank of India the fraud-scoring job ran with automatic checkpoints for crash recovery and the team took savepoints before every deployment. When deploying a new fraud model that added operators and scaled from 4 to 8 slots, they stopped with a savepoint, started the new version from it, and accumulated per-account scores carried cleanly because every operator had a stable UID.
Remember one sentence: Checkpoints are cheap, automatic, Flink-owned snapshots for crash recovery, while savepoints are portable, retained, user-owned snapshots for planned changes, and stable operator UIDs are essential for savepoint restore.
---