Redis — Persistence: RDB & AOF
Redis lives in RAM by default — this chapter is about the specific, tunable trade-off between how fast it restarts and how much you're willing to lose if it doesn't restart cleanly.
Learning objectives
- Explain the RDB vs. AOF trade-off in terms of restart speed vs. data-loss window.
- Choose the correct appendfsync setting for a given durability requirement.
- Explain why many production Redis deployments run with persistence disabled entirely.
◆ Story
A Redis instance holding session data for a live web app restarts — a routine deploy, an OOM kill, a hardware fault, it doesn't matter which. RAM is wiped the instant the process stops. Every session, every cached value, every counter that only ever lived in that instance's memory is gone in that same instant, with nothing to recover from — there was never a disk copy to replay, the way a crashed relational database recovers via its write-ahead log (Transaction Mastery, Durability chapter).
Redis's persistence options — RDB and AOF, this chapter's two subjects — exist to make that restart survivable when it needs to be. It's worth being precise about what persistence is not, because the terms get conflated constantly: persistence is not a backup (a backup is a separate, independently-stored copy you can restore from after a bad write or corruption — see NoSQL Foundations, Chapter 4's backup-vs-replication distinction, which applies to Redis just as much as any other database) and it is not replication (Chapter 03 — a second live copy on another node). Persistence is narrower and more local than either: it's whether this one process, restarting on this one disk, can reconstruct its own dataset afterward.
RDB (Redis Database) persistence writes the entire dataset to a single compact file on disk, at a point in time, via SAVE (blocking — freezes the instance until the write completes) or BGSAVE (forks a child process to write the snapshot in the background while the main thread keeps serving commands). In practice, BGSAVE is what runs automatically, triggered by save-point rules in configuration — the classic defaults are phrased as "save if at least 1 key changed in 900 seconds, or at least 10 keys changed in 300 seconds, or at least 10,000 keys changed in 60 seconds," giving you a snapshot cadence that tightens automatically under heavier write load.
The trade-off is exactly what "point-in-time snapshot" implies: restart is fast, because loading one compact binary file back into memory is a single sequential read, but you can lose everything written since the last snapshot completed — if the last BGSAVE finished 4 minutes ago and the process dies now, those 4 minutes of writes are gone, not partially recoverable.
💻 Code example
# redis.conf — classic default save points save 900 1 save 300 10 save 60 10000 # trigger a snapshot manually, in the background BGSAVE
AOF (Append-Only File) takes the opposite approach: instead of a periodic full snapshot, it logs every write command sequentially to a file as it happens, and reconstructs the dataset on restart by replaying that log from the start. This is the same structural idea as a relational database's write-ahead log (Transaction Mastery, Durability chapter) — append first, fast and sequential, then let the actual reconstruction happen on demand — applied to whole commands rather than row-level changes.
How much you can lose on a crash is governed by appendfsync, and this is the exact same trade-off Transaction Mastery's Durability chapter covers for synchronous_commit — trading commit latency against a data-loss window:
appendfsync value | What it promises | Cost |
|---|---|---|
always | fsync to disk after every single write — effectively zero data-loss window | Highest latency; every write waits on a disk flush |
everysec (default) | fsync once per second, batching all writes in that window | Up to ~1 second of writes lost on a hard crash; the standard balance in practice |
no | Let the OS decide when to flush | Fastest, but the loss window is however long the OS chooses to buffer — unpredictable, not just "a second" |
Because AOF logs commands rather than a periodic snapshot, its restart is slower (replaying a long log of individual writes takes longer than loading one binary snapshot) and its file is typically larger for the same dataset — which is what BGREWRITEAOF addresses, compacting the log down to the minimal set of commands needed to reproduce the current dataset, dropping the intermediate history.
💻 Code example
# redis.conf appendonly yes appendfsync everysec # compact the AOF file down to the minimal command set BGREWRITEAOF
| RDB | AOF | |
|---|---|---|
| What it stores | A full snapshot at a point in time | Every write command, sequentially |
| Restart speed | Fast — one file, one sequential load | Slower — replays the whole log |
| File size | Compact | Larger for the same dataset (until rewritten) |
| Data-loss window | Minutes, depending on save-point cadence | Up to ~1 second with everysec, near-zero with always |
| Best for | Fast recovery, backups, replica bootstrapping | Minimizing data loss on crash |
Redis can run both simultaneously — RDB for fast restarts and as the file replicas bootstrap from (Chapter 03), AOF for the tighter durability guarantee — and on restart with both enabled, Redis reconstructs from AOF, since it's the more complete, more recent record.
▲ Common mistake
Assuming AOF gives Redis the same durability semantics as a relational database's COMMIT. It doesn't, fully — AOF logs the commands you send, one at a time; if a client crashes mid-way through a sequence of related writes that was never wrapped in MULTI (Chapter 05), AOF will faithfully persist exactly that partial, inconsistent state. AOF protects against the Redis process losing acknowledged writes on crash; it does nothing to make an unrelated multi-command operation atomic — that's what MULTI/EXEC and Lua scripting are for.
◆ Real-world example
A Redis instance sits in front of Postgres purely as a cache-aside layer (Chapter 04): every key it holds is a copy of something that also lives, durably, in Postgres. If this instance is wiped — crash, restart, whatever — every single key simply becomes a cache miss on the next read, and gets repopulated from Postgres exactly as if it had expired normally. Nothing is lost, because nothing here was ever the only copy.
In that specific situation, RDB and AOF are pure cost with no correctness benefit: every write pays snapshot or fsync overhead to protect data that didn't need protecting, since the source of truth already protects it. This is exactly why it's common, and correct, to run a pure-cache Redis instance with persistence disabled outright.
The moment that assumption stops holding — the moment Redis itself is a primary store for some piece of data with no other copy anywhere (a @RedisHash entity in Chapter 06, or a Streams-based event log in Chapter 05) — persistence stops being optional, because now a wipe genuinely means permanent data loss, and RDB/AOF are the only thing standing between a restart and that outcome for data that lives only in Redis.
Want a visual for this concept?
Generate a diagram tailored to “Redis — Persistence: RDB & AOF” — the AI picks whichever visual (flowchart, comparison, sequence, etc.) best fits.
Sign in to generate a visual →