
Redis persistence determines which data Redis can reload after it stops. RDB snapshots capture a point in time; append-only files record writes for replay. Neither choice, by itself, proves that you can recover after losing the host. Choose the acceptable loss window, then test a separate recovery copy.
This guide explains the choice and gives you a small RDB-only exercise: create a snapshot, copy it out, check a restart, and restore into a different Docker volume. If you need the surrounding service setup, read Redis with Docker Compose. Keep that AOF-based lab separate from this one.
Validation, November 10, 2026: local checks cover Compose parsing, distinct source and restore resource names, credential generation and overwrite refusal, and the restore helper’s command ordering. Docker’s Linux engine was unavailable. Image pull, Redis configuration acceptance, authentication, Linux permissions, snapshots, restart, backup and restore, network isolation, and Hivenet deployment were not executed. All expected responses below are checks to perform, not observed results.
The Redis durability tutorial compares these persistence options and their operating costs.
Since Redis 7, AOF storage is a set of base and incremental files with a manifest. Copying a file called appendonly.aof is not a general modern backup procedure. Rewriting AOF compacts the history needed to rebuild the current dataset; it does not give you historical backup retention. See the Redis persistence documentation.
For a cache, write down how you will repopulate it and how the database will handle a cold start. For queued work, keep job state and retry behavior in the design. The Celery with Redis guide explains the application side. Restoring Redis cannot prove that each job ran exactly once or undo an external side effect.
A restart check reuses the original storage. A backup is a separate retained artifact. A restore check loads that artifact into an isolated target and verifies useful data. Passing the first check does not establish the other two. Record the source, capture time, Redis version, copy location, and restore result separately.
The exercise below copies a single manually completed RDB snapshot. It uses a new RDB-only server so AOF cannot take precedence at startup. Do not switch an existing Redis service to these settings, mount its volume here, or use live application data. Changes to a running persistence configuration need their own migration procedure.
Use a disposable Linux host with Docker Engine, Compose, Python 3, and a shell. Confirm that the engine responds. Both project names below must be unused, including retained volumes from previous runs. Stop and choose fresh names if either already exists.
docker version
docker compose version
python3 --version
mkdir redis-persistence-lab
cd redis-persistence-lab
Save the four files below in this new directory. Run each later command only after the previous check succeeds. The selected redis:8.10.3-alpine tag is listed in the official image manifest at this review. Record the pulled image digest for a reproducible deployment; a tag is not an immutable digest.
Save as compose.yaml:
name: redis-persistence-lab
services:
redis:
image: redis:8.10.3-alpine
command: [redis-server, /usr/local/etc/redis/redis.conf]
volumes:
- ./redis.conf:/usr/local/etc/redis/redis.conf:ro
- redis_data:/data
secrets: [redis_users, redis_password]
networks: [private]
mem_limit: 256m
restart: unless-stopped
healthcheck:
test: [CMD-SHELL, 'REDISCLI_AUTH="$$(cat /run/secrets/redis_password)" redis-cli --user operator ping | grep -qx PONG']
interval: 5s
timeout: 3s
retries: 10
start_period: 5s
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
client:
image: redis:8.10.3-alpine
profiles: [tools]
secrets: [redis_password]
networks: [private]
entrypoint:
- /bin/sh
- -c
- 'export REDISCLI_AUTH="$$(cat /run/secrets/redis_password)"; exec redis-cli --user operator -h redis "$$@"'
- --
command: [PING]
seed:
image: redis:8.10.3-alpine
profiles: [tools]
network_mode: none
volumes:
- redis_data:/data
- type: bind
source: ./backup/dump.rdb
target: /backup/dump.rdb
read_only: true
bind:
create_host_path: false
entrypoint: [/bin/sh, -ec]
command:
- |
test -z "$$(ls -A /data)" || { echo "Refusing nonempty restore volume" >&2; exit 1; }
redis-check-rdb /backup/dump.rdb
cp /backup/dump.rdb /data/dump.rdb
chown redis:redis /data/dump.rdb
networks:
private:
internal: true
volumes:
redis_data:
secrets:
redis_users:
file: ./secrets/users.acl
redis_password:
file: ./secrets/redis_password
The server has no published port. The client connects over an internal network and reads a mounted password through REDISCLI_AUTH. The seed helper has no network and refuses a nonempty target volume. Its backup bind is read-only and will not create a missing host path. These declarations were parsed locally, but their runtime behavior remains untested. The Compose service reference describes these settings.
The source project creates its own named volume and network. The later -p redis-restore-check override gives the restore project different resources. Do not add an explicit shared volume name or attach either project to another application. This lab has no TLS and is limited to a trusted single host; an administrator with Docker access can still read its data and credentials.
Save as redis.conf:
bind 0.0.0.0
protected-mode yes
port 6379
aclfile /run/secrets/redis_users
dir /data
dbfilename dump.rdb
save ""
appendonly no
maxmemory 64mb
maxmemory-policy noeviction
The empty save rule disables scheduled snapshots; appendonly no disables AOF. You will request a snapshot explicitly. The 64 MiB Redis memory limit and 256 MiB container cap are small lab values, not sizing recommendations. Snapshot work also consumes resources. Consult the Redis 8.10 configuration reference before applying persistence or memory settings to a workload.
Save as prepare_secrets.py:
"""Create disposable lab credentials once; never print or overwrite them."""
import hashlib
import secrets
from pathlib import Path
def main():
directory = Path(__file__).resolve().parent / "secrets"
directory.mkdir(mode=0o700, exist_ok=False)
password = secrets.token_hex(32)
password_hash = hashlib.sha256(password.encode("ascii")).hexdigest()
files = {
"redis_password": password + "\n",
"users.acl": (
"user default off\n"
f"user operator on #{password_hash} ~persist:* -@all "
"+ping +get +set +exists +dbsize +info +lastsave +bgsave\n"
),
}
for name, content in files.items():
path = directory / name
with path.open("x", encoding="ascii", newline="\n") as handle:
handle.write(content)
path.chmod(0o444)
print("Created lab credentials in secrets/. Keep this directory private.")
if __name__ == "__main__":
main()
Save as .gitignore:
secrets/
backup/
__pycache__/
python3 prepare_secrets.py
docker compose config --quiet
docker compose --profile tools config --quiet
docker compose -p redis-restore-check --profile tools config --quiet
The helper refuses to overwrite its secrets directory and does not print the generated password. It requests a private Linux directory with readable files for container mounts. Verify permissions on your actual host; the Windows checks do not establish Linux enforcement. Local Compose secrets are file mounts, not an encrypted vault. Keep the directory out of Git, shared archives, and backup packages.
This is an operator account for a disposable lab, not an application account. Key access is restricted to persist:*, but INFO, DBSIZE, LASTSAVE, and BGSAVE inspect or affect the database globally. Key patterns do not constrain those operations. Do not reuse the account as a Celery broker credential. Review the Redis ACL reference for an application’s actual command requirements.
docker compose pull redis
docker compose up -d --wait --wait-timeout 120 redis
docker compose run --rm client PING
docker compose run --rm client DBSIZE
Expect PONG and a database size of zero. Stop if the database is not empty: you may have reused a volume. Inspect startup logs if the health check fails. Parsing Compose does not prove that Redis accepted its configuration or credentials.
docker compose run --rm client SET persist:marker snapshot-v1
docker compose run --rm client GET persist:marker
docker compose run --rm client LASTSAVE
Expect OK and then snapshot-v1. Record LASTSAVE’s timestamp before the next step. The marker deliberately has no expiry, so a later restore check does not confuse expiration with missing persistence.
sleep 2
docker compose run --rm client BGSAVE
docker compose run --rm client INFO persistence
docker compose run --rm client LASTSAVE
BGSAVE starts background work; its acknowledgment is not a completion receipt. Repeat INFO and LASTSAVE until rdb_bgsave_in_progress:0, rdb_last_bgsave_status:ok, and a LASTSAVE timestamp newer than the recorded value all agree. The short delay avoids comparing saves within the same timestamp second. Stop on an error or a save that does not finish; do not copy an older file and call it this snapshot. See BGSAVE, INFO persistence fields, and LASTSAVE.
Keep all writers stopped except the commands in this exercise. Create a new private backup directory, copy the completed snapshot, and record its checksum:
mkdir -m 700 backup
docker compose cp redis:/data/dump.rdb backup/dump.rdb
sha256sum backup/dump.rdb > backup/dump.rdb.sha256
sha256sum -c backup/dump.rdb.sha256
Stop if the backup directory already exists. Compose cp copies the file out of the container. The checksum detects a changed copy; it does not prove that Redis can load it or that the dataset satisfies your application. This host-local copy is separate from the Redis volume but still shares the host’s failure risk.
docker compose up -d --force-recreate --wait --wait-timeout 120 redis
docker compose run --rm client GET persist:marker
Expect snapshot-v1. Record this as a graceful container-recreation check only. It does not measure sudden power-loss behavior, the AOF loss window, or recovery after losing the machine. Do not rewrite a missing marker to make the check pass.
docker compose run --rm client SET persist:marker after-snapshot-v2
docker compose run --rm client GET persist:marker
Expect after-snapshot-v2 on the source. Do not take another snapshot or replace the copied file. The different values will help distinguish a real restore of the earlier copy from accidentally connecting to the source.
Keep the source running. Confirm that redis-restore-check is a fresh project with no running Redis service or existing data. The helper must run before the restore server starts. Do not run it against the source project.
sha256sum -c backup/dump.rdb.sha256
docker compose -p redis-restore-check --profile tools run --rm seed
docker compose -p redis-restore-check up -d --wait --wait-timeout 120 redis
docker compose -p redis-restore-check run --rm client GET persist:marker
docker compose -p redis-restore-check run --rm client DBSIZE
docker compose run --rm client GET persist:marker
The helper first checks that its target directory is empty, runs redis-check-rdb, copies the snapshot, and assigns the file to the image’s Redis user. A nonempty target is a stop condition, not a reason to delete files and retry. Review the failure before choosing another fresh restore project.
Expected results are snapshot-v1 and a size of one on the restore, while the source still returns after-snapshot-v2. Check startup logs as well. These observations would establish recovery of this one nonexpiring marker from this copied RDB into a separate volume. They would not validate a complete application, an AOF recovery path, or an off-host disaster-recovery process. None of these runtime results has been observed for this article.
Redis 8.10 also introduces BACKUP START and related commands for online backups. That is a separate workflow with a base file, incremental writes, and a manifest; it is not exercised here. Follow the version-specific start, status, seal, copy, and restore instructions before using it. A command being available does not make an unfinished backup ready to restore.
For older or differently configured AOF deployments, use the supported procedure for the complete file set. Do not copy a changing AOF directory piecemeal or assume that an RDB placed next to enabled AOF will be selected at startup. Keep the original backup intact when investigating corruption; a repair that discards data changes what you can recover.
For data you need to retain, protect and transfer a recovery copy outside the VM, define retention, and rehearse a restore on an isolated host. Verify credentials, file integrity, representative keys, application behavior, and the age of the recovered data. Record the recovery time from actual runs. A second directory or Docker volume on the same host does not cover host loss.
docker compose -p redis-restore-check down
docker compose down
These commands retain named volumes. Do not add -v or --volumes unless you intend to delete their data. Preserve the configuration, protected credentials, copied snapshot, checksum, and your check results if you plan to investigate or repeat the exercise.
If you are choosing between operating Redis yourself and buying a managed service, return to the Redis hosting guide. Hivenet’s CPU VM documentation describes a development and testing scope. Its instance lifecycle rules state that a stopped instance expires after 10 days and its local data is deleted. A named volume does not override those rules. Cloud provisioning, storage behavior, and recovery remain untested here.
Pick one AI, compute, or storage workload and see the difference for yourself. Spin it up in minutes, or let our team map your fastest path to production.