backup Docker volumes bằng Restic - server room và hệ thống lưu trữ

Backup Docker volumes bằng Restic: cách làm chuẩn

Bạn đang chạy một stack Docker Compose gọn gàng: app, PostgreSQL, Redis, Nginx/Caddy, vài volume mount vào /opt/app. Mọi thứ chạy ổn cho đến ngày server lỗi disk, lỡ tay xóa volume, hoặc migrate VPS gấp. Lúc đó câu hỏi không phải “có backup không?”, mà là backup đó restore được không.

Bài này hướng dẫn cách backup Docker volumes bằng Restic theo hướng thực dụng: dump database trước để nhất quán dữ liệu, backup bind mounts/named volumes bằng Restic, đặt retention policy, kiểm tra repository và restore thử. Mục tiêu là có một workflow đủ an toàn cho self-hosted server, staging hoặc VPS production nhỏ.

Nguyên tắc chính: không backup raw database volume đang chạy rồi tin rằng dữ liệu luôn nhất quán. Với PostgreSQL/MySQL/MariaDB, hãy tạo logical dump trước, sau đó mới đưa dump vào Restic snapshot.

backup Docker volumes - hệ thống lưu trữ trong datacenter
Với Docker, dữ liệu quan trọng thường nằm ở bind mount, named volume và database dump. Photo by Leonardo Rizzi on Flickr (CC BY-SA).

Backup Docker volumes bằng Restic giải quyết vấn đề gì?

Docker làm deploy rất nhanh, nhưng dữ liệu thật thường nằm ngoài image: bind mount như ./data:/app/data, named volume như postgres_data, file upload của user, config runtime, certificate, log quan trọng hoặc database. Container có thể recreate, image có thể pull lại, nhưng volume mất là mất dữ liệu.

Restic phù hợp vì nó mã hóa dữ liệu trước khi gửi lên repository, deduplicate block để tiết kiệm dung lượng, hỗ trợ nhiều backend như local disk, SFTP, S3-compatible storage, Backblaze B2, rclone backend. Điểm mạnh nhất là restore đơn giản: snapshot nào, path nào, restore về đâu đều rõ ràng.

Tuy nhiên Restic không tự biến một filesystem đang thay đổi liên tục thành backup nhất quán. Vì vậy với Docker, ta chia dữ liệu thành hai nhóm: dữ liệu file có thể backup trực tiếp, và database cần dump trước.

Kiến trúc backup đề xuất cho Docker Compose

Một setup dễ vận hành là đặt toàn bộ stack dưới /opt/stacks, dump database ra /opt/backups/dumps, sau đó dùng Restic backup cả hai thư mục này vào repository từ xa. Nếu dùng named volume, bạn có thể mount volume đó vào một container phụ hoặc backup trực tiếp đường dẫn /var/lib/docker/volumes/.../_data, nhưng cách dễ kiểm soát hơn vẫn là ưu tiên bind mount cho dữ liệu cần backup.

  • /opt/stacks: chứa compose file, env mẫu, app data dạng bind mount.
  • /opt/backups/dumps: chứa PostgreSQL/MySQL dump được tạo trước mỗi lần backup.
  • Restic repository: S3/SFTP/local NAS, luôn có password riêng và được kiểm tra định kỳ.
  • systemd timer: chạy backup tự động thay vì nhớ chạy tay.
/opt/stacks/
  myapp/
    docker-compose.yml
    .env
    data/
    uploads/
/opt/backups/
  dumps/
  scripts/
    backup-docker-restic.sh
  restic.env

Nếu stack của bạn đang dùng Caddy làm reverse proxy, có thể liên kết nội bộ với bài Caddy reverse proxy cho Docker. Phần scheduling bên dưới cũng có thể dùng chung tư duy với bài systemd timer thay cron.

Chuẩn bị Restic repository và file secrets

Ví dụ dưới đây dùng S3-compatible storage. Với SFTP hoặc local disk, bạn chỉ cần đổi RESTIC_REPOSITORY. Không nên hardcode password vào script backup; đặt trong file env có permission chặt.

sudo mkdir -p /opt/backups/{dumps,scripts}
sudo install -m 600 /dev/null /opt/backups/restic.env
sudo nano /opt/backups/restic.env
export RESTIC_REPOSITORY="s3:https://s3.example.com/docker-backups"
export AWS_ACCESS_KEY_ID="YOUR_ACCESS_KEY"
export AWS_SECRET_ACCESS_KEY="YOUR_SECRET_KEY"
export RESTIC_PASSWORD="use-a-long-random-password"

Khởi tạo repository một lần duy nhất:

set -a
source /opt/backups/restic.env
set +a
restic init

Nếu repository đã tồn tại, restic snapshots phải trả về danh sách snapshot hoặc repo trống. Đây là bước kiểm tra nhanh credential và network trước khi automate.

Dump PostgreSQL trước khi backup

PostgreSQL nên được dump bằng pg_dump hoặc pg_dumpall. Nếu database chạy trong container, chạy dump bằng docker exec giúp dùng đúng version client bên trong image PostgreSQL. Với production lớn, bạn có thể dump từng database bằng custom format -Fc; với VPS nhỏ, pg_dumpall | gzip thường đủ đơn giản.

#!/usr/bin/env bash
set -euo pipefail

APP_NAME="myapp"
PG_CONTAINER="myapp-postgres-1"
PG_USER="postgres"
DUMP_DIR="/opt/backups/dumps"
TS="$(date +%Y%m%d-%H%M%S)"

mkdir -p "$DUMP_DIR"

docker exec "$PG_CONTAINER"   pg_dumpall -U "$PG_USER"   | gzip -9 > "$DUMP_DIR/${APP_NAME}-postgres-${TS}.sql.gz"

# giữ local dump vài ngày để tiết kiệm disk
find "$DUMP_DIR" -type f -name "${APP_NAME}-postgres-*.sql.gz" -mtime +7 -delete

Với MySQL/MariaDB, thay bằng mysqldump. Điểm quan trọng vẫn giống nhau: tạo dump nhất quán trước, rồi để Restic backup file dump đó.

Script backup Docker volumes bằng Restic

Sau khi có dump, backup các thư mục cần thiết. Tránh backup toàn bộ /var/lib/docker vì nó chứa image layer, overlay2, build cache, log runtime — vừa nặng vừa khó restore sạch. Hãy backup đúng data path của stack.

sudo nano /opt/backups/scripts/backup-docker-restic.sh
sudo chmod 750 /opt/backups/scripts/backup-docker-restic.sh
#!/usr/bin/env bash
set -euo pipefail

set -a
source /opt/backups/restic.env
set +a

HOST_TAG="$(hostname -s)"
APP_NAME="myapp"
STACK_DIR="/opt/stacks/myapp"
DUMP_DIR="/opt/backups/dumps"
PG_CONTAINER="myapp-postgres-1"
PG_USER="postgres"
TS="$(date +%Y%m%d-%H%M%S)"

mkdir -p "$DUMP_DIR"

echo "[1/4] Dump PostgreSQL..."
docker exec "$PG_CONTAINER" pg_dumpall -U "$PG_USER"   | gzip -9 > "$DUMP_DIR/${APP_NAME}-postgres-${TS}.sql.gz"

echo "[2/4] Run restic backup..."
restic backup   "$STACK_DIR"   "$DUMP_DIR"   --host "$HOST_TAG"   --tag docker   --tag "$APP_NAME"

echo "[3/4] Apply retention policy..."
restic forget   --host "$HOST_TAG"   --tag "$APP_NAME"   --keep-daily 7   --keep-weekly 4   --keep-monthly 6   --prune

echo "[4/4] Quick repository check..."
restic check

find "$DUMP_DIR" -type f -name "${APP_NAME}-postgres-*.sql.gz" -mtime +7 -delete

echo "Backup completed."

Với database lớn, không nên chạy forget --prune mỗi lần backup vì prune có thể lock repository lâu. Khi đó hãy tách prune sang timer riêng chạy tuần/lần, còn backup hằng ngày chỉ chạy restic backup và có thể restic forget không prune.

Lên lịch bằng systemd timer

Cron vẫn dùng được, nhưng systemd timer dễ xem log bằng journalctl, quản lý dependency tốt hơn và hợp server Linux hiện đại. Tạo service chạy script backup:

sudo nano /etc/systemd/system/docker-restic-backup.service
[Unit]
Description=Backup Docker volumes and database dumps with Restic
Wants=network-online.target
After=network-online.target docker.service

[Service]
Type=oneshot
ExecStart=/opt/backups/scripts/backup-docker-restic.sh
Nice=10
IOSchedulingClass=best-effort
IOSchedulingPriority=7

Tạo timer chạy mỗi ngày lúc 02:30:

sudo nano /etc/systemd/system/docker-restic-backup.timer
[Unit]
Description=Daily Docker Restic backup

[Timer]
OnCalendar=*-*-* 02:30:00
Persistent=true
RandomizedDelaySec=15m

[Install]
WantedBy=timers.target
sudo systemctl daemon-reload
sudo systemctl enable --now docker-restic-backup.timer
systemctl list-timers docker-restic-backup.timer
sudo systemctl start docker-restic-backup.service
journalctl -u docker-restic-backup.service -n 100 --no-pager
Restic restore Docker volumes - terminal Linux kiểm tra backup
Backup chỉ có giá trị khi restore test chạy được trên môi trường tách biệt. Photo by Sandro Doro on Flickr (CC0).

Restore test: bước nhiều người bỏ qua nhưng quan trọng nhất

Một backup chưa được restore test chỉ là “hy vọng”. Ít nhất mỗi tháng, hãy restore snapshot mới nhất vào thư mục tạm, dựng lại stack ở môi trường khác hoặc staging, rồi import database dump để kiểm tra app có chạy không.

set -a
source /opt/backups/restic.env
set +a

restic snapshots --tag myapp
sudo mkdir -p /restore-test/myapp
restic restore latest --target /restore-test/myapp --tag myapp

Sau restore, bạn sẽ thấy cấu trúc path gốc bên trong target. Copy dữ liệu về đúng thư mục staging, giải nén dump và import vào PostgreSQL container mới:

cd /restore-test/myapp/opt/stacks/myapp
docker compose up -d postgres

gunzip -c /restore-test/myapp/opt/backups/dumps/myapp-postgres-YYYYMMDD-HHMMSS.sql.gz   | docker exec -i myapp-postgres-1 psql -U postgres

Với pg_dumpall, file dump có thể chứa nhiều database/role. Với pg_dump -Fc, dùng pg_restore thay vì psql. Hãy ghi lại runbook restore thật rõ: restore snapshot nào, copy path nào, import database ra sao, DNS/certificate cần đổi gì.

Checklist hardening cho backup Docker production

  • Dùng env_file hoặc file root-only 0600 cho secrets; không commit RESTIC_PASSWORD lên Git.
  • Không backup /var/lib/docker/overlay2, image cache hoặc log tạm không cần thiết.
  • Database đang chạy thì dump logical trước; không chỉ copy raw volume rồi xem là an toàn.
  • Bật retention rõ ràng: daily/weekly/monthly, có prune theo lịch riêng nếu repo lớn.
  • Chạy restic check định kỳ; thỉnh thoảng dùng --read-data-subset để kiểm tra sâu hơn.
  • Có alert khi backup fail: systemd mail hook, Uptime Kuma push, Telegram bot hoặc monitoring stack.
  • Restore test vào staging theo lịch, không đợi sự cố thật mới thử.

FAQ về backup Docker volumes bằng Restic

Có nên backup trực tiếp named volume của PostgreSQL không?

Không nên xem đó là phương án chính nếu database đang chạy. Raw volume backup có thể không nhất quán. Hãy dùng pg_dump/pg_dumpall trước, sau đó backup dump bằng Restic.

Restic có thay thế snapshot của VPS provider không?

Không hoàn toàn. Snapshot VPS giúp rollback nhanh toàn máy, còn Restic giúp backup cấp file, mã hóa, deduplicate và restore chọn lọc. Tốt nhất dùng cả hai nếu dữ liệu quan trọng.

Backup hằng ngày có đủ không?

Tùy RPO. Blog nhỏ hoặc app nội bộ có thể đủ với daily backup. App có đơn hàng/user data liên tục nên dump nhiều lần/ngày hoặc dùng replication/WAL archiving cho PostgreSQL.

Restic repository để cùng server có ổn không?

Chỉ ổn như bản copy tạm. Backup thật nên có ít nhất một bản offsite: S3-compatible storage, NAS khác máy, SFTP server khác hoặc cloud object storage.

Kết luận

Backup Docker volumes bằng Restic là cách gọn, an toàn và dễ tự động hóa cho server tự host. Công thức nên nhớ: database dump trước, backup đúng data path, mã hóa bằng Restic, retention rõ ràng, check repository và restore test định kỳ. Khi sự cố xảy ra, thứ cứu bạn không phải file backup nằm đâu đó, mà là một quy trình restore đã được thử và ghi lại.

Leave a Comment

Your email address will not be published. Required fields are marked *