
Trả lời nhanh: Docker Compose là công cụ khai cả cụm dịch vụ trong một file YAML, app, database, cache, automation, rồi vận hành bằng lệnh ngắn: docker compose up -d dựng tất cả đúng thứ tự, docker compose down hạ gọn gàng. Giá trị lớn nhất: hạ tầng thành văn bản, commit vào git, chuyển máy mới chỉ việc chép file và up lại, và AI agent đọc hiểu để tự vận hành giúp bạn.
Chạy lẻ từng docker run với chuỗi flag dài dằng dặc chỉ vui được hai container đầu. Đến container thứ ba, khi app cần database, database cần đợi sẵn sàng, cả đám cần chung mạng, bạn cần Compose. Bài này đi từ khái niệm đến một file mẫu chạy trọn vibe stack: web app + Postgres + Redis + n8n trên một VPS.
- Compose = hạ tầng thành văn bản: services, volumes, networks trong một YAML
- up -d / down / logs -f / ps, bốn lệnh vận hành cả cụm
- Volume có tên cho dữ liệu bền; mạng nội bộ tự có, service gọi nhau bằng tên
- File mẫu vibe stack cuối bài: app + Postgres + Redis + n8n, dán là chạy
Từ docker run đến compose: cùng nội dung, khác đẳng cấp quản lý
Một lệnh docker run dài chính là một service trong compose, viết lại thành cấu trúc:
services:
db:
image: postgres:16
restart: unless-stopped
environment:
POSTGRES_PASSWORD: doi-mat-khau-nay
volumes:
- pgdata:/var/lib/postgresql/data
ports:
- "127.0.0.1:5432:5432"
volumes:
pgdata:docker compose up -d: chạy. Điểm ăn tiền không phải cú pháp mà là chỗ để: cấu hình giờ nằm trong file cạnh dự án, ai (kể cả agent AI, kể cả bạn-của-6-tháng-sau) nhìn vào cũng biết hệ thống gồm gì.
Bốn khái niệm gánh cả framework
- services: mỗi dịch vụ một khối, image dùng gì, env nào, mở port đâu, restart: unless-stopped để sống qua reboot.
- volumes có tên: dữ liệu bền qua đời container, mọi thứ là database/cấu hình phải nằm trong volume, không là mất khi recreate.
- mạng nội bộ: compose tự tạo network riêng, service gọi nhau bằng tên: app kết nối chuỗi
postgres://db:5432, khỏi IP khỏi port ngoài. - depends_on: khai thứ tự dựng; kèm healthcheck khi app đòi database thật sự sẵn sàng chứ không chỉ đã bật.
File mẫu: vibe stack hoàn chỉnh trên một VPS
services:
app:
build: ./app # Dockerfile cua du an (bai Docker build cham co mau)
restart: unless-stopped
environment:
DATABASE_URL: postgres://postgres:doi-mk@db:5432/shop
REDIS_URL: redis://cache:6379
ports:
- "127.0.0.1:3000:3000"
depends_on:
db: { condition: service_healthy }
db:
image: postgres:16
restart: unless-stopped
environment: { POSTGRES_PASSWORD: doi-mk, POSTGRES_DB: shop }
volumes: [ pgdata:/var/lib/postgresql/data ]
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
retries: 10
cache:
image: redis:7-alpine
restart: unless-stopped
volumes: [ redisdata:/data ]
n8n:
image: docker.n8n.io/n8nio/n8n
restart: unless-stopped
environment:
- WEBHOOK_URL=https://n8n.ten-mien.vn/
- GENERIC_TIMEZONE=Asia/Ho_Chi_Minh
volumes: [ n8ndata:/home/node/.n8n ]
ports:
- "127.0.0.1:5678:5678"
volumes: { pgdata: {}, redisdata: {}, n8ndata: {} }Caddy đứng ngoài trỏ tên miền vào 3000 và 5678 (mẫu ở bài cài n8n). Một file này + một VPS = sản phẩm, database, cache, automation, cả startup trong 40 dòng YAML.
Vận hành hằng ngày và bẫy cần né
docker compose logs -f app: dò lỗi một service;ps: trạng thái cả cụm;pull && up -d: nâng cấp image (backup volume trước với database).- Bẫy đổi env không ăn: sửa environment phải
up -dlại (recreate), restart suông không đọc env mới. - Bẫy down -v: chữ -v xóa luôn volume, dữ liệu bay màu. Gõ down trần trụi là đủ hạ cụm.
- Secrets: mật khẩu để file .env cạnh compose (tự động đọc), file đó vào .gitignore, YAML sạch, git sạch.
Cả cụm này sống trên một ổ đĩa: Postgres fsync, Redis snapshot, n8n ghi execution, app ghi log, cộng dồn là dòng I/O liên tục. Đây chính là kịch bản bài chia tài nguyên nhiều service phân tích sâu, và là nơi ổ NVMe enterprise trả lương chăm chỉ nhất.
Mười lệnh vận hành hằng ngày
docker compose up -d # dung ca cum, chay nen docker compose ps # xem trang thai tung service docker compose logs -f app # xem log mot service, theo doi truc tiep docker compose logs --tail=100 # xem 100 dong cuoi cua tat ca docker compose restart app # khoi dong lai mot service docker compose exec db psql -U postgres # vao trong container dang chay docker compose pull && docker compose up -d # cap nhat image roi dung lai docker compose down # ha cum, GIU volume docker compose down -v # ha cum, XOA CA VOLUME, can than docker compose config # kiem cu phap va xem file sau khi gop bien
Lệnh cuối đáng dùng trước mỗi lần sửa tệp: nó in ra cấu hình cuối cùng sau khi thay biến môi trường, nên bạn thấy đúng thứ sẽ chạy chứ không phải thứ mình nghĩ.
Ba thứ nên có trong mọi tệp compose chạy thật
| Mục | Vì sao cần | Cách khai |
|---|---|---|
healthcheck | Để depends_on chờ dịch vụ sẵn sàng thật, không phải chỉ chờ container khởi động | test: CMD-SHELL pg_isready -U postgres, kèm interval và retries |
| Giới hạn log | Log mặc định không giới hạn, chạy vài tháng là đầy ổ | Khối logging với driver: json-file và tùy chọn max-size: 10m, max-file: 3 |
| Giới hạn tài nguyên | Một container rò bộ nhớ không kéo sập cả máy | Khai mem_limit: 512m cho từng service |
Về depends_on: khai suông chỉ bảo đảm thứ tự khởi động, không bảo đảm dịch vụ đã sẵn sàng nhận kết nối. Muốn chờ thật thì phải kèm condition: service_healthy và dịch vụ kia phải có healthcheck. Đây là nguyên nhân số một của lỗi ứng dụng không nối được cơ sở dữ liệu ở lần khởi động đầu.
Bảo mật: ba lỗi hay gặp trong tệp compose
- Mở cổng cơ sở dữ liệu ra ngoài. Viết
"5432:5432"là phơi ra mọi nơi. Viết"127.0.0.1:5432:5432"là chỉ mở cho chính máy đó. Các dịch vụ trong cùng cụm gọi nhau bằng tên, không cần mở cổng ra ngoài chút nào. - Mật khẩu viết thẳng trong tệp rồi đẩy lên kho mã nguồn. Đưa vào tệp
.envcạnh tệp compose, và thêm.envvào.gitignore. Compose tự đọc tệp đó. - Không cố định phiên bản image. Dùng
postgres:16chứ đừng dùngpostgres:latest. Một sáng đẹp trời image mới đổi phiên bản lớn là dữ liệu không mở được, và bạn sẽ mất cả ngày để hiểu chuyện gì xảy ra.
Sao lưu dữ liệu trong volume
# Sao luu co so du lieu, cach dung nhat docker compose exec -T db pg_dump -U postgres tendb | gzip > backup-$(date +%F).sql.gz # Sao luu ca volume theo kieu tho docker run --rm -v ten_volume:/data -v $(pwd):/backup alpine \ tar czf /backup/volume-$(date +%F).tar.gz -C /data .
Cách đầu dùng cho cơ sở dữ liệu vì nó lấy bản nhất quán. Cách hai dùng cho volume chứa tệp tĩnh. Đặt cả hai vào một tệp kịch bản chạy theo lịch, và nhớ chép bản sao sang máy khác, giữ trên cùng máy chủ là không có tác dụng khi máy chủ hỏng.
Câu hỏi thường gặp
docker compose và docker-compose khác gì nhau?
docker compose (hai từ) là plugin hiện hành đi cùng Docker; docker-compose gạch ngang là bản cũ độc lập. File YAML gần như tương thích, cứ dùng bản hai từ, tài liệu cũ đọc vẫn hiểu được.
Compose có dùng cho production được không?
Với quy mô một-vài VPS: hoàn toàn, restart policy + healthcheck + backup volume là đủ nghiêm túc. Kubernetes chỉ cần đến khi bạn có nhiều máy và đội vận hành riêng; đừng vác dao mổ trâu giết gà.
Sửa file compose xong có phải down rồi up không?
Không cần down: docker compose up -d tự so khác biệt và chỉ recreate service thay đổi, các service khác chạy tiếp không gián đoạn.
Nhiều dự án compose trên một VPS có đụng nhau không?
Không, mỗi thư mục dự án là một 'project' tách biệt: mạng riêng, tên container riêng theo prefix thư mục. Chỉ cần tránh đụng port công khai (mỗi dự án một port 127.0.0.1 khác nhau, Caddy phân phối tên miền).
Bài viết liên quan
- Docker Compose nhiều service: chia tài nguyên
- Cài Docker trên VPS Ubuntu 24.04 chuẩn từng lệnh
- VPS ổ Enterprise U.2 NVMe RAID 10 tại TND
- Cài Proxmox build cloud nhỏ tại nhà: tự host vs thuê VPS, khi nào đáng?
- Cursor và Windsurf dùng VPS làm môi trường remote
- Ổ NVMe và M.2: vì sao không phải cứ M.2 là nhanh
- Cấu hình VPS tối thiểu để Claude Code chạy mượt cả ngày
- Tailscale Funnel lỗi 502, TLS handshake, cert: cách sửa
- Zalo OA API: webhook, token và gửi tin CSKH



