Public Docker an toàn trong 10 phút với Nginx Proxy Manager

P P T P Chung

Cách dùng Nginx Proxy Manager để public ứng dụng Docker an toàn trong 10 phút

Bạn có một app chạy Docker ở nhà, VPS, homelab: n8n, Uptime Kuma, Ghost, Portainer, Nextcloud, API nội bộ… App chạy ngon ở http://localhost:3000, nhưng muốn truy cập qua domain đẹp như https://app.example.com.

Cách “thô” thường là mở port app ra Internet. Ví dụ expose 3000:3000, trỏ domain vào IP, xong. Nhanh, nhưng rủi ro: không HTTPS, lộ port, khó quản lý nhiều app, cấu hình SSL thủ công, app nào cũng phải tự lo bảo mật tầng ngoài.

Nginx Proxy Manager giải quyết đúng bài toán này: một reverse proxy có giao diện web, tự xin SSL Let’s Encrypt, route nhiều domain/subdomain về nhiều container khác nhau. Không cần viết file Nginx dài. Không cần nhớ syntax. Phù hợp cho homelab, VPS nhỏ, team dev, staging server.

Mục tiêu bài này: trong khoảng 10 phút, bạn sẽ public một ứng dụng Docker qua domain HTTPS, có SSL, hạn chế expose port, cấu hình an toàn cơ bản.


Nginx Proxy Manager là gì?

Nginx Proxy Manager — thường gọi tắt là NPM — là lớp giao diện quản trị cho Nginx reverse proxy.

Thay vì tự viết:

server {
  listen 443 ssl;
  server_name app.example.com;

location / { proxy_pass http://app:3000; } }

Bạn thao tác bằng UI:

– Nhập domain. – Nhập IP/container/port backend. – Bật SSL. – Chọn Let’s Encrypt. – Lưu.

NPM sẽ tạo config Nginx phía sau.

Khi nào nên dùng NPM?

Rất hợp khi bạn có:

– Nhiều app Docker trên cùng VPS. – Nhiều subdomain: n8n.example.com, status.example.com, api.example.com. – Muốn HTTPS miễn phí, tự gia hạn. – Muốn giấu port thật của app. – Muốn có Basic Auth, Access List, redirect, custom headers. – Không muốn maintain config Nginx thủ công.

Không nên dùng nếu bạn cần hạ tầng cực lớn, config phức tạp, service mesh, zero-downtime routing nâng cao. Khi đó dùng Nginx thuần, Traefik, Caddy, Kubernetes Ingress sẽ hợp hơn.


Kiến trúc đơn giản

Mô hình cơ bản:

Internet
  ↓
Domain: app.example.com
  ↓
Port 80/443 trên VPS
  ↓
Nginx Proxy Manager
  ↓
Docker app nội bộ: app:3000

Điểm quan trọng: chỉ NPM cần public port 80 và 443. App phía sau không cần mở trực tiếp ra Internet.

Ví dụ sai:

ports:
  - "3000:3000"

Ví dụ tốt hơn:

expose:
  - "3000"

Hoặc không cần ports, miễn là NPM và app cùng Docker network.


Chuẩn bị trước khi bắt đầu

Bạn cần:

– Một VPS hoặc máy chủ có IP public. – Docker + Docker Compose. – Một domain/subdomain. – Quyền chỉnh DNS. – Port 80443 mở trên firewall/security group.

Kiểm tra Docker:

docker --version
docker compose version

Tạo bản ghi DNS:

Type: A
Name: app
Value: <IP_VPS>

Ví dụ:

app.example.com → 123.123.123.123

Nếu dùng Cloudflare, lúc xin SSL lần đầu nên để proxy status là DNS only. Sau khi ổn, có thể bật lại orange cloud nếu hiểu rõ SSL mode.


Cài Nginx Proxy Manager bằng Docker Compose

Tạo thư mục:

mkdir -p ~/nginx-proxy-manager
cd ~/nginx-proxy-manager

Tạo file docker-compose.yml:

services:
  npm:
    image: jc21/nginx-proxy-manager:latest
    container_name: npm
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
      - "81:81"
    volumes:
      - ./data:/data
      - ./letsencrypt:/etc/letsencrypt
    networks:
      - proxy

networks: proxy: name: proxy

Chạy:

docker compose up -d

Mở UI:

http://<IP_VPS>:81

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

Email: [email protected]
Password: changeme

Việc đầu tiên: đổi email và password ngay.

Port 81 là cổng quản trị. Sau khi cấu hình xong, không nên để public tự do. Có thể giới hạn bằng firewall, VPN, Tailscale, hoặc chỉ cho IP cá nhân truy cập.


Chạy thử một ứng dụng Docker nội bộ

Ví dụ dùng Uptime Kuma:

mkdir -p ~/uptime-kuma
cd ~/uptime-kuma

Tạo docker-compose.yml:

services:
  uptime-kuma:
    image: louislam/uptime-kuma:1
    container_name: uptime-kuma
    restart: unless-stopped
    expose:
      - "3001"
    volumes:
      - ./data:/app/data
    networks:
      - proxy

networks: proxy: external: true

Chạy:

docker compose up -d

Lưu ý: app không expose port ra host. Nó chỉ nằm trong Docker network proxy, nơi NPM có thể truy cập.


Tạo Proxy Host trong NPM

Vào giao diện NPM:

Hosts → Proxy Hosts → Add Proxy Host

Tab Details:

Domain Names: status.example.com
Scheme: http
Forward Hostname / IP: uptime-kuma
Forward Port: 3001

Bật:

Block Common ExploitsWebsockets Support nếu app cần realtime/websocket

Không bật cache nếu chưa chắc app phù hợp.

Bấm Save.

Lúc này thử:

http://status.example.com

Nếu DNS đã trỏ đúng, bạn sẽ thấy app.


Bật HTTPS miễn phí với Let’s Encrypt

Mở lại Proxy Host vừa tạo:

Edit → SSL

Chọn:

SSL Certificate: Request a new SSL Certificate

Bật:

Force SSLHTTP/2 SupportHSTS Enabled nếu bạn chắc domain luôn dùng HTTPS – I Agree to the Let’s Encrypt Terms of Service

Nhập email thật để nhận cảnh báo hết hạn cert.

Bấm Save.

Sau vài giây, truy cập:

https://status.example.com

Nếu thành công, bạn đã public app Docker bằng HTTPS.


Cấu hình an toàn tối thiểu

Public app không chỉ là “truy cập được”. Cần giảm bề mặt tấn công.

1. Không expose port app ra Internet

Tránh:

ports:
  - "3001:3001"

Ưu tiên:

expose:
  - "3001"

Hoặc chỉ dùng Docker network nội bộ.

Lý do: nếu app có lỗi auth, dashboard debug, API chưa khóa, attacker có thể truy cập thẳng qua port thật, bỏ qua proxy.

2. Khóa port quản trị NPM

Port 81 rất nhạy cảm. Ít nhất nên giới hạn firewall.

Ví dụ với UFW:

sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow from <IP_CUA_BAN> to any port 81 proto tcp
sudo ufw enable

Nếu IP cá nhân thay đổi thường xuyên, dùng VPN/Tailscale/ZeroTier sẽ ổn hơn.

3. Đặt password mạnh

Đổi ngay tài khoản admin mặc định. Dùng password manager. Không dùng lại password.

4. Bật Access List cho app nội bộ

Với app nhạy cảm như Portainer, admin panel, database UI:

Access Lists → Add Access List

Có thể thêm:

– Basic Auth. – Allow/deny IP. – Chỉ cho IP công ty/VPN.

Sau đó gán Access List vào Proxy Host.

5. Bật bảo vệ cơ bản

Trong Proxy Host:

Block Common Exploits: nên bật. – Force SSL: nên bật. – HTTP/2: nên bật. – HSTS: chỉ bật khi chắc chắn không cần HTTP nữa.

HSTS có thể gây khó chịu nếu cấu hình sai, vì trình duyệt sẽ ép HTTPS trong thời gian dài.


Lỗi thường gặp và cách xử lý nhanh

SSL không xin được

Nguyên nhân phổ biến:

– DNS chưa trỏ đúng IP. – Port 80 bị chặn. – Cloudflare đang proxy sai mode. – Domain nhập sai. – NPM không reachable từ Internet.

Kiểm tra:

dig status.example.com
curl -I http://status.example.com

502 Bad Gateway

NPM không gọi được backend.

Kiểm tra:

– Container app có chạy không:

docker ps

– NPM và app có cùng network không:

docker network inspect proxy

– Forward hostname đúng tên service/container chưa. – Forward port đúng port nội bộ chưa.

Websocket lỗi

Bật:

Websockets Support

Một số app như n8n, Uptime Kuma, code-server, Home Assistant cần tùy chọn này.

App redirect sai domain

Một số app cần cấu hình biến môi trường public URL.

Ví dụ n8n thường cần:

environment:
  - N8N_HOST=n8n.example.com
  - N8N_PROTOCOL=https
  - WEBHOOK_URL=https://n8n.example.com/

Nếu không, app có thể redirect về localhost hoặc URL nội bộ.


Backup và cập nhật

NPM lưu dữ liệu ở:

./data
./letsencrypt

Backup hai thư mục này là đủ cho phần lớn setup.

Ví dụ:

tar -czf npm-backup.tar.gz ~/nginx-proxy-manager/data ~/nginx-proxy-manager/letsencrypt

Cập nhật:

cd ~/nginx-proxy-manager
docker compose pull
docker compose up -d

Nên backup trước khi update, nhất là server đang chạy nhiều app quan trọng.


Kết luận thực tế

Nginx Proxy Manager là cách nhanh, gọn, dễ vận hành để public ứng dụng Docker qua HTTPS. Với một Docker network chung, vài bản ghi DNS, một Proxy Host, bạn có thể đưa app ra Internet trong khoảng 10 phút mà không cần viết config Nginx thủ công.

Công thức an toàn tối thiểu:

– Chỉ mở port 80/443. – Không expose port app. – Khóa port admin 81. – Bật SSL, Force SSL, Block Common Exploits. – Dùng Access List cho app nhạy cảm. – Backup dataletsencrypt.

Nhanh không đồng nghĩa với ẩu. Public ít, khóa kỹ, backup đều. NPM lo phần proxy; bạn vẫn phải lo quyền truy cập, mật khẩu, cập nhật container, và dữ liệu phía sau.

Tác giả

P T P

Chia sẻ

Bài viết liên quan

Bình luận (0)

Email của bạn sẽ không được hiển thị công khai.

Chưa có bình luận. Hãy là người đầu tiên!