❤️ AZDIGI chính thức cập nhật hệ thống blog mới hoàn chỉnh. Tuy nhiên có thể một số bài viết bị sai lệch hình ảnh, hãy ấn nút Báo cáo bài viết ở cuối bài để AZDIGI cập nhật trong thời gian nhanh nhất. Chân thành cám ơn.

Xin chào, đây là Bài 17, bài cuối cùng trong serie “Docker từ Zero đến Hero trên VPS”.

Docker Compose thực tế - Project mẫu
Minh họa: Docker Compose trong thực tế – Project mẫu

Suốt 16 bài trước, bạn đã đi từ những khái niệm cơ bản nhất : Docker là gì, container là gì , đến việc build image, viết Dockerfile, quản lý volume, network, Docker Compose, reverse proxy, CI/CD, monitoring, logging… bạn đã có đầy đủ kiến thức nền tảng để triển khai bất kỳ ứng dụng nào trên VPS bằng Docker.

Bài cuối này, mình sẽ không dạy thêm lý thuyết nào mới. Thay vào đó, mình tổng hợp 5 project mẫu thực tế mà bạn có thể copy-paste docker-compose.yml → chạy ngay trên VPS. Mỗi project đều là ứng dụng self-hosted phổ biến, hữu ích, và minh họa rõ những gì bạn đã học trong serie.

Cuối bài, mình sẽ gộp tất cả lại thành một full VPS setup hoàn chỉnh với Nginx Proxy Manager, đúng kiểu production-ready.

Nhìn lại hành trình 16 bài

Docker Compose ps services
docker compose ps và top xem toàn bộ services

Trước khi bắt đầu, hãy cùng nhìn lại những gì bạn đã học:

  • Bài 1-3: Docker là gì, cài đặt Docker trên VPS, các lệnh cơ bản (docker run, docker ps, docker exec…).
  • Bài 4: Viết Dockerfile: build image riêng cho ứng dụng.
  • Bài 5: Volume & Network: dữ liệu persistent và container giao tiếp với nhau.
  • Bài 6-8: Docker Compose: từ cơ bản đến triển khai WordPress, LEMP stack hoàn chỉnh.
  • Bài 9: Environment variables & secrets: cách quản lý cấu hình an toàn.
  • Bài 10: Reverse proxy: một VPS, nhiều domain, SSL tự động.
  • Bài 11: Deploy ứng dụng Node.js & Python với Docker.
  • Bài 12: Backup & restore: bảo vệ dữ liệu Docker volume.
  • Bài 13: Monitoring: Portainer, Uptime Kuma, cAdvisor.
  • Bài 14: Centralized logging: thu thập và quản lý log container.
  • Bài 15-16: CI/CD, Docker trong quy trình phát triển, multi-stage build, security best practices.

Tất cả những kiến thức trên sẽ được áp dụng trong các project bên dưới. Nếu bạn gặp khái niệm nào chưa rõ, hãy quay lại bài tương ứng để ôn lại nhé.

Project 1: Gitea – Self-hosted Git server

Gitea là gì?

Gitea là một Git server tự host, nhẹ và nhanh, có thể xem như phiên bản lightweight của GitHub hay GitLab. Gitea viết bằng Go, chạy cực nhẹ (chỉ cần ~200MB RAM), nhưng đầy đủ tính năng: quản lý repository, pull request, issue tracker, CI/CD tích hợp (Gitea Actions), và giao diện web dễ dùng.

Nếu bạn muốn tự host code riêng tư mà không phụ thuộc GitHub, hoặc cần Git server nội bộ cho team nhỏ, Gitea là lựa chọn tuyệt vời.

Cấu trúc thư mục

mkdir -p ~/apps/gitea && cd ~/apps/gitea

docker-compose.yml

services:
  gitea:
    image: gitea/gitea:latest
    container_name: gitea
    restart: unless-stopped
    ports:
      - "3000:3000"
      - "2222:22"
    environment:
      - USER_UID=1000
      - USER_GID=1000
      - GITEA__database__DB_TYPE=postgres
      - GITEA__database__HOST=gitea-db:5432
      - GITEA__database__NAME=gitea
      - GITEA__database__USER=gitea
      - GITEA__database__PASSWD=gitea_secret_password
    volumes:
      - gitea_data:/data
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    depends_on:
      gitea-db:
        condition: service_healthy
    networks:
      - gitea-net
  gitea-db:
    image: postgres:16-alpine
    container_name: gitea-db
    restart: unless-stopped
    environment:
      - POSTGRES_USER=gitea
      - POSTGRES_PASSWORD=gitea_secret_password
      - POSTGRES_DB=gitea
    volumes:
      - gitea_db_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U gitea"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - gitea-net
volumes:
  gitea_data:
  gitea_db_data:
networks:
  gitea-net:

Chạy và setup ban đầu

docker compose up -d

Truy cập http://IP-VPS:3000 trên trình duyệt. Lần đầu tiên, Gitea sẽ hiển thị trang Initial Configuration. Hầu hết thông tin database đã được điền sẵn từ environment variables, bạn chỉ cần:

  • Đặt Site Title theo ý bạn.
  • Mở phần Administrator Account Settings ở cuối trang để tạo tài khoản admin.
  • Click Install Gitea.

Port 2222 được map cho SSH, để bạn có thể clone repo qua SSH: git clone ssh://git@IP-VPS:2222/user/repo.git.

Điểm nhấn Docker: Compose file này áp dụng depends_on với condition: service_healthy (Bài 6), đảm bảo PostgreSQL sẵn sàng trước khi Gitea khởi động. Volume gitea_data chứa toàn bộ repositories và cấu hình, gitea_db_data chứa database.

Project 2: Nextcloud – Cloud storage cá nhân

Nextcloud là gì?

Nextcloud là nền tảng cloud storage tự host, giống Google Drive hay Dropbox, nhưng dữ liệu hoàn toàn nằm trên VPS của bạn. Ngoài lưu trữ file, Nextcloud còn có calendar, contacts, notes, office suite, và hàng trăm app mở rộng.

Đây là một trong những ứng dụng self-hosted phổ biến nhất thế giới, và Docker là cách triển khai nhanh nhất.

Cấu trúc thư mục

mkdir -p ~/apps/nextcloud && cd ~/apps/nextcloud

docker-compose.yml

services:
  nextcloud:
    image: nextcloud:stable
    container_name: nextcloud
    restart: unless-stopped
    ports:
      - "8080:80"
    environment:
      - MYSQL_HOST=nextcloud-db
      - MYSQL_DATABASE=nextcloud
      - MYSQL_USER=nextcloud
      - MYSQL_PASSWORD=nextcloud_secret_password
      - REDIS_HOST=nextcloud-redis
      - NEXTCLOUD_TRUSTED_DOMAINS=your-domain.com
      - OVERWRITEPROTOCOL=https
    volumes:
      - nextcloud_html:/var/www/html
      - nextcloud_data:/var/www/html/data
    depends_on:
      nextcloud-db:
        condition: service_healthy
      nextcloud-redis:
        condition: service_started
    networks:
      - nextcloud-net
  nextcloud-db:
    image: mariadb:11
    container_name: nextcloud-db
    restart: unless-stopped
    command: --transaction-isolation=READ-COMMITTED --log-bin=binlog --binlog-format=ROW
    environment:
      - MYSQL_ROOT_PASSWORD=root_secret_password
      - MYSQL_DATABASE=nextcloud
      - MYSQL_USER=nextcloud
      - MYSQL_PASSWORD=nextcloud_secret_password
    volumes:
      - nextcloud_db_data:/var/lib/mysql
    healthcheck:
      test: ["CMD-SHELL", "healthcheck.sh --connect --innodb_initialized"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - nextcloud-net
  nextcloud-redis:
    image: redis:7-alpine
    container_name: nextcloud-redis
    restart: unless-stopped
    command: redis-server --requirepass redis_secret_password
    volumes:
      - nextcloud_redis_data:/data
    networks:
      - nextcloud-net
volumes:
  nextcloud_html:
  nextcloud_data:
  nextcloud_db_data:
  nextcloud_redis_data:
networks:
  nextcloud-net:

Chạy và cấu hình

docker compose up -d

Truy cập http://IP-VPS:8080. Lần đầu, Nextcloud sẽ yêu cầu tạo tài khoản admin. Vì đã cấu hình database qua environment variables, bạn chỉ cần đặt username/password cho admin rồi click Install.

Cấu hình Trusted Domains

Nếu bạn truy cập qua domain (ví dụ cloud.example.com) mà gặp lỗi “Access through untrusted domain”, cần thêm domain vào trusted list. Cách nhanh nhất:

docker exec -u www-data nextcloud php occ config:system:set \
  trusted_domains 1 --value="cloud.example.com"

Hoặc bạn đã set sẵn qua biến NEXTCLOUD_TRUSTED_DOMAINS trong compose file, chỉ cần thay your-domain.com bằng domain thực tế trước khi chạy.

Điểm nhấn Docker: Project này dùng 3 services (Nextcloud + MariaDB + Redis), đúng pattern multi-service mà bạn đã học ở Bài 6-8. Redis làm cache giúp Nextcloud chạy nhanh hơn đáng kể. Volume nextcloud_data được tách riêng để dễ backup (Bài 12).

Project 3: n8n – Workflow automation

n8n là gì?

n8n (đọc là “n-eight-n”) là nền tảng workflow automation tự host, tương tự Zapier hay Make.com, nhưng bạn kiểm soát hoàn toàn dữ liệu và không bị giới hạn số lượng workflow.

Với n8n, bạn có thể tự động hóa đủ thứ: nhận webhook → xử lý dữ liệu → gửi Telegram, đồng bộ data giữa các dịch vụ, scrape web, gọi API, chạy cron job… Tất cả qua giao diện kéo-thả trực quan.

Cấu trúc thư mục

mkdir -p ~/apps/n8n && cd ~/apps/n8n

docker-compose.yml

services:
  n8n:
    image: n8nio/n8n:latest
    container_name: n8n
    restart: unless-stopped
    ports:
      - "5678:5678"
    environment:
      - DB_TYPE=postgresdb
      - DB_POSTGRESDB_HOST=n8n-db
      - DB_POSTGRESDB_PORT=5432
      - DB_POSTGRESDB_DATABASE=n8n
      - DB_POSTGRESDB_USER=n8n
      - DB_POSTGRESDB_PASSWORD=n8n_secret_password
      - N8N_HOST=n8n.example.com
      - N8N_PROTOCOL=https
      - WEBHOOK_URL=https://n8n.example.com/
      - GENERIC_TIMEZONE=Asia/Ho_Chi_Minh
    volumes:
      - n8n_data:/home/node/.n8n
    depends_on:
      n8n-db:
        condition: service_healthy
    networks:
      - n8n-net
  n8n-db:
    image: postgres:16-alpine
    container_name: n8n-db
    restart: unless-stopped
    environment:
      - POSTGRES_USER=n8n
      - POSTGRES_PASSWORD=n8n_secret_password
      - POSTGRES_DB=n8n
    volumes:
      - n8n_db_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U n8n"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - n8n-net
volumes:
  n8n_data:
  n8n_db_data:
networks:
  n8n-net:

Chạy và cấu hình

docker compose up -d

Truy cập http://IP-VPS:5678, tạo tài khoản admin, và bắt đầu tạo workflow đầu tiên.

Lưu ý về Webhook URL

Biến WEBHOOK_URL rất quan trọng, nó quyết định URL mà các dịch vụ bên ngoài sẽ gọi vào n8n. Nếu bạn dùng reverse proxy (Bài 10), hãy set đúng domain. Nếu chỉ test local, có thể bỏ biến này hoặc dùng http://IP-VPS:5678/.

Biến N8N_HOSTN8N_PROTOCOL cũng cần khớp với cách bạn truy cập n8n (qua domain + HTTPS hay IP + HTTP).

Điểm nhấn Docker: n8n mặc định dùng SQLite, nhưng dùng PostgreSQL sẽ ổn định hơn khi chạy nhiều workflow. Environment variables ở đây minh họa rõ cách cấu hình app qua biến môi trường (Bài 9), không cần sửa file config bên trong container.

Project 4: Uptime Kuma + Grafana + Prometheus – Monitoring stack

Tổng quan

Ở Bài 13, bạn đã làm quen với monitoring cơ bản. Giờ mình nâng cấp lên một monitoring stack hoàn chỉnh:

  • Uptime Kuma: giám sát uptime các dịch vụ (HTTP, TCP, DNS, ping…), gửi alert qua Telegram/Discord/Email.
  • Prometheus: thu thập metrics từ các exporter (node, container, app…).
  • Grafana: dashboard trực quan, visualize metrics từ Prometheus.

Kết hợp 3 tool này, bạn vừa biết dịch vụ có up không (Uptime Kuma), vừa biết resource đang như thế nào (Prometheus + Grafana).

Cấu trúc thư mục

mkdir -p ~/apps/monitoring && cd ~/apps/monitoring

Tạo file cấu hình Prometheus

Trước khi chạy compose, cần tạo file cấu hình cho Prometheus:

cat > prometheus.yml << 'EOF'
global:
  scrape_interval: 15s
  evaluation_interval: 15s
scrape_configs:
  - job_name: "prometheus"
    static_configs:
      - targets: ["localhost:9090"]
  - job_name: "node-exporter"
    static_configs:
      - targets: ["node-exporter:9100"]
EOF

File này cấu hình Prometheus scrape metrics mỗi 15 giây từ chính nó và từ Node Exporter (cung cấp metrics về CPU, RAM, disk, network của VPS).

docker-compose.yml

services:
  uptime-kuma:
    image: louislam/uptime-kuma:latest
    container_name: uptime-kuma
    restart: unless-stopped
    ports:
      - "3001:3001"
    volumes:
      - uptime_kuma_data:/app/data
    networks:
      - monitoring-net
  prometheus:
    image: prom/prometheus:latest
    container_name: prometheus
    restart: unless-stopped
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
      - prometheus_data:/prometheus
    command:
      - "--config.file=/etc/prometheus/prometheus.yml"
      - "--storage.tsdb.retention.time=30d"
    networks:
      - monitoring-net
  node-exporter:
    image: prom/node-exporter:latest
    container_name: node-exporter
    restart: unless-stopped
    pid: host
    volumes:
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
      - /:/rootfs:ro
    command:
      - "--path.procfs=/host/proc"
      - "--path.sysfs=/host/sys"
      - "--path.rootfs=/rootfs"
      - "--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)"
    networks:
      - monitoring-net
  grafana:
    image: grafana/grafana:latest
    container_name: grafana
    restart: unless-stopped
    ports:
      - "3002:3000"
    environment:
      - GF_SECURITY_ADMIN_USER=admin
      - GF_SECURITY_ADMIN_PASSWORD=grafana_secret_password
    volumes:
      - grafana_data:/var/lib/grafana
    networks:
      - monitoring-net
volumes:
  uptime_kuma_data:
  prometheus_data:
  grafana_data:
networks:
  monitoring-net:

Chạy và kết nối

docker compose up -d

Sau khi chạy, bạn có 3 giao diện web:

  • Uptime Kuma: http://IP-VPS:3001: tạo tài khoản admin, thêm monitor cho các dịch vụ bạn muốn theo dõi.
  • Prometheus: http://IP-VPS:9090: kiểm tra targets ở Status → Targets, đảm bảo cả prometheus và node-exporter đều UP.
  • Grafana: http://IP-VPS:3002: đăng nhập bằng tài khoản admin đã set trong compose file.

Kết nối Grafana với Prometheus

Trong Grafana, thêm datasource:

  1. Vào Connections → Data sources → Add data source.
  2. Chọn Prometheus.
  3. URL: http://prometheus:9090 (dùng tên container vì cùng network).
  4. Click Save & Test.

Sau đó, import dashboard có sẵn: vào Dashboards → Import, nhập ID 1860 (Node Exporter Full) → chọn Prometheus datasource → Import. Bạn sẽ có dashboard monitoring VPS đầy đủ ngay lập tức.

Điểm nhấn Docker: Đây là compose file nhiều service nhất trong bài : 4 services chạy cùng network, giao tiếp qua tên container. Prometheus mount file config từ host (bind mount , Bài 5), Node Exporter mount /proc/sys để đọc metrics hệ thống. Tất cả hoạt động nhịp nhàng nhờ Docker network.

Project 5: Ghost Blog - Modern publishing platform

Ghost là gì?

Ghost là nền tảng blog/publishing hiện đại, viết bằng Node.js. So với WordPress, Ghost nhẹ hơn, nhanh hơn, và tập trung vào trải nghiệm viết bài. Ghost cũng hỗ trợ newsletter, membership, và thanh toán tích hợp, rất phù hợp cho blog cá nhân hoặc trang tin tức.

Cấu trúc thư mục

mkdir -p ~/apps/ghost && cd ~/apps/ghost

docker-compose.yml

services:
  ghost:
    image: ghost:5
    container_name: ghost
    restart: unless-stopped
    ports:
      - "2368:2368"
    environment:
      - url=https://blog.example.com
      - database__client=mysql
      - database__connection__host=ghost-db
      - database__connection__port=3306
      - database__connection__user=ghost
      - database__connection__password=ghost_secret_password
      - database__connection__database=ghost
      - NODE_ENV=production
    volumes:
      - ghost_content:/var/lib/ghost/content
    depends_on:
      ghost-db:
        condition: service_healthy
    networks:
      - ghost-net
  ghost-db:
    image: mysql:8.0
    container_name: ghost-db
    restart: unless-stopped
    environment:
      - MYSQL_ROOT_PASSWORD=root_secret_password
      - MYSQL_DATABASE=ghost
      - MYSQL_USER=ghost
      - MYSQL_PASSWORD=ghost_secret_password
    volumes:
      - ghost_db_data:/var/lib/mysql
    healthcheck:
      test: ["CMD-SHELL", "mysqladmin ping -h localhost"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - ghost-net
volumes:
  ghost_content:
  ghost_db_data:
networks:
  ghost-net:

Chạy và truy cập

docker compose up -d

Truy cập http://IP-VPS:2368 để xem blog. Trang admin ở http://IP-VPS:2368/ghost: tạo tài khoản admin lần đầu.

Lưu ý quan trọng: Biến url phải đúng với domain thực tế bạn sẽ dùng. Nếu chỉ test, dùng http://IP-VPS:2368. Khi đưa lên production với domain + SSL, đổi thành https://blog.example.com.

Volume ghost_content chứa toàn bộ themes, images, và file đã upload. Đây là volume quan trọng nhất cần backup.

Điểm nhấn Docker: Ghost dùng nested environment variables (database__connection__host), cách phổ biến trong nhiều app Node.js. Compose file tuy đơn giản nhưng đầy đủ cho production.

Full VPS Setup - Gộp tất cả với Nginx Proxy Manager

Giờ là phần hay nhất: gộp tất cả 5 project ở trên + Nginx Proxy Manager thành một hệ thống hoàn chỉnh trên VPS. Mỗi app truy cập qua subdomain riêng, SSL tự động, chạy song song không xung đột.

Cấu trúc thư mục recommended

~/apps/
├── nginx-proxy-manager/
│   └── docker-compose.yml
├── gitea/
│   └── docker-compose.yml
├── nextcloud/
│   └── docker-compose.yml
├── n8n/
│   └── docker-compose.yml
├── monitoring/
│   ├── docker-compose.yml
│   └── prometheus.yml
└── ghost/
    └── docker-compose.yml

Nguyên tắc: Mỗi app một thư mục, một compose file. Dễ quản lý, dễ backup, dễ xóa, không app nào ảnh hưởng app khác.

Bước 1: Tạo shared proxy network

Tất cả apps cần kết nối với Nginx Proxy Manager qua một network chung. Tạo network này trước:

docker network create proxy-network

Network này tạo thủ công bên ngoài compose file (external network), nên nó tồn tại độc lập, không bị xóa khi bạn docker compose down bất kỳ app nào.

Bước 2: Nginx Proxy Manager

mkdir -p ~/apps/nginx-proxy-manager && cd ~/apps/nginx-proxy-manager
# ~/apps/nginx-proxy-manager/docker-compose.yml
services:
  npm:
    image: jc21/nginx-proxy-manager:latest
    container_name: nginx-proxy-manager
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
      - "81:81"
    volumes:
      - npm_data:/data
      - npm_letsencrypt:/etc/letsencrypt
    networks:
      - proxy-network
volumes:
  npm_data:
  npm_letsencrypt:
networks:
  proxy-network:
    external: true
docker compose up -d

Truy cập http://IP-VPS:81, đăng nhập lần đầu với email admin@example.com / password changeme, rồi đổi ngay thông tin đăng nhập.

Bước 3: Sửa compose file mỗi app

Để kết nối mỗi app với Nginx Proxy Manager, bạn cần sửa 2 thứ trong mỗi compose file:

1. Bỏ phần ports (không expose port ra ngoài nữa, proxy sẽ lo):

# Xóa hoặc comment dòng ports
# ports:
#   - "3000:3000"

2. Thêm proxy-network cho service chính:

services:
  gitea:
    # ... các config khác giữ nguyên ...
    networks:
      - gitea-net
      - proxy-network    # Thêm dòng này
# ... giữ nguyên phần networks nội bộ ...
networks:
  gitea-net:
  proxy-network:         # Thêm block này
    external: true

Làm tương tự cho từng app: nextcloud, n8n, ghost, uptime-kuma, grafana. Chỉ service chính (cái cần truy cập web) mới cần join proxy-network: database, redis không cần.

Ví dụ: Gitea với proxy-network

Đây là compose file Gitea đã sửa cho production:

services:
  gitea:
    image: gitea/gitea:latest
    container_name: gitea
    restart: unless-stopped
    # Không expose port HTTP — proxy sẽ forward
    ports:
      - "2222:22"  # Giữ SSH port
    environment:
      - USER_UID=1000
      - USER_GID=1000
      - GITEA__database__DB_TYPE=postgres
      - GITEA__database__HOST=gitea-db:5432
      - GITEA__database__NAME=gitea
      - GITEA__database__USER=gitea
      - GITEA__database__PASSWD=gitea_secret_password
      - GITEA__server__ROOT_URL=https://git.example.com/
    volumes:
      - gitea_data:/data
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    depends_on:
      gitea-db:
        condition: service_healthy
    networks:
      - gitea-net
      - proxy-network
  gitea-db:
    image: postgres:16-alpine
    container_name: gitea-db
    restart: unless-stopped
    environment:
      - POSTGRES_USER=gitea
      - POSTGRES_PASSWORD=gitea_secret_password
      - POSTGRES_DB=gitea
    volumes:
      - gitea_db_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U gitea"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - gitea-net
volumes:
  gitea_data:
  gitea_db_data:
networks:
  gitea-net:
  proxy-network:
    external: true

Bước 4: Tạo Proxy Host trong NPM

Với mỗi app, vào Nginx Proxy Manager → Proxy Hosts → Add Proxy Host:

  • Domain: git.example.com
  • Forward Hostname: gitea (tên container)
  • Forward Port: 3000 (port nội bộ của container)
  • SSL: Request new SSL certificate → Force SSL → HSTS

Làm tương tự cho các app khác:

  • cloud.example.com → container nextcloud, port 80
  • n8n.example.com → container n8n, port 5678
  • status.example.com → container uptime-kuma, port 3001
  • grafana.example.com → container grafana, port 3000
  • blog.example.com → container ghost, port 2368

Nhớ trỏ DNS tất cả subdomain về IP VPS trước khi tạo proxy host.

Quản lý hàng ngày

Với cấu trúc này, mỗi app hoàn toàn độc lập:

# Restart chỉ Gitea (không ảnh hưởng app khác)
cd ~/apps/gitea && docker compose restart
# Update Ghost lên version mới
cd ~/apps/ghost && docker compose pull && docker compose up -d
# Xóa hoàn toàn n8n (giữ data nếu muốn)
cd ~/apps/n8n && docker compose down
# Xem log Nextcloud
cd ~/apps/nextcloud && docker compose logs -f nextcloud
# Backup tất cả volumes
for app in gitea nextcloud n8n monitoring ghost; do
  cd ~/apps/$app && docker compose stop
  # backup volumes...
  docker compose start
done

Tổng kết: Bạn đã học được gì từ 5 project này?

Mỗi project ở trên đều minh họa một hoặc nhiều concept từ serie:

  • Multi-service compose: app + database + cache, quản lý bằng 1 file duy nhất.
  • Named volumes: dữ liệu persistent, dễ backup, dễ migrate.
  • Environment variables: cấu hình app mà không sửa code hay file config.
  • Docker networks: services giao tiếp qua tên container, isolate traffic.
  • Health checks & depends_on: đảm bảo thứ tự khởi động đúng.
  • Reverse proxy: nhiều app, nhiều domain, một VPS, SSL tự động.
  • External networks: chia sẻ network giữa các compose file riêng biệt.

Nếu bạn hiểu được tất cả những pattern này, bạn có thể triển khai hầu hết mọi ứng dụng trên VPS bằng Docker.

Docker Compose logs
docker compose logs hiển thị log tất cả services

📚 Serie Docker từ A đến Z

  1. Bài 1: Docker là gì? Tại sao nên dùng Docker trên VPS
  2. Bài 2: Cài đặt Docker và Docker Compose trên VPS Ubuntu
  3. Bài 3: Làm quen với Docker – Các lệnh cơ bản cần biết
  4. Bài 4: Docker Image & Dockerfile – Tự tạo Image riêng
  5. Bài 5: Docker Volume & Network – Quản lý dữ liệu và mạng
  6. Bài 6: Docker Compose là gì? Cài đặt và cú pháp cơ bản
  7. Bài 7: Deploy WordPress + MySQL + phpMyAdmin bằng Docker Compose
  8. Bài 8: Deploy LEMP Stack (Nginx + PHP-FPM + MariaDB) bằng Docker Compose
  9. Bài 9: Biến môi trường & file .env trong Docker Compose
  10. Bài 10: Reverse Proxy với Nginx Proxy Manager + SSL tự động
  11. Bài 11: Deploy ứng dụng Node.js / Python với Docker Compose
  12. Bài 12: Backup & Restore dữ liệu Docker Volume
  13. Bài 13: Monitoring Docker với Portainer, Uptime Kuma và cAdvisor
  14. Bài 14: Docker Logging – Quản lý log hiệu quả
  15. Bài 15: Bảo mật Docker trên VPS
  16. Bài 16: CI/CD đơn giản – Auto deploy với Webhook + Docker Compose
  17. Bài 17: Docker Compose trong thực tế – Tổng hợp project mẫu (đang đọc)

Lời kết serie - Docker từ Zero đến Hero

17 bài viết. Từ câu hỏi "Docker là gì?" đến việc chạy cả một hệ thống multi-app trên VPS với reverse proxy, monitoring, logging, backup, và CI/CD.

Hãy nhìn lại hành trình bạn đã đi qua:

  1. Bài 1-3: Hiểu Docker, cài đặt, chạy container đầu tiên.
  2. Bài 4-5: Dockerfile, volume, network: nền tảng cốt lõi.
  3. Bài 6-9: Docker Compose, triển khai WordPress, LEMP, quản lý biến môi trường.
  4. Bài 10-11: Reverse proxy, deploy app Node.js/Python.
  5. Bài 12-14: Backup, monitoring, logging: vận hành production.
  6. Bài 15-16: CI/CD, security, multi-stage build: chuyên nghiệp hóa.
  7. Bài 17: Tổng hợp project mẫu: áp dụng thực tế.

Đó là một lượng kiến thức vững chắc. Bạn giờ đã có thể tự tin nói: "Mình biết dùng Docker."

Tiếp theo là gì?

Docker trên single VPS là nền tảng. Từ đây, có nhiều hướng để bạn tiếp tục:

  • Docker Swarm: chạy Docker trên nhiều VPS, tự động cân bằng tải và failover. Nếu bạn đã quen Docker Compose, Swarm sẽ rất dễ tiếp cận vì dùng cùng format file compose.
  • Kubernetes (K8s): orchestration ở quy mô lớn hơn. Phức tạp hơn Swarm nhiều, nhưng là tiêu chuẩn ngành cho production ở quy mô enterprise.
  • CI/CD nâng cao: GitHub Actions, GitLab CI, hoặc Gitea Actions (tự host luôn!) để tự động build → test → deploy mỗi khi push code.
  • Container security: scan image với Trivy, chạy container rootless, AppArmor/Seccomp profiles, image signing.

Tài liệu tham khảo

Chọn VPS phù hợp cho Docker

Tất cả project trong serie này đều được thiết kế để chạy trên VPS. Để có trải nghiệm tốt nhất với Docker, bạn nên chọn VPS có:

  • Tối thiểu 2GB RAM: đủ cho 3-5 app nhỏ. 4GB+ nếu chạy nhiều app như bài này.
  • SSD/NVMe storage: Docker build và pull image nhanh hơn nhiều so với HDD.
  • Network tốc độ cao: pull image nhanh, response time thấp cho reverse proxy.
  • Ubuntu 22.04+ hoặc Debian 12+: hỗ trợ Docker tốt nhất.

VPS tại AZDIGI đáp ứng tất cả tiêu chí trên, SSD NVMe, network tốc độ cao, hỗ trợ kỹ thuật 24/7 tiếng Việt. Nếu bạn đang tìm VPS để thực hành Docker hoặc triển khai production, có thể tham khảo các gói VPS tại https://azdigi.com/x-platinum-vps/.


Cảm ơn bạn đã theo dõi serie "Docker từ Zero đến Hero trên VPS" từ Bài 1 đến Bài 17 của AZDIGI.

Mình hy vọng serie này giúp bạn nắm được Docker và áp dụng luôn vào công việc. Từ những lệnh docker run đầu tiên đến việc quản lý cả một hệ thống multi-app trên VPS, bạn đã đi được một chặng đường dài.

Docker không khó. Nó chỉ cần bạn bắt tay vào làm, dần dần sẽ thành thói quen. Và khi đã quen, bạn sẽ không muốn quay lại cách cài đặt thủ công nữa đâu. 😄

Chúc bạn deploy vui vẻ! 🐳

👈 Bài trước: CI/CD đơn giản – Auto deploy với Webhook + Docker Compose

Chia sẻ:
Bài viết đã được kiểm duyệt bởi AZDIGI Team

Về tác giả

Thạch Phạm

Thạch Phạm

Đồng sáng lập và Giám đốc điều hành của AZDIGI. Có hơn 15 năm kinh nghiệm trong phổ biến kiến thức liên quan đến WordPress tại thachpham.com, phát triển website và phát triển hệ thống.

Hơn 10 năm phục vụ 80.000+ khách hàng

Bắt đầu dự án web của bạn với AZDIGI