SSD Nodes Learn 🎉 VPS từ $4.99/tháng
Hướng dẫn Matt ConnorBởi Matt Connor · Cập nhật ngày 2026-08-07

Cheat sheet Docker Compose cho server thực tế

Tổng hợp lệnh Docker Compose V2 dùng hằng ngày trên server: khởi động, cập nhật, xem log, mở shell, quản lý network, volume và dọn dẹp an toàn.

Các lệnh Compose bạn thực sự thường dùng

Docker Compose có hơn 40 subcommand. Công việc hằng ngày trên server chỉ dùng khoảng một tá lệnh. Trang này nhóm các lệnh theo công việc bạn đang thực hiện, nêu một lý do rõ ràng cho từng lệnh và dẫn đến phần chuyên sâu khi lệnh đó có bẫy cần lưu ý.

Mọi nội dung ở đây đều dùng Compose V2: docker compose có dấu cách, không phải script docker-compose cũ. V2 là một plugin Go được cài cùng Docker Engine. V1 đã bị loại khỏi các package hiện tại, vì vậy thấy docker-compose: command not found trên một máy Ubuntu mới cài tính đến July 2026 là điều bình thường, không phải lỗi. Kiểm tra bằng docker compose version. Nếu lệnh đó không in gì, hãy cài package docker-compose-plugin.

Mọi lệnh bên dưới phải được chạy từ thư mục chứa compose.yaml, vì Compose lấy project name từ thư mục đó và tìm file theo đường dẫn tương đối với thư mục này. Chạy cùng lệnh ở thư mục cha một cấp sẽ khiến Compose dừng với no configuration file provided: not found. Nếu định dạng file còn mới với bạn, hãy bắt đầu bằng file Compose đầu tiên trên VPS, sau đó quay lại đây để xem các lệnh.

Vòng đời: bốn lệnh bạn dùng và lệnh còn lại xóa container

docker compose up -d
docker compose up -d --wait
docker compose stop
docker compose start
docker compose restart web
docker compose down

up -d tạo network, tạo container, khởi động chúng rồi trả về. Lệnh này trả về ngay khi các container vừa được tạo, nên script deploy chạy tiếp bằng probe curl thường thất bại ở lần đầu. up -d --wait chặn cho đến khi mọi service có khai báo healthcheck đều báo healthy, và thoát với mã khác 0 nếu một service không bao giờ đạt trạng thái đó. Flag này chỉ đáng tin khi healthcheck phía sau nó đáng tin, vì vậy hãy viết healthcheck mà Compose có thể tin cậy trước khi dùng nó trong automation.

stop dừng các container nhưng giữ lại chúng, nên start khởi động lại đúng các container đó với cùng writable layer. down dừng rồi xóa các container và network của project. Mọi dữ liệu được ghi bên trong container nhưng nằm ngoài volume đều bị xóa theo. Đây là hiểu nhầm gây thiệt hại lớn nhất khi dùng Compose, và toàn bộ khác biệt giữa down và stop giải thích trường hợp dữ liệu bị ảnh hưởng.

restart không phải là reload. Lệnh này dừng rồi khởi động lại cùng container với cấu hình mà container hiện có, nên biến môi trường đã thay đổi, image tag mới hoặc port mapping đã chỉnh sửa hoàn toàn không có hiệu lực. Để áp dụng thay đổi trong file, bạn chạy lại up -d. Compose so sánh từng service với container đang chạy và chỉ tạo lại những service có cấu hình thay đổi.

Áp dụng thay đổi: tạo lại, pull hoặc build lại

docker compose up -d --force-recreate
docker compose pull && docker compose up -d
docker compose build --no-cache web
docker compose up -d --build web

up -d tự nó không làm gì nếu không có thay đổi. Vì vậy, bạn có thể chạy lặp lại lệnh này một cách an toàn. --force-recreate bỏ qua bước so sánh và thay thế mọi container ngay cả khi cấu hình giống hệt nhau. Đây là cách nhanh nhất để xóa trạng thái bất thường bên trong container.

Cập nhật image cần 2 lệnh vì mỗi lệnh thực hiện một việc khác nhau. pull tải image hiện tại cho từng tag được khai báo trong file. Sau đó, up -d nhận thấy ID image của service không còn khớp với container đang chạy và tạo lại container đó. Nếu bỏ qua bước pull, up -d vẫn tiếp tục chạy latest của tháng trước mà không báo lỗi.

build áp dụng cho các service khai báo phần build: thay vì image:. up -d --build build và start trong một bước. Đây là vòng lặp thông thường khi bạn đang thay đổi code. Chỉ dùng --no-cache khi chắc chắn một layer trong cache đã cũ, vì lệnh này build lại mọi layer từ đầu.

Xem các thành phần đang chạy

docker compose ps
docker compose ps -a
docker compose logs -f --tail=100
docker compose logs --since 15m --timestamps db
docker compose top
docker compose ls

ps chỉ liệt kê các container đang chạy. Một service bị crash trong lúc khởi động sẽ không xuất hiện ở đó cho đến khi bạn thêm -a. Vì vậy, container không có trong ps nhưng ps -a hiển thị nó ở trạng thái Exited (1) là dạng lỗi khởi động thông thường. Đọc exit code, sau đó đọc log.

logs -f theo dõi tất cả service cùng lúc và thêm tên service vào đầu mỗi dòng. Đây là chế độ xem phù hợp khi các service trao đổi với nhau và thứ tự sự kiện quan trọng. Ghi tên một service để thu hẹp kết quả. --tail=100 rất quan trọng với container đã chạy được một tháng, vì mặc định sẽ in toàn bộ lịch sử và làm ngập terminal. --since 15m trả lời câu hỏi bạn thường cần biết: chuyện gì đã xảy ra trong lần restart vừa thực hiện.

top liệt kê các process bên trong từng container. Nhờ đó, bạn phân biệt được “container đang chạy” với “process bên trong container đang chạy”. ls thoát khỏi thư mục hiện tại và liệt kê mọi project Compose trên host cùng trạng thái của chúng. Nhờ đó, bạn có thể tìm stack đã khởi động từ ba tháng trước.

Mở shell bên trong một service

docker compose exec web sh
docker compose exec -u root web sh
docker compose run --rm web env
docker compose run --rm --no-deps web sh

exec chạy một command bên trong container đã chạy. run khởi động một container mới từ cùng service definition. Bạn cần cách này khi service không chạy đủ lâu để dùng exec vào container. Luôn dùng run cùng với --rm. Nếu không, mỗi lần gọi sẽ để lại một container đã dừng. Các container này sẽ tích tụ cho đến khi docker compose ps -a không còn đọc được.

Hãy thử sh trước bash. Image dựa trên Alpine không có bash, nên lỗi hiển thị là exec: "bash": executable file not found in $PATH. Thêm --no-deps vào run sẽ bỏ qua các dependency của service. Nhờ đó, việc kiểm tra nhanh cấu hình không khởi động toàn bộ database.

run --rm web env là cách nhanh nhất để xem chính xác environment mà service nhận được, sau khi mọi file .env, block environment: và biến shell đã được gộp. Khi một giá trị bị sai, nguyên nhân thường là thứ tự gộp. cách Compose phân giải env file và secret mô tả source nào được ưu tiên.

Mạng, cổng và phân giải tên

docker compose port web 80
docker compose exec web getent hosts db
docker compose config --networks

Compose đặt mọi service vào một network của project, và tên mỗi service là một tên DNS trên network đó. Chạy getent hosts db bên trong web sẽ in ra IP của container nếu phân giải tên thành công và không in gì nếu phân giải thất bại. Vì vậy, lệnh này trả lời câu hỏi “các container có nhìn thấy nhau không” trong 2 giây. Nếu tên được phân giải nhưng kết nối bị từ chối, tiến trình bên trong db đang bind vào 127.0.0.1 thay vì 0.0.0.0. Do đó, tiến trình không bao giờ nhận được packet từ container khác. Phần còn lại của mô hình này nằm trong cách network và DNS service của Compose hoạt động.

port web 80 in ra địa chỉ và cổng trên host mà cổng của container được publish vào. Cách này giúp bạn không phải đoán khi mapping được tạo từ một biến. Việc publish một cổng cũng tạo một firewall rule do Docker tự quản lý. Rule đó được áp dụng trước rule của bạn, nên một service mà bạn tưởng là private có thể bị mở ra Internet. Trường hợp này được trình bày trong vì sao các cổng Docker được publish bypass ufw.

Volume và dữ liệu

docker compose config --volumes
docker compose cp db:/etc/postgresql/pg_hba.conf ./pg_hba.conf
docker compose down -v

config --volumes in các named volume mà project khai báo, mỗi volume trên một dòng. Đây là danh sách bạn cần backup. cp sao chép file vào hoặc ra khỏi container mà không cần mở shell, bằng cú pháp service:path ở phía nào là container thì đặt container ở phía đó.

down -v xóa các named volume đó cùng với các container. Đây là lệnh phù hợp để dỡ một test stack và không phù hợp với bất kỳ thứ gì chứa dữ liệu bạn cần giữ, vì lệnh không yêu cầu xác nhận và không thể undo. Bind mount không bị ảnh hưởng, vì chúng nằm trên filesystem của host. Khác biệt về phạm vi ảnh hưởng này là một lý do để cân nhắc rõ ràng giữa bind mount và named volume.

Dọn dẹp để giải phóng đĩa mà không mất dữ liệu

docker compose down --remove-orphans
docker system df
docker image prune -a
docker builder prune

--remove-orphans xóa các container thuộc project nhưng không còn xuất hiện trong file. Đây chính là trường hợp xảy ra sau khi đổi tên service. Nếu không dùng lệnh này, các container đó vẫn tiếp tục chạy và không hiển thị trong docker compose ps.

docker system df cho biết dung lượng đĩa đang được sử dụng ở đâu trước khi bạn xóa bất kỳ thứ gì. Lệnh này tách image, container, local volume và build cache, đồng thời hiển thị dung lượng có thể reclaim cho từng loại. image prune -a xóa mọi image không còn tag nào trỏ đến. Trên máy chủ đã pull nhiều phiên bản của một image lớn, đây thường là khoản giải phóng lớn nhất. builder prune xóa build cache. Cache này âm thầm tăng trên mọi server tự build image.

Không lệnh nào trong số đó tác động đến named volume. Chỉ docker volume prunedocker compose down -v mới làm việc đó.

Kiểm tra file trước khi file gây lỗi

docker compose config --quiet
docker compose config --services
docker compose --dry-run up -d

config --quiet kiểm tra và không in gì nếu thành công, nên phù hợp để chạy trong bước pre-deploy hoặc git hook. config thông thường in ra file đã được merge và nội suy đầy đủ. Đây là cách xác nhận một biến đã được phân giải và file override được áp dụng theo đúng cách bạn mong đợi. Biến chưa được thiết lập sẽ xuất hiện dưới dạng giá trị rỗng, cùng với cảnh báo The "X" variable is not set. Defaulting to a blank string.

--dry-run là global flag chứ không phải subcommand flag, nên phải đặt trước up. Lệnh này in ra mọi thao tác Compose sẽ thực hiện nhưng không thay đổi gì. Chạy lệnh này trước down trên một stack quan trọng là 30 giây hoàn toàn đáng bỏ ra.

Làm việc với nhiều file, profile và project

docker compose -f compose.yaml -f compose.prod.yaml up -d
docker compose --profile debug up -d
docker compose -p staging up -d

Nhiều flag -f được gộp theo thứ tự, và các file đứng sau sẽ ghi đè từng key của các file đứng trước. Đây là cách chuẩn để giữ một file cơ sở cùng một file override nhỏ cho production. Tuy nhiên, quy tắc xử lý list và map khác nhau, vì vậy hãy đọc cách Compose gộp nhiều file trước khi debug một kết quả bất ngờ.

--profile khởi động các service có gắn profile đó cùng với các service không gắn profile, nhờ vậy các công cụ debug không xuất hiện trong up thông thường. -p đặt tên project, nên bạn có thể chạy song song hai bản sao của cùng một stack với network riêng và tên volume riêng. Để khôi phục stack sau khi reboot, bạn không cần tự gõ lệnh. Thay vào đó, một unit sẽ tự chạy lệnh này, như mô tả trong khởi động stack Compose khi boot.

FAQ

Điều gì thay thế docker-compose bằng dấu gạch nối?

Compose V2, được gọi bằng docker compose với một khoảng trắng. Đây là plugin đi kèm Docker Engine, còn công cụ Python V1 không còn được cài trong các package hiện tại. Nếu dạng có khoảng trắng không in ra gì, hãy cài package docker-compose-plugin cho bản phân phối của bạn. Hãy cập nhật script cũ sang dạng có khoảng trắng thay vì thêm alias, vì V2 có các flag mà V1 không có.

Tại sao docker compose restart không nhận thay đổi cấu hình của tôi?

restart dừng rồi khởi động lại container hiện có với cấu hình mà container đó được tạo cùng, và không bao giờ đọc lại compose.yaml. Mọi thay đổi về biến môi trường, port, volume hoặc image tag đều cần docker compose up -d. Lệnh này so sánh từng service với container đang chạy và tạo lại những service có khác biệt. Thêm --force-recreate nếu bạn muốn tạo lại container ngay cả khi file không thay đổi.

Làm cách nào để cập nhật service lên image mới hơn?

Chạy docker compose pull, sau đó chạy docker compose up -d. Lệnh pull tải image hiện tại cho từng tag trong file, còn up -d tạo lại mọi service có image ID không còn khớp với container của service đó. Tự chạy up -d sẽ dùng lại image đã có trên disk. Vì vậy, một stack được ghim vào latest có thể tiếp tục chạy trên bản build đã vài tháng tuổi mà không in ra lỗi nào.

Những lệnh cleanup nào an toàn trên server đang hoạt động?

docker system df, docker image prune -adocker builder prune chỉ xóa image và cache, nên các service đang chạy vẫn hoạt động và named volume không bị ảnh hưởng. Cặp lệnh nguy hiểm là docker compose down -vdocker volume prune, vì chúng xóa named volume mà không hỏi xác nhận. Hãy chạy docker compose config --volumes trước để biết những gì có thể bị xóa.

Tôi có thể chạy một lệnh mà không khởi động toàn bộ stack không?

Có. docker compose run --rm --no-deps web sh khởi động một container duy nhất từ định nghĩa service web, bỏ qua các dependency và xóa container khi bạn thoát. Dùng exec khi container đã chạy, vì exec kết nối vào tiến trình đang hoạt động và cho bạn thấy trạng thái thực tế của service.