Hướng dẫn cài đặt Nginx Proxy Manager trên Docker để quản lý SSL và định tuyến cho HomeLab

HomeLab tutorial - IT technology blog
HomeLab tutorial - IT technology blog

Các hướng tiếp cận Reverse Proxy cho hạ tầng Self-Hosted

Khi bạn bắt đầu chạy nhiều dịch vụ web trên máy chủ cục bộ—từ dashboard quản trị, media server cho đến các engine tự động hóa—việc phải ghi nhớ từng cổng như :8096, :8123 hay :9000 sẽ nhanh chóng trở nên rắc rối và khó kiểm soát. Để mở các dịch vụ này ra ngoài an toàn với giao thức mã hóa chuẩn chỉ, bạn cần một lớp reverse proxy đóng vai trò làm cổng vào (entry point) cho toàn bộ hệ thống mạng của mình.

Qua nhiều năm vận hành các môi trường khác nhau, tôi thường thấy những người làm homelab lựa chọn một trong ba hướng đi riêng biệt sau:

  • Cấu hình máy chủ web thủ công (Nginx / Apache cài trực tiếp trên máy): Tự tay viết các server block và virtual host trong các file cấu hình dạng text, kết hợp với các cron trigger chạy certbot độc lập.
  • Enterprise Edge Proxy (Traefik / Caddy): Tận dụng cơ chế tự động nhận diện container thông qua Docker label hoặc dùng các file cấu hình auto-HTTPS tối giản.
  • Reverse Proxy quản lý qua giao diện đồ họa (Nginx Proxy Manager): Chạy một web interface đóng gói sẵn dựng trên nền tảng Nginx và SQLite/MariaDB, giúp bạn xác thực Let’s Encrypt, tạo proxy host và thiết lập danh sách phân quyền (access list) hoàn toàn trực quan.

Đặt lên bàn cân: Ưu và nhược điểm

1. Cấu hình Nginx thủ công

Việc trực tiếp quản lý trong /etc/nginx/sites-available/ cho phép bạn can thiệp sâu vào từng thông số buffer size, header injection hay upstream keepalive. Tuy nhiên, việc tự tay duy trì hàng tá file cấu hình rất dễ dẫn đến sai sót do con người. Ngoài ra, việc gia hạn chứng chỉ trên nhiều subdomain nội bộ dùng split-DNS cũng thường làm gãy cấu hình mỗi khi hook của certbot gặp sự cố.

2. Traefik và proxy điều hướng qua Label

Traefik tự động nhận diện các container mới ngay khi chúng khởi động nhờ lắng nghe Docker socket. Cách này giúp bạn không phải tạo host thủ công, nhưng việc debug lỗi chính tả trong label hay thích ứng với những thay đổi cú pháp giữa các bản nâng cấp lớn của Traefik có thể khiến bạn mất hàng giờ đọc log. Với một homelab nơi phân nửa số dịch vụ có thể đang chạy trên máy ảo riêng biệt hoặc Raspberry Pi nằm ngoài Docker daemon chính, cơ chế nhận diện qua label sẽ mất đi phần lớn sự tiện lợi vốn có.

3. Nginx Proxy Manager (NPM)

Nginx Proxy Manager đem lại sự cân bằng rất thực tế. Bạn vẫn có được sự ổn định cốt lõi của Nginx tiêu chuẩn, nhưng lại sở hữu một giao diện quản trị trực quan, sạch sẽ để cấp phát wildcard SSL certificate, xây dựng access list và bật tắt hỗ trợ WebSocket chỉ với một cú click chuột. Theo kinh nghiệm thực chiến của tôi, đây là một trong những công cụ rất đáng để thuần thục nếu bạn muốn hệ sinh thái self-hosted của mình vận hành bền bỉ mà không biến thành gánh nặng quản trị hệ thống toàn thời gian.

Kiến trúc HomeLab khuyến nghị

Để tránh nguy cơ điểm nghẽn duy nhất (single point of failure) khi định tuyến và giảm thiểu xung đột phụ thuộc giữa các container, đây là mô hình kiến trúc mà tôi khuyên dùng:

  • Mạng Bridge cách ly: Tạo một external Docker network (ví dụ: proxy-network). Bất kỳ container nào bạn muốn public đều sẽ tham gia mạng này, cho phép Nginx Proxy Manager trỏ thẳng tới container mục tiêu theo tên thay vì dựa vào các địa chỉ IP host dễ thay đổi.
  • Tách biệt cơ sở dữ liệu: Mặc dù NPM có thể chạy với SQLite tích hợp, việc triển khai kèm MariaDB sẽ giúp tránh tình trạng khóa database và hỏng file trạng thái khi máy chủ bị mất điện đột ngột.
  • DNS Challenge để cấp Wildcard SSL: Thay vì phải mở port 80 ra internet công cộng để xác thực ACME HTTP-01, hãy cấu hình xác thực DNS-01 thông qua nhà cung cấp DNS của bạn (chẳng hạn như Cloudflare). Cách này sẽ cấp chứng chỉ wildcard Let’s Encrypt hợp lệ (*.yourdomain.com) hoàn toàn từ bên trong mạng LAN mà không cần phơi bày các cổng quản trị nội bộ ra ngoài internet.

Hướng dẫn triển khai: Cài đặt Nginx Proxy Manager

Bước 1: Chuẩn bị Docker Network riêng

Trước khi khởi chạy stack, hãy tạo một mạng external. Điều này cho phép các Docker compose stack độc lập khác gắn vào proxy một cách gọn gàng mà không bắt buộc bạn phải gom tất cả dịch vụ vào chung một file khổng lồ, tương tự như khi bạn cấu hình các container công cụ khác hoặc quản lý container HomeLab bằng Podman Quadlets.

docker network create proxy-network

Bước 2: Tạo cấu hình Docker Compose

Tạo một thư mục trên máy chủ của bạn (ví dụ: ~/homelab/npm) và tạo file triển khai:

mkdir -p ~/homelab/npm && cd ~/homelab/npm
nano docker-compose.yml

Dán cấu hình compose đã được kiểm nghiệm thực tế sau đây vào file:

version: "3.8"

services:
  app:
    image: "jc21/nginx-proxy-manager:latest"
    container_name: "nginx-proxy-manager"
    restart: unless-stopped
    ports:
      - "80:80"      # Lưu lượng HTTP công khai
      - "81:81"      # Giao diện Web quản trị NPM
      - "443:443"    # Lưu lượng HTTPS công khai
    environment:
      DB_MYSQL_HOST: "npm-db"
      DB_MYSQL_PORT: 3306
      DB_MYSQL_USER: "npm_user"
      DB_MYSQL_PASSWORD: "StrongDbSecretPasswordHere"
      DB_MYSQL_NAME: "npm_database"
    volumes:
      - ./data:/data
      - ./letsencrypt:/etc/letsencrypt
    networks:
      - proxy-network
      - npm-internal
    depends_on:
      - db

  db:
    image: "jc21/mariadb-aria:latest"
    container_name: "npm-db"
    restart: unless-stopped
    environment:
      MYSQL_ROOT_PASSWORD: "SuperRootSecretPasswordHere"
      MYSQL_DATABASE: "npm_database"
      MYSQL_USER: "npm_user"
      MYSQL_PASSWORD: "StrongDbSecretPasswordHere"
    volumes:
      - ./mysql:/var/lib/mysql
    networks:
      - npm-internal

networks:
  proxy-network:
    external: true
  npm-internal:
    driver: bridge

Khởi chạy các container ở chế độ chạy nền (detached mode):

docker compose up -d

Bước 3: Thiết lập ban đầu và tăng cường bảo mật

Khi cả hai container đã ở trạng thái healthy, hãy truy cập vào web panel bằng cách mở địa chỉ IP nội bộ của máy chủ qua port 81 trên trình duyệt (ví dụ: http://192.168.1.100:81).

Sử dụng thông tin đăng nhập mặc định sau:

Hệ thống sẽ ngay lập tức yêu cầu bạn đổi tên quản trị viên, email và mật khẩu mới. Hãy hoàn thành bước này trước tiên để đảm bảo an toàn, đồng thời bạn cũng có thể cân nhắc tích hợp thêm giải pháp phòng chống xâm nhập với CrowdSec trên Docker cho toàn bộ hạ tầng.

Bước 4: Tự động hóa chứng chỉ SSL qua DNS-01 Challenge

Thay vì phải đau đầu xử lý lỗi gia hạn do port 80 bị tường lửa chặn, bạn hãy yêu cầu cấp chứng chỉ wildcard bằng phương thức xác thực DNS, một giải pháp cực kỳ thuận tiện nếu homelab của bạn đã có sẵn hệ thống DNS nội bộ trong HomeLab:

  1. Truy cập vào SSL Certificates > Add SSL Certificate > Let’s Encrypt.
  2. Tại mục Domain Names, thêm cả tên miền gốc lẫn bản ghi wildcard: example.com và *.example.com.
  3. Gạt bật tùy chọn Use a DNS Challenge.
  4. Chọn nhà cung cấp DNS của bạn (ví dụ: Cloudflare) và dán DNS API token đã phân quyền tương ứng vào ô thông tin cấu hình:
dns_cloudflare_api_token = your_scoped_api_token_here
  1. Đồng ý với các điều khoản dịch vụ của Let’s Encrypt rồi nhấn Save. NPM sẽ gửi yêu cầu cấp chứng chỉ, xác minh quyền sở hữu thông qua các bản ghi TXT tạm thời và tự động lên lịch chạy ngầm để gia hạn.

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

Bây giờ hãy kết nối một ứng dụng mẫu đang chạy trên máy chủ của bạn. Giả sử bạn có một container ứng dụng đã kết nối vào proxy-network tên là uptime-kuma đang lắng nghe trên cổng 3001, hãy thực hiện theo các bước sau:

  1. Vào mục Hosts > Proxy Hosts > Add Proxy Host.
  2. Điền thông tin trong tab Details:
    • Domain Names: status.example.com
    • Scheme: http
    • Forward Hostname / IP: uptime-kuma (DNS tích hợp sẵn của Docker sẽ tự động phân giải tên container này).
    • Forward Port: 3001
    • Bật Block Common Exploits và Websockets Support (bắt buộc đối với các ứng dụng dựa trên kết nối socket thời gian thực).
  3. Chuyển sang tab SSL:
    • Chọn chứng chỉ wildcard của bạn từ danh sách thả xuống.
    • Tích chọn Force SSL và HTTP/2 Support.
    • Tích chọn HSTS Enabled để phòng chống các cuộc tấn công hạ cấp giao thức (protocol downgrade attack).
  4. Nhấn Save.

Kinh nghiệm thực tế hữu ích

  • Không mở Port 81 ra ngoài Internet: Tuyệt đối không port-forward cổng 81 trên router của bạn. Hãy đảm bảo giao diện quản trị chỉ có thể truy cập được từ bên trong mạng LAN nội bộ hoặc thông qua mạng riêng ảo WireGuard/Tailscale.
  • Tinh chỉnh cấu hình Nginx riêng: Nếu bạn chạy các dịch vụ đòi hỏi tải lên file dung lượng lớn, hãy vào tab Advanced của host đó trong NPM và thêm vào đoạn sau:
client_max_body_size 512M;
proxy_read_timeout 600s;
proxy_send_timeout 600s;

Với mô hình này, mỗi dịch vụ mới bạn dựng lên trong homelab—dù là FileBrowser quản lý file hay gateway truy cập từ xa như Apache Guacamole trên Docker—đều sẽ có ngay một tên miền riêng được mã hóa SSL an toàn chỉ trong chưa đầy ba mươi giây. Nhờ đó, bạn có thể thoải mái tập trung vào việc phát triển dự án thay vì phải vật lộn với các file cấu hình web server.

Share: