Backup cho Docker container và volume: đừng chỉ backup mỗi code
Backup cho Docker container và volume: đừng chỉ backup mỗi code
Bạn có bao giờ chạy docker compose down -v để "dọn dẹp" rồi bàng hoàng nhận ra toàn bộ dữ liệu database đã bốc hơi? Hoặc tự tin rằng "code đã push lên Git là an toàn", đến khi server gặp sự cố mới phát hiện không ai backup volume chứa dữ liệu khách hàng suốt hai năm qua?
Đây là sai lầm phổ biến nhất khi vận hành Docker: backup mỗi code mà quên mất dữ liệu. Bài viết hướng dẫn backup Docker volume và toàn bộ những gì cần backup trong một hệ thống container — từ volume, bind mount, database đến file cấu hình — kèm cách restore và tự động hóa.
Backup docker volume: vì sao backup code là chưa đủ
Git bảo vệ code của bạn rất tốt, nhưng một ứng dụng Docker thực tế gồm nhiều thành phần có trạng thái (stateful) nằm ngoài Git: database trong container Postgres/MySQL, file upload của người dùng trong volume, file cấu hình .env chứa key bí mật, và cả chính file docker-compose.yml nếu bạn sửa trực tiếp trên server mà quên commit.
Nguy hiểm hơn, Docker tạo ra ảo giác an toàn: container chạy ổn định nhiều tháng khiến bạn quên mất dữ liệu đang nằm ở đâu. Đến khi ổ đĩa server đầy, hoặc ai đó chạy nhầm lệnh xóa volume — lúc đó mới đi tìm backup thì đã muộn.
Quy tắc cần khắc cốt ghi tâm: container là tạm thời, dữ liệu phải bền vững. Mọi thứ quan trọng đều phải nằm trong volume hoặc bind mount đã được backup — và bản backup đó phải được kiểm tra khôi phục định kỳ.
Backup docker volume: hiểu 3 nơi dữ liệu Docker có thể nằm
Trước khi backup, cần biết dữ liệu của bạn đang nằm ở đâu. Docker có ba cơ chế lưu trữ, mỗi loại cần cách backup khác nhau.
Named volume là cách chuẩn nhất để lưu dữ liệu bền vững. Volume do Docker quản lý, nằm trong thư mục của Docker trên host. Khi bạn khai báo postgres_data:/var/lib/postgresql/data, dữ liệu database nằm trong named volume — an toàn khi container bị xóa, nhưng sẽ mất nếu volume bị xóa hoặc ổ đĩa hỏng.
Bind mount gắn trực tiếp một thư mục trên host vào container (ví dụ ./uploads:/app/uploads). Dữ liệu nằm ngay trên filesystem của host nên dễ backup bằng rsync — nhưng cũng dễ bị xóa nhầm vì "trông như" thư mục bình thường.
Container writable layer là lớp ghi tạm của chính container — mọi file tạo ra trong container mà không qua volume hay bind mount đều nằm đây. Lớp này bị xóa hoàn toàn khi container bị xóa hoặc rebuild từ image mới. Đây là cái bẫy lớn nhất.
Cách kiểm tra nhanh: chạy docker inspect <container> để liệt kê mọi mount. Bất kỳ dữ liệu quan trọng nào không xuất hiện trong danh sách mount đều đang nằm trong writable layer — hãy di chuyển nó ra volume ngay.
Backup docker volume: backup named volume: cách chuẩn theo tài liệu Docker
Cách được tài liệu chính thức của Docker khuyến nghị là dùng một container tạm thời để nén volume thành file tar. Phương pháp này đơn giản, không cần cài thêm công cụ, và hoạt động với mọi named volume.
Câu lệnh backup một volume tên app_db_data:
docker run --rm \
-v app_db_data:/data:ro \
-v $(pwd)/backup:/backup \
alpine tar czf /backup/app_db_data.tar.gz -C /data .
Giải thích: container alpine siêu nhẹ mount volume cần backup vào /data ở chế độ chỉ đọc, mount thư mục backup của host vào /backup, rồi nén toàn bộ thành file tar.gz. Tham số --rm tự xóa container sau khi xong.
Để restore, tạo volume mới (hoặc dùng volume cũ đã làm trống) rồi giải nén ngược lại:
docker volume create app_db_data_restored
docker run --rm \
-v app_db_data_restored:/data \
-v $(pwd)/backup:/backup \
alpine sh -c "tar xzf /backup/app_db_data.tar.gz -C /data"
Lưu ý quan trọng: dừng container đang dùng volume trước khi backup nếu đó là database đang ghi dữ liệu — nén volume trong lúc database đang ghi có thể tạo ra bản backup không nhất quán.
Backup bind mount: rsync và snapshot filesystem
Bind mount dễ backup hơn named volume vì dữ liệu đã nằm sẵn trên filesystem của host. Công cụ đơn giản và hiệu quả nhất là rsync:
rsync -a --delete /srv/app/uploads/ /backup/uploads/
Tùy chọn -a giữ nguyên quyền file và cấu trúc thư mục, --delete đồng bộ cả các file đã bị xóa ở nguồn. Kết hợp với --link-dest để tạo các bản backup incremental tiết kiệm dung lượng:
rsync -a --delete --link-dest=/backup/uploads-latest \
/srv/app/uploads/ /backup/uploads-$(date +%Y%m%d)/
ln -sfn /backup/uploads-$(date +%Y%m%d) /backup/uploads-latest
Cách này tạo mỗi ngày một thư mục backup đầy đủ, nhưng các file không thay đổi chỉ chiếm dung lượng một lần nhờ hard link — vừa có nhiều bản để quay lui, vừa tiết kiệm ổ đĩa.
Nếu host dùng filesystem hỗ trợ snapshot như ZFS hoặc Btrfs, bạn có thêm một lớp bảo vệ: snapshot toàn bộ thư mục dữ liệu trong vài giây mà không cần dừng container — cứu cánh tuyệt vời khi ai đó xóa nhầm file.
Database trong container: dump trước, tar sau
Đây là phần quan trọng nhất và cũng là nơi nhiều người sai nhất. Đừng bao giờ chỉ tar volume của database đang chạy — file dữ liệu đang được ghi dở có thể khiến bản backup không restore được.
Quy trình đúng cho từng loại database phổ biến:
PostgreSQL — dùng pg_dump từ bên trong hoặc bên ngoài container:
docker exec db_container pg_dump -U postgres ten_db \
| gzip > /backup/db-$(date +%Y%m%d).sql.gz
MySQL/MariaDB — dùng mysqldump tương tự:
docker exec db_container mysqldump -u root -p"$MYSQL_PW" \
--single-transaction ten_db | gzip > /backup/db-$(date +%Y%m%d).sql.gz
Tùy chọn --single-transaction của mysqldump tạo bản dump nhất quán cho bảng InnoDB mà không khóa bảng — website vẫn hoạt động bình thường trong lúc dump.
Sau khi có file dump, bạn có hai lớp bảo vệ: file dump SQL (dễ restore từng bảng) và tar của volume khi container đã dừng. Lưu file dump ra ngoài host hoặc đẩy lên object storage ngay.
Cuối cùng, hãy test restore file dump vào một database trống ít nhất mỗi quý. Một file dump bị lỗi giữa chừng mà không ai kiểm tra thì không khác gì không có backup.

Tự động hóa backup bằng cron và script mẫu
Backup thủ công là backup sẽ bị quên. Hãy tự động hóa toàn bộ bằng một script duy nhất chạy qua cron mỗi đêm.
Script mẫu backup-docker.sh:
#!/bin/bash
BACKUP_DIR=/backup/$(date +%Y%m%d)
mkdir -p "$BACKUP_DIR"
# 1. Dump database
docker exec db_container pg_dump -U postgres ten_db \
| gzip > "$BACKUP_DIR/db.sql.gz"
# 2. Backup named volumes
for vol in app_uploads app_config; do
docker run --rm -v $vol:/data:ro -v "$BACKUP_DIR":/backup \
alpine tar czf /backup/$vol.tar.gz -C /data .
done
# 3. Rsync bind mounts
rsync -a --delete /srv/app/uploads/ "$BACKUP_DIR/uploads/"
# 4. Sao chép file cấu hình quan trọng
cp /srv/app/docker-compose.yml /srv/app/.env "$BACKUP_DIR/"
# 5. Xóa bản backup quá 14 ngày
find /backup -maxdepth 1 -type d -mtime +14 -exec rm -rf {} \;
# 6. Đẩy lên object storage (ví dụ dùng rclone)
rclone sync /backup remote:s3-bucket/docker-backup/
Lên lịch chạy lúc 2 giờ sáng bằng cron:
0 2 * * * /usr/local/bin/backup-docker.sh >> /var/log/docker-backup.log 2>&>&1
Ba điểm cần nhớ: script phải ghi log để sáng hôm sau kiểm tra; phải có cơ chế xoay vòng (retention) để ổ đĩa không bị đầy; và bước đẩy lên object storage là bắt buộc.

Restore: diễn tập khôi phục trước khi cần thật
Có backup mà chưa từng restore thử thì chưa thể yên tâm. Quy trình diễn tập nên thực hiện mỗi quý trên một môi trường tách biệt (VPS test hoặc máy local).
Kịch bản diễn tập đầy đủ: dựng một host trống, cài Docker, tải bản backup mới nhất về, restore theo đúng tài liệu: giải nén volume, restore database từ file dump, đặt lại .env và docker-compose.yml, chạy docker compose up -d, rồi kiểm tra ứng dụng hoạt động bình thường.
Đo thời gian khôi phục thực tế và so với RTO đã cam kết. Nếu diễn tập mất 4 giờ trong khi cam kết là 2 giờ, bạn vừa phát hiện vấn đề khi chưa có sự cố thật — đó chính là giá trị của diễn tập.
Lưu trữ bản backup ở đâu: quy tắc 3-2-1 cho Docker
Quy tắc 3-2-1 kinh điển áp dụng hoàn hảo cho Docker: 3 bản sao dữ liệu, trên 2 phương tiện khác nhau, 1 bản ở vị trí địa lý khác.
Bản sao 1: trên chính host Docker (thư mục /backup) — để khôi phục nhanh khi xóa nhầm file hoặc volume. Bản sao 2: trên NAS hoặc server backup trong cùng datacenter/mạng nội bộ — để khôi phục nhanh toàn bộ khi host gặp sự cố phần cứng. Bản sao 3: trên object storage (S3-compatible) ở vùng khác — phòng trường hợp datacenter gặp thảm họa.
Mã hóa là bắt buộc với bản sao offsite, đặc biệt khi backup chứa database khách hàng. Quản lý khóa mã hóa tách biệt với bản backup — mất khóa mã hóa cũng tệ như mất backup.
Cuối cùng, đừng quên backup cả "bản đồ" của hệ thống: file docker-compose.yml, file .env (mã hóa trước khi lưu offsite), Dockerfile tùy chỉnh và tài liệu restore.
5 sai lầm phổ biến khiến backup Docker vô dụng
- Chạy
docker compose down -vmà không hiểu-v: tùy chọn-vxóa toàn bộ named volume của project. Vô số dữ liệu đã mất vì một lệnh dọn dẹp tưởng chừng vô hại. Hãy alias hoặc ghi chú cảnh báo cho lệnh này trên server production. - Chỉ backup code trên Git: Git không chứa database, file upload,
.envhay volume. Code restore được trong 5 phút nhưng dữ liệu khách hàng thì không — và dữ liệu mới là thứ không thể viết lại. - Tar volume database đang chạy: file dữ liệu đang ghi dở tạo ra bản backup hỏng một cách âm thầm — bạn chỉ phát hiện khi cần restore. Luôn dump bằng công cụ chuyên dụng trước.
- Để bản backup duy nhất trên cùng server: ổ đĩa hỏng, server bị hack hay datacenter gặp sự cố sẽ cuốn theo cả dữ liệu gốc lẫn backup. Đẩy bản sao ra object storage hoặc NAS khác ngay trong script backup.
- Không bao giờ test restore: backup chưa từng được kiểm tra khôi phục chỉ là niềm tin, không phải sự đảm bảo. Lên lịch diễn tập mỗi quý và ghi lại kết quả — đó là khoản bảo hiểm rẻ nhất bạn có thể mua.
Kết luận
Backup Docker volume và hệ sinh thái xung quanh nó không phức tạp, nhưng đòi hỏi kỷ luật: xác định mọi nơi dữ liệu đang nằm, dump database bằng công cụ chuyên dụng thay vì tar bừa, tự động hóa bằng cron, đẩy bản sao ra offsite theo quy tắc 3-2-1, và diễn tập restore mỗi quý. Năm việc này tạo nên sự khác biệt giữa "tưởng là có backup" và "chắc chắn khôi phục được".
Đừng để đến khi chạy nhầm một lệnh xóa volume mới bắt đầu nghĩ về backup. Hãy dành một buổi chiều hôm nay để dựng script backup cho hệ thống Docker của bạn — buổi chiều rẻ nhất trong sự nghiệp vận hành của bạn.
Cần hạ tầng ổn định để chạy Docker production kèm giải pháp backup tự động? VPS và Cloud Server của SunCloud dùng SSD Enterprise, backup tự động hàng tuần, hỗ trợ kỹ thuật 24/7. Liên hệ hotline 083 979 3434 hoặc truy cập suncloud.vn để được tư vấn.
Câu hỏi thường gặp
docker commit có phải là cách backup container không?
Không nên dùng docker commit để backup dữ liệu. Lệnh này chỉ lưu lại filesystem của container thành image mới — không bao gồm dữ liệu trong volume, dễ tạo ra image phình to, và không phù hợp để khôi phục database. Hãy backup volume và dump database theo đúng phương pháp trong bài; docker commit chỉ hữu ích trong một số tình huống debug tạm thời.
Nên backup volume khi container đang chạy hay phải dừng?
Với dữ liệu tĩnh (file upload, cấu hình), backup khi đang chạy thường an toàn. Với database đang ghi dữ liệu, nên dump bằng công cụ chuyên dụng (pg_dump, mysqldump) khi đang chạy, hoặc dừng container rồi mới tar volume. Quy tắc đơn giản: dữ liệu đang thay đổi liên tục thì dump trước, tar sau.
Lưu file backup Docker ở đâu là tốt nhất?
Theo quy tắc 3-2-1: một bản trên host để khôi phục nhanh, một bản trên NAS/server backup trong cùng hệ thống, và một bản trên object storage ở vị trí khác. Đừng bao giờ để bản backup duy nhất nằm trên cùng server với dữ liệu gốc — đó là sai lầm phổ biến và nguy hiểm nhất.
Bao lâu nên backup Docker một lần?
Database nên backup ít nhất mỗi ngày một lần (dump), với hệ thống quan trọng có thể tăng lên vài giờ một lần kết hợp binary log. Volume chứa file upload có thể backup incremental hàng ngày. Tần suất backup quyết định RPO — lượng dữ liệu tối đa bạn chấp nhận mất — nên hãy chốt con số này với chủ sở hữu dữ liệu trước.
File .env có cần backup không, có an toàn không?
Cần backup, vì mất file .env đồng nghĩa mất toàn bộ key, mật khẩu và cấu hình bí mật để dựng lại hệ thống. Nhưng phải mã hóa trước khi lưu offsite (dùng gpg hoặc vault), không bao giờ commit .env lên Git công khai, và quản lý khóa mã hóa tách biệt với bản backup.
Khôi phục hệ thống Docker từ backup mất bao lâu?
Tùy quy mô: hệ thống nhỏ vài GB có thể restore trong 15–30 phút, hệ thống lớn với database hàng chục GB có thể mất vài giờ (chủ yếu là thời gian tải backup về và import database). Cách duy nhất để biết con số chính xác là diễn tập restore định kỳ và đo thời gian thực tế — đừng đoán.
