Hướng dẫn cài đặt Nginx Proxy Manager với Docker Compose: Reverse Proxy và SSL tự động qua giao diện Web

Docker tutorial - IT technology blog
Docker tutorial - IT technology blog

Vấn đề thực tế: Quản lý nhiều dịch vụ trên một VPS

Nếu bạn đang chạy nhiều ứng dụng trên cùng một VPS — ví dụ Nextcloud, Gitea, một API backend và một trang WordPress — bạn sẽ nhanh chóng gặp bài toán cổ điển: tất cả đều muốn chạy trên cổng 80 và 443, nhưng chỉ một tiến trình được phép lắng nghe trên mỗi cổng.

Giải pháp truyền thống là cấu hình Nginx thủ công làm reverse proxy: mỗi khi thêm dịch vụ mới, bạn phải tạo một file config mới, xin SSL certificate từ Let’s Encrypt, reload Nginx. Không phức tạp về kỹ thuật, nhưng lặp đi lặp lại và dễ mắc lỗi — nhất là khi quản lý 5–10 subdomain cùng lúc.

Ba cách tiếp cận phổ biến — So sánh thẳng thắn

1. Nginx thuần + Certbot thủ công

Ưu điểm: Nhẹ, toàn quyền kiểm soát, không phụ thuộc thêm công cụ nào.

Nhược điểm: Mỗi lần thêm domain phải chạy certbot, sửa file /etc/nginx/sites-available/, rồi nginx -t && systemctl reload nginx. Với 10 subdomain, điều này trở thành gánh nặng bảo trì.

2. Traefik

Ưu điểm: Tích hợp tốt với Docker, tự động phát hiện container qua label, hỗ trợ Let’s Encrypt.

Nhược điểm: Cấu hình qua file YAML và label phức tạp, đường cong học tập dốc hơn. Không có Web UI trực quan để xem trạng thái routing.

3. Nginx Proxy Manager (NPM)

Ưu điểm: Giao diện Web đẹp, thêm proxy host chỉ cần điền form, SSL Let’s Encrypt tự động một click, hỗ trợ access list, redirect, stream proxy.

Nhược điểm: Nặng hơn một chút vì có database đi kèm (SQLite hoặc MariaDB), cần hiểu cơ bản về reverse proxy để dùng đúng.

Khi nào chọn Nginx Proxy Manager?

NPM phù hợp nhất khi:

  • Bạn quản lý VPS cá nhân hoặc team nhỏ, ưu tiên tốc độ triển khai hơn tối ưu tài nguyên
  • Đội nhóm có người không quen sửa file config Nginx tay
  • Cần thêm/xóa subdomain thường xuyên mà không muốn SSH vào server mỗi lần
  • Muốn có dashboard xem nhanh trạng thái tất cả proxy host

Lần đầu dùng Docker Compose cho dự án thực tế, mình mắc khá nhiều lỗi cơ bản mà bây giờ nghĩ lại thấy buồn cười — trong đó có việc cố cấu hình Nginx thủ công bên ngoài Docker trong khi các container chạy bên trong network riêng. Phát hiện ra NPM chạy ngay trong Docker network giải quyết sạch vấn đề đó.

Triển khai Nginx Proxy Manager với Docker Compose

Yêu cầu trước khi bắt đầu

  • VPS chạy Linux (Ubuntu 22.04 hoặc Debian 12 khuyến nghị)
  • Docker và Docker Compose đã cài đặt
  • Domain đã trỏ A record về IP của VPS
  • Cổng 80, 443, 81 đã được mở trên firewall

Bước 1: Tạo thư mục và file docker-compose.yml

mkdir -p ~/npm && cd ~/npm

Tạo file docker-compose.yml:

version: '3.8'

services:
  npm:
    image: 'jc21/nginx-proxy-manager:latest'
    container_name: nginx-proxy-manager
    restart: unless-stopped
    ports:
      - '80:80'    # HTTP
      - '443:443'  # HTTPS
      - '81:81'    # Web UI
    volumes:
      - ./data:/data
      - ./letsencrypt:/etc/letsencrypt

networks:
  default:
    name: npm_network
    driver: bridge

Cấu trúc trên dùng SQLite mặc định — đủ dùng cho VPS cá nhân. Nếu cần scale hoặc backup dễ hơn, bạn có thể thêm MariaDB vào stack.

Bước 2: Khởi động NPM

docker compose up -d

# Kiểm tra log
docker compose logs -f npm

Sau khoảng 30 giây, truy cập http://<IP-VPS>:81 để vào giao diện quản lý.

Thông tin đăng nhập mặc định:

Đổi ngay sau lần đăng nhập đầu tiên.

Bước 3: Thêm Proxy Host đầu tiên

Giả sử bạn có một ứng dụng đang chạy trên container tên myapp, lắng nghe cổng 3000 trong Docker network npm_network.

  1. Vào Proxy HostsAdd Proxy Host
  2. Domain Names: nhập app.yourdomain.com
  3. Scheme: http
  4. Forward Hostname/IP: tên container (myapp) hoặc IP nội bộ
  5. Forward Port: 3000
  6. Bật Block Common ExploitsWebsockets Support nếu cần
  7. Tab SSL → chọn Request a new SSL Certificate → bật Force SSLHTTP/2 Support
  8. Nhập email Let’s Encrypt → tick đồng ý → Save

NPM sẽ tự động lấy certificate từ Let’s Encrypt và cấu hình HTTPS. Toàn bộ quá trình mất dưới 30 giây.

Bước 4: Kết nối ứng dụng Docker vào cùng network

Để NPM có thể forward request đến các container khác, chúng phải ở cùng Docker network. Cách đơn giản nhất là khai báo external network trong docker-compose.yml của ứng dụng:

services:
  myapp:
    image: myapp:latest
    container_name: myapp
    # Không cần expose port ra host
    networks:
      - npm_network

networks:
  npm_network:
    external: true

Với cách này, myapp không cần publish port ra ngoài host — chỉ NPM mới nhận traffic từ internet, các container còn lại hoàn toàn isolated.

Một số tình huống thực tế hay gặp

Redirect HTTP sang HTTPS cho toàn bộ subdomain

Trong phần SSL của mỗi Proxy Host, bật Force SSL là đủ. NPM tự xử lý redirect 301.

Thêm Basic Authentication cho một subdomain nội bộ

Vào tab Access Lists → tạo list mới với username/password → gán vào Proxy Host muốn bảo vệ. Hữu ích cho Grafana, Kibana hoặc các tool internal không có auth riêng.

Gia hạn SSL tự động

NPM tự động gia hạn certificate qua cronjob nội bộ. Bạn không cần làm gì thêm — khác với setup Certbot thủ công phải kiểm tra systemctl status certbot.timer.

Backup và restore

Toàn bộ dữ liệu (config, certificate, database) nằm trong thư mục ./data./letsencrypt mà bạn đã mount. Backup đơn giản:

tar -czf npm-backup-$(date +%Y%m%d).tar.gz ~/npm/data ~/npm/letsencrypt

Những điều cần lưu ý

  • Cổng 81 chỉ nên mở tạm thời hoặc giới hạn theo IP. Sau khi cấu hình xong, đóng cổng 81 trên firewall và chỉ mở khi cần quản trị.
  • Let’s Encrypt rate limit: Tối đa 5 certificate mới cho cùng một domain trong 7 ngày. Đừng thử đi thử lại nhiều lần khi test — dùng staging environment trước.
  • Wildcard SSL: NPM hỗ trợ wildcard certificate (*.yourdomain.com) nhưng cần xác minh qua DNS challenge, yêu cầu cấu hình thêm DNS provider (Cloudflare, AWS Route53…).
  • Version pinning: Thay jc21/nginx-proxy-manager:latest bằng tag cụ thể trong môi trường production để tránh breaking change khi update.

Share: