Gõ từng dòng trong /etc/nginx/sites-available/, reload service rồi nhận về dòng báo lỗi cú pháp đỏ lòm là trải nghiệm quen thuộc của dân quản trị web. Mỗi lần gia hạn SSL bằng Certbot lại thêm một phen hồi hộp xem cron job có chạy hay không. Khi hạ tầng chạm mốc 15–20 subdomain, việc cấu hình thủ công rất dễ sót. Nginx Proxy Manager (NPM) gom toàn bộ công đoạn này vào một giao diện trực quan, biến mớ file cấu hình dài ngoằng thành vài form nhập liệu đơn giản.
1. Quick start: Triển khai Nginx Proxy Manager trong 5 phút
Trên các VPS Ubuntu 22.04 hoặc 24.04 mới tinh, đây là dịch vụ đầu tiên mình luôn dựng để làm reverse proxy gateway cho toàn bộ web app nội bộ. Mọi thứ được đóng gói sẵn trong Docker Compose.
Cài đặt Docker engine và plugin Compose trên Ubuntu:
sudo apt update && sudo apt install -y docker.io docker-compose-v2
sudo systemctl enable --now docker
Tạo thư mục chứa cấu hình:
mkdir -p ~/npm && cd ~/npm
nano docker-compose.yml
Dán nội dung sau vào file docker-compose.yml:
services:
app:
image: 'jc21/nginx-proxy-manager:latest'
restart: unless-stopped
ports:
- '80:80'
- '81:81'
- '443:443'
volumes:
- ./data:/data
- ./letsencrypt:/etc/letsencrypt
Khởi động container ở chế độ chạy ngầm:
docker compose up -d
Đợi khoảng 30–45 giây để container khởi tạo database SQLite ban đầu. Sau đó, mở trình duyệt truy cập: http://IP_SERVER:81.
Thông tin đăng nhập mặc định:
- Email:
[email protected] - Password:
changeme
Hệ thống sẽ ép đổi email và mật khẩu ngay sau lần đăng nhập đầu tiên. Đừng bỏ qua bước này vì cổng 81 đang mở công khai ra Internet.
2. Giải thích chi tiết: Cơ chế hoạt động của Nginx Proxy Manager
Nginx truyền thống thường vấp ở đâu?
Theo cách làm cũ, muốn trỏ một domain về ứng dụng Node.js (cổng 3000) hay container Docker (cổng 8080), bạn phải tạo file vhost riêng. Trong đó cần định nghĩa upstream, khối location /, chỉnh proxy_pass và truyền đủ các header như X-Real-IP, X-Forwarded-For.
Kế tiếp là chạy Certbot xin chứng chỉ SSL. Khi hệ thống phình to lên hàng chục app, chỉ cần quên dấu chấm phẩy ; ở một file là toàn bộ Nginx ngưng reload.
Nginx Proxy Manager xử lý việc này thế nào?
NPM không viết lại Nginx từ đầu. Nó đóng vai trò là một web panel điều khiển Nginx và Certbot chạy bên dưới container:
- Mỗi khi bạn tạo một host trên Web UI, hệ thống sẽ sinh ra một file
.confchuẩn trong thư mục./data/nginx/proxy_host/. - Certbot bên trong container tự động gọi ACME challenge qua HTTP-01 hoặc DNS-01 để xin chứng chỉ từ Let’s Encrypt rồi lưu vào
./letsencrypt. - NPM phát tín hiệu
nginx -s reloadtức thì mà không rớt một kết nối HTTP nào đang hoạt động.
Từng bước tạo một Proxy Host chuẩn chỉ
- Vào menu Hosts → Proxy Hosts → bấm nút Add Proxy Host.
- Tab Details:
- Domain Names: Nhập domain (ví dụ:
api.itfromzero.com). Đảm bảo bản ghi DNS A đã trỏ đúng về IP của VPS. - Scheme: Để
httpnếu app nội bộ chưa bật SSL. - Forward Hostname / IP: Điền IP nội bộ máy chủ hoặc tên Docker container nếu chung network.
- Forward Port: Nhập cổng ứng dụng (ví dụ:
3000). - Gạt bật Websockets Support (bắt buộc nếu web app dùng Socket.io, NestJS hoặc Next.js có live reload/chat).
- Bật Block Common Exploits để lọc nhanh các payload độc hại phổ biến như SQLi hay directory traversal cơ bản.
- Domain Names: Nhập domain (ví dụ:
- Tab SSL:
- Mục SSL Certificate: Chọn Request a new SSL Certificate.
- Bật Force SSL để ép toàn bộ request HTTP (port 80) chuyển hướng sang HTTPS (port 443).
- Bật HTTP/2 Support và HSTS Enabled để tăng tốc độ tải trang và chống tấn công hạ cấp giao thức.
- Tích chọn I Agree to the Let’s Encrypt Terms of Service rồi bấm Save.
3. Nâng cao: Access Lists và Docker Network nội bộ
Khoá chặt trang quản trị với Access Lists
Các dashboard nhạy cảm như Portainer, PgAdmin hay tool backup nội bộ không nên phơi ra ngoài cho bất kỳ ai truy cập. Tính năng Access Lists của NPM xử lý việc này theo hai lớp:
- HTTP Basic Authentication: Bắt người dùng nhập username/password qua hộp thoại trình duyệt trước khi chạm đến trang đăng nhập chính của app.
- IP Whitelisting: Chỉ cho phép các dải IP cố định (ví dụ IP mạng công ty
14.241.x.x/32hoặc IP VPN WireGuard) kết nối; mọi IP lạ khác đều nhận mã 403 Forbidden.
Kết nối container trực tiếp qua Docker Network
Mở port trực tiếp ra ngoài máy chủ bằng ports: ["8080:8080"] khiến dịch vụ dễ bị bot dò quét tự động. Cách an toàn nhất là đưa NPM và các app backend vào chung một bridge network nội bộ:
# Tạo bridge network dùng chung
docker network create proxy-net
Cập nhật lại docker-compose.yml của Nginx Proxy Manager để dùng network này:
services:
app:
image: 'jc21/nginx-proxy-manager:latest'
restart: unless-stopped
ports:
- '80:80'
- '81:81'
- '443:443'
volumes:
- ./data:/data
- ./letsencrypt:/etc/letsencrypt
networks:
- proxy-net
networks:
proxy-net:
external: true
Chạy docker compose up -d để apply thay đổi. Lúc này, với bất kỳ web app nào cắm vào proxy-net, ở ô Forward Hostname trên giao diện NPM bạn chỉ cần gõ đúng tên container (ví dụ: strapi-cms hoặc grafana) thay vì dùng địa chỉ IP.
4. Kinh nghiệm thực chiến khi vận hành trên Ubuntu
Xử lý xung đột cổng 80 và 443 trên VPS cũ
Lỗi phổ biến nhất khi cài đặt là Docker báo: bind: address already in use. Nghĩa là máy chủ đang có dịch vụ khác chiếm cổng 80 hoặc 443. Xác định ngay tiến trình gây nghẽn bằng lệnh:
sudo ss -tulpn | grep -E ':(80|443)'
Nếu thấy apache2 hoặc nginx có sẵn của Ubuntu chiếm giữ, hãy dừng và tắt khởi động cùng hệ thống:
sudo systemctl stop nginx apache2
sudo systemctl disable nginx apache2
Khóa cổng quản trị 81 khỏi Internet công cộng
Port 81 phơi ra ngoài rất dễ bị bot bruteforce tài khoản admin. Có hai phương án gia cố:
- Đổi bind IP trong docker-compose.yml: Sửa port mapping thành
- '127.0.0.1:81:81'. Khi cần vào cấu hình, mở kết nối SSH Tunnel từ máy local qua lệnh:ssh -L 8181:localhost:81 user@IP_SERVERrồi truy cậphttp://localhost:8181. - Tạo Proxy Host cho chính NPM: Trỏ tên miền nội bộ (ví dụ
npm.internal.yourdomain.com) về127.0.0.1:81và đặt thêm Access List giới hạn IP.
Quy trình sao lưu an toàn
Tất cả cấu hình proxy và chứng chỉ SSL nằm trọn trong hai thư mục ./data và ./letsencrypt. Để tránh lỗi hỏng dữ liệu SQLite khi copy file lúc database đang ghi, hãy pause container tạm thời trước khi nén:
cd ~/npm
docker compose pause
tar -czvf npm-backup-$(date +%F).tar.gz data/ letsencrypt/
docker compose unpause
Quá trình pause chỉ mất khoảng 2 giây, không ngắt kết nối mạng lâu và đảm bảo file backup nguyên vẹn 100%.

