Backing up Docker services

A "just in case" copy is useless if you can't restore from it. Let's look at how to take consistent copies of a Docker service's data, encrypt them and keep them in two places.

Consistency over speed

Simply copying the volume files of a running database gives a smeared snapshot: some pages have already changed, others haven't. The database may reject such a copy. The right way is to take a dump with the database's own tool, and copy file volumes separately.

#!/usr/bin/env bash
set -euo pipefail
DAY=$(date +%F)
TMP=$(mktemp -d)

# consistent database dump straight from the container
docker exec db pg_dump -U postgres app > "$TMP/db.sql"

# application files from the volume
docker run --rm -v app_data:/data -v "$TMP":/out alpine \
  tar czf /out/data.tgz -C /data .

# a single encrypted archive
tar cf - -C "$TMP" . | openssl enc -aes-256-cbc -pbkdf2 -salt \
  -pass file:/root/.backup-pass -out "/backups/app-$DAY.tar.enc"
rm -rf "$TMP"

Two storage locations

A copy next to the service won't help if the whole location goes down. Keep a second copy on another node or with another provider. We send the encrypted archive to a separate server right after it's created and make sure both copies are fresh.

Test recovery

A backup you've never restored is an assumption, not a guarantee. Every so often, bring a copy up on a test node and confirm the service starts. That's the only way to know the plan works before you need it.

← All notes