Trên máy tính cá nhân (localhost), việc chạy Docker Compose thường rất đơn giản: viết vài dòng service, gõ lệnh docker compose up -d là website đã chạy mượt mà.
Thế nhưng, nếu mang nguyên xi file cấu hình đó đặt lên một chiếc máy chủ VPS production, bạn sẽ sớm gặp rắc rối:
- Vẫn giữ cấu hình gắn thẳng thư mục mã nguồn (bind mount) kèm chế độ debug hoặc hot reload của môi trường phát triển.
- Mở toang các cổng database (như 3306, 5432 hay 6379) ra toàn bộ Internet mà không có tường lửa che chắn.
- Nhét tất cả mật khẩu và API key vào một file
.envlộ thiên trên máy chủ. - Không khống chế log, khiến vài tuần sau ổ cứng VPS bị đầy 100% và kéo sập toàn bộ dịch vụ.
- Container web khởi động trước khi database sẵn sàng, hoặc bản cập nhật bị lỗi mà không có cách nào quay về phiên bản cũ.
Docker Compose có thể phù hợp để vận hành ứng dụng trên một máy chủ khi đội ngũ chấp nhận failure domain của chính VPS đó. Restart policy giúp tiến trình chạy lại sau lỗi nhưng không tạo high availability; nếu VPS hoặc vùng lưu trữ gặp sự cố, toàn bộ stack vẫn có thể dừng. Điều quan trọng cần nhớ là Docker đóng gói ứng dụng chứ không thay thế việc quản trị máy chủ, như phân tích tại bài Docker không thay thế quản trị server: ranh giới giữa container và vận hành.
Image, cấu hình, secret, volume và healthcheck đều phải đi qua kiểm tra trước khi deploy.
5 nhóm cần rà soát trước khi deploy
1. Tách biệt cấu hình Production và Development
Một cách dễ kiểm soát là giữ compose.yaml làm cấu hình chung và dùng compose.production.yaml để ghi đè các thiết lập dành riêng cho production. Bạn cũng có thể tổ chức theo cách khác, miễn là cấu hình sau khi merge được lưu phiên bản, review và kiểm tra trước khi áp dụng:
# compose.production.yaml
services:
web:
image: my-app:1.2.0 # Luôn pin rõ số phiên bản, không dùng latest
restart: unless-stopped
# Bỏ hoàn toàn các dòng bind-mount code kiểu ./src:/appTrước khi triển khai, hãy chạy lệnh kiểm tra xem các file sau khi ghép lại có đúng chuẩn cú pháp hay không:
docker compose -f compose.yaml -f compose.production.yaml config --quietLệnh này kiểm tra và render model Compose sau khi merge cùng nội suy biến. Nếu cần đọc output đầy đủ, hãy nhớ rằng kết quả có thể chứa giá trị nhạy cảm; không đưa output đó vào log công khai. Biến chưa được đặt cũng cần được rà soát riêng thay vì chỉ dựa vào việc lệnh trả mã thành công.
2. Khóa chặt mạng nội bộ, chỉ mở cổng Reverse Proxy
Trong kiến trúc website phổ biến, reverse proxy như Nginx, Traefik hoặc Caddy là thành phần nhận traffic công khai trên 80 và 443. Nếu hệ thống còn public SSH, VPN hoặc một dịch vụ khác, từng cổng phải có lý do, giới hạn nguồn truy cập và firewall tương ứng.
Database, Redis và các service nội bộ thường chỉ cần giao tiếp qua Docker network. Nếu có nhu cầu quản trị từ host, có thể bind cổng vào loopback hoặc đi qua VPN thay vì mở trên mọi interface:
services:
db:
image: postgres:16
# KHÔNG dùng ports: ["5432:5432"]
networks:
- internal
web:
image: my-app:1.2.0
networks:
- internal
- public
networks:
internal:
public:Thiết kế này giảm bề mặt cổng công khai, nhưng không thay thế firewall và không bảo vệ database khỏi một container khác đã bị xâm nhập trong cùng network. Sau khi deploy, hãy kiểm tra cổng đang lắng nghe trên host và thử kết nối từ một máy bên ngoài.
3. Tách biệt Config thông thường và Secret bí mật
Không commit mật khẩu vào Git. Biến môi trường có thể xuất hiện trong cấu hình render, thông tin tiến trình hoặc công cụ chẩn đoán, nên chỉ cấp secret cho đúng service cần dùng.
- Các biến cấu hình thông thường (
PORT,LOG_LEVEL) có thể quản lý qua fileenv_file. - Các dữ liệu nhạy cảm có thể được cấp bằng Compose Secrets dưới dạng file trong
/run/secrets/nếu ứng dụng hỗ trợ đọc secret từ file. Cơ chế này giúp giới hạn secret theo service nhưng không phải một secret manager hoàn chỉnh: file nguồn, quyền trên host, rotation và backup vẫn cần được bảo vệ riêng.
Chi tiết về cách dùng BuildKit mount secret lúc build và mẫu file .dockerignore chuẩn có tại bài Biến môi trường và secret trong Docker: Quản lý thế nào để không bị lộ mật khẩu?.
4. Dữ liệu bền vững (Volume) và kịch bản Backup
Hãy nhớ rằng: Named volume chỉ giúp dữ liệu không bị mất khi container khởi động lại, nó không phải là bản sao lưu (backup).
Trước khi đưa website vào hoạt động:
- Xác định rõ database và thư mục upload đang được lưu ở named volume hay bind mount nào.
- Không bao giờ dùng lệnh
tarnén sống thư mục database đang chạy vì sẽ gây lỗi dữ liệu (corrupt). Luôn xuất dữ liệu bằng lệnh dump chuyên dụng (mysqldump,pg_dump). - Tự động đẩy các bản backup ra dịch vụ lưu trữ độc lập (như Cloudflare R2 hay AWS S3) sau mỗi đêm thay vì để dồn trên ổ cứng VPS.
Xem hướng dẫn đầy đủ tại bài Dữ liệu persistent của Docker container: Backup thế nào cho đúng và lưu ở đâu?.
5. Khống chế dung lượng Log và cấu hình Healthcheck
Log tăng không kiểm soát và healthcheck không phản ánh đúng khả năng phục vụ là hai rủi ro thường gặp trên một VPS chạy Docker.
- Khống chế log: Chọn logging driver và giới hạn dung lượng dựa trên lưu lượng log, dung lượng đĩa và khoảng thời gian cần giữ để điều tra.
max-sizecùngmax-filelà một cách xoay vòng, nhưng con số phải được đo trên hệ thống thật. Hướng dẫn chi tiết có tại bài Log Docker làm đầy ổ cứng: Cách khống chế và xoay vòng. - Healthcheck có ý nghĩa: Route như
/healthzcần kiểm tra đúng mức readiness mà ứng dụng có thể trả lời ổn định.start_periodgiúp tránh đánh lỗi trong lúc khởi động;depends_on: condition: service_healthycó thể điều phối thứ tự lúc tạo stack nhưng không tự restart service phụ thuộc nếu database hỏng sau đó. Xem chi tiết tại Healthcheck trong Docker Compose: Viết sao cho đúng và tránh báo ảo?.
Quy trình cập nhật phiên bản mới và kịch bản rút lui (Rollback)
Khi hệ thống đã vận hành ổn định, mỗi lần phát hành một phiên bản mới cần tuân thủ theo quy trình có đường lui:
- Pull image trước giờ bảo trì: Tải image mới về trước bằng
docker compose pull webđể không lãng phí thời gian bảo trì vào việc chờ tải mạng. - Kiểm tra tương thích database: Nếu bản cập nhật có chạy migration, xác định xem migration này có cho phép container cũ chạy tiếp nếu cần quay xe hay không. Đọc kỹ hướng dẫn tại Nâng cấp Docker image an toàn: pull, migrate và xác nhận ra sao?.
- Recreate đúng phạm vi: Sau khi pull hoặc build image, dùng
docker compose up -dvới đúng file production và service cần thay đổi. Chỉ thêm--no-depskhi đã xác nhận dependency không có thay đổi cấu hình, image hoặc migration liên quan. Tránhdocker compose downnếu không cần dừng toàn stack. - Chuẩn bị rollback trước khi deploy: Ghi lại image tag hoặc digest cũ, cấu hình đang chạy và trạng thái migration. Đổi lại image không tự hoàn tác schema hoặc dữ liệu đã ghi; nếu migration không tương thích ngược, cần một kế hoạch phục hồi hoặc roll-forward riêng. Tham khảo Rollback Docker Compose: Làm sao quay về bản cũ an toàn khi deploy gặp lỗi?.
Bảng Checklist thực chiến trước khi Go-Live
Hãy in hoặc lưu bảng kiểm này để đánh dấu trước mỗi đợt phát hành:
Chuẩn bị trước khi deploy:
- ☐ Image được gắn số phiên bản cụ thể (ví dụ
v1.2.0), tuyệt đối không dùng taglatest. - ☐ Đã gỡ bỏ toàn bộ bind-mount mã nguồn dev và tắt chế độ hot-reload / debug.
- ☐ File Compose được kiểm tra cú pháp hợp lệ bằng
docker compose config --quiet. - ☐ Không có mật khẩu hay secret thật nào bị commit lên Git repository.
- ☐ Đã có file
.dockerignorechặn thư mục.git,.envvàsecrets/. - ☐ Database và cache không mở cổng ra ngoài Internet.
- ☐ Đã chọn log rotation và retention phù hợp với dung lượng đĩa cùng nhu cầu điều tra.
- ☐ Healthcheck đã được thiết lập với
start_periodhợp lý. - ☐ Đã tạo một bản backup database an toàn trước khi bấm cập nhật.
Xác nhận sau khi container khởi động:
- ☐ Mở domain thật từ bên ngoài và kiểm tra các hành trình quan trọng bằng tài khoản test hoặc thao tác an toàn.
- ☐ Soi log 50 dòng gần nhất (
docker compose logs --tail=50 web) không thấy lỗi kết nối hay lỗi permission. - ☐ Tiến trình xử lý hàng đợi (Worker) và cron job đang nhận việc bình thường.
- ☐ CPU và RAM máy chủ ổn định, không có hiện tượng container khởi động lại liên tục.
- ☐ Image cũ và các volume dữ liệu quan trọng vẫn còn nguyên vẹn trên máy chủ làm phao cứu sinh.
- ☐ Đã xác nhận có thể truy cập backup và credential phục hồi nếu chính VPS này bị mất.
- ☐ Đã ghi nhận giới hạn single-host: restart policy không thay thế monitoring hoặc high availability.



