ติดตั้ง Nginx Proxy Manager จัดการ Reverse Proxy ด้วย Web UI
ถ้าคุณรัน service หลายตัวบน VPS เครื่องเดียว เช่น Nextcloud, Gitea, Portainer, Grafana — การจัดการ Nginx config ด้วยมือทุกครั้งที่เพิ่ม service ใหม่นั้นยุ่งยากและเสี่ยง error Nginx Proxy Manager (NPM) คือทางออกที่หลายคนใช้ เป็น Web UI สำหรับจัดการ reverse proxy และ SSL Certificate แบบ point-and-click ไม่ต้องแตะไฟล์ config เลย
NPM รันบน Docker ติดตั้งง่าย รองรับ Let's Encrypt อัตโนมัติ ออก wildcard cert ได้ และมีระบบ Access List สำหรับจำกัดการเข้าถึงด้วย IP หรือ username/password ในบทความนี้จะพาทำทุกขั้นตอนตั้งแต่ติดตั้งจนใช้งานได้จริง
Nginx Proxy Manager คืออะไร
Nginx Proxy Manager เป็น open-source project ที่ wrap Nginx ไว้ใน Docker container พร้อม Admin UI สำหรับจัดการ:
- Proxy Hosts — ชี้ domain/subdomain ไปยัง service ปลายทาง (http://localhost:PORT)
- SSL Certificates — ออก cert จาก Let's Encrypt อัตโนมัติ รวมถึง wildcard cert ผ่าน DNS challenge
- Redirections — redirect HTTP → HTTPS หรือ domain เก่า → domain ใหม่
- Streams — proxy TCP/UDP (เช่น forward port MySQL, SSH)
- Access Lists — ป้องกัน service ด้วย HTTP Basic Auth หรือ IP whitelist
ความต้องการระบบ
- VPS รัน Ubuntu 20.04/22.04/24.04 หรือ Debian 11/12
- Docker และ Docker Compose ติดตั้งแล้ว
- Port 80, 443 และ 81 เปิดอยู่ (81 สำหรับ Admin UI)
- Domain ที่ชี้ DNS A Record มาที่ IP ของ VPS
- RAM อย่างน้อย 512 MB (แนะนำ 1 GB)
ติดตั้ง Docker และ Docker Compose
ถ้ายังไม่มี Docker รันคำสั่งต่อไปนี้บน Ubuntu/Debian:
# ติดตั้ง Docker อย่างเป็นทางการ
curl -fsSL https://get.docker.com | sh
# เพิ่ม user ปัจจุบันเข้า docker group (ไม่ต้องใช้ sudo ทุกครั้ง)
sudo usermod -aG docker $USER
newgrp docker
# ตรวจสอบ
docker --version
docker compose version
สร้างไฟล์ Docker Compose สำหรับ NPM
สร้าง directory และ docker-compose.yml:
mkdir -p ~/nginx-proxy-manager && cd ~/nginx-proxy-manager
สร้างไฟล์ docker-compose.yml:
services:
app:
image: 'jc21/nginx-proxy-manager:latest'
restart: unless-stopped
ports:
- '80:80' # HTTP
- '443:443' # HTTPS
- '81:81' # Admin UI
volumes:
- ./data:/data
- ./letsencrypt:/etc/letsencrypt
รัน NPM:
docker compose up -d
# ตรวจสอบสถานะ
docker compose ps
docker compose logs -f app
./data อย่างไรก็ตามถ้าต้องการ HA หรือ multi-node ควรเปลี่ยนเป็น MySQL/MariaDB โดยเพิ่ม environment variable ใน compose fileเข้าใช้งาน Admin UI ครั้งแรก
เปิดเบราว์เซอร์ไปที่ http://YOUR-VPS-IP:81 แล้ว login ด้วย default credentials:
- Email:
[email protected] - Password:
changeme
ระบบจะบังคับให้เปลี่ยน email และ password ทันทีหลัง login ครั้งแรก อย่าตั้ง password อ่อนแอ เพราะ Admin UI มีสิทธิ์ควบคุม traffic ทั้งหมดของ server
เพิ่ม Proxy Host (Forward Domain → Service)
ตัวอย่าง: forward app.example.com ไปยัง Portainer ที่รันบน localhost:9000
- ไปที่ Proxy Hosts → Add Proxy Host
- กรอก Domain Names:
app.example.com - Forward Hostname/IP:
172.17.0.1(Docker host IP) หรือhost.docker.internal - Forward Port:
9000 - เปิด Block Common Exploits
- ไปแท็บ SSL → Request a new SSL Certificate → เปิด Force SSL และ HTTP/2 Support
- กรอก Email สำหรับ Let's Encrypt → Save
portainer แต่ถ้า service รันบน host ใช้ host.docker.internal (Docker Desktop) หรือ 172.17.0.1 (Linux Docker bridge)ตั้งค่า Wildcard SSL Certificate
สำหรับ wildcard cert (*.example.com) ต้องใช้ DNS challenge ผ่าน provider ที่รองรับ เช่น Cloudflare:
- ไปที่ SSL Certificates → Add SSL Certificate → Let's Encrypt
- กรอก Domain:
*.example.comและexample.com - เปิด Use a DNS Challenge → เลือก Cloudflare
- กรอก Cloudflare API Token (ต้องมีสิทธิ์ Zone:DNS:Edit)
- Save — NPM จะ verify ผ่าน DNS TXT record และออก cert ให้อัตโนมัติ
ตั้งค่า Access List ป้องกัน Service
ถ้ามี service ที่ไม่ต้องการให้ทุกคนเข้าถึง เช่น Grafana dashboard หรือ admin panel:
- ไปที่ Access Lists → Add Access List
- ตั้งชื่อ เช่น
internal-only - แท็บ Authorization — เพิ่ม username/password สำหรับ Basic Auth
- แท็บ Access — ระบุ IP ที่อนุญาต (เช่น
192.168.1.0/24) หรือปล่อยว่างถ้าใช้ Basic Auth อย่างเดียว - ไปที่ Proxy Host → Edit → เลือก Access List ที่สร้างไว้
TCP/UDP Stream Proxy
NPM รองรับการ proxy TCP/UDP ด้วย ไปที่ Streams → Add Stream ระบุ Incoming Port และ Forward Host/Port เหมาะสำหรับ forward port database หรือ game server ที่ต้องการ expose ออก internet โดยไม่ต้องใช้ SSH tunnel
ตรวจสอบว่า Nginx Proxy Manager ทำงานถูกต้อง
# ดูสถานะ container
docker compose ps
# ดู log แบบ real-time
docker compose logs -f app
# ทดสอบ HTTP response header ของ domain ที่ตั้งค่า
curl -I https://app.example.com
# ตรวจสอบ SSL certificate วันหมดอายุ
echo | openssl s_client -connect app.example.com:443 2>/dev/null | openssl x509 -noout -dates
# ดู Nginx config ที่ NPM สร้างขึ้น
docker exec -it nginx-proxy-manager-app-1 cat /etc/nginx/conf.d/default.conf
# ตรวจสอบ port ที่ NPM ฟัง
ss -tlnp | grep -E ':80|:81|:443'
แก้ปัญหาที่พบบ่อย
Error: "Port 80 is already in use"
เกิดเมื่อมี Nginx หรือ Apache รันบน host อยู่แล้ว NPM ไม่สามารถ bind port 80 ได้ ต้องหยุด service เหล่านั้นก่อน หรือเปลี่ยน port mapping
# หา process ที่ใช้ port 80
sudo ss -tlnp | grep :80
sudo systemctl stop nginx apache2 2>/dev/null
sudo systemctl disable nginx apache2
# รัน NPM ใหม่
docker compose up -d
Error: "SSL Certificate could not be obtained"
Let's Encrypt ยืนยัน domain ไม่ได้ มักเกิดจาก DNS ยังไม่ propagate, port 80 ถูกบล็อก หรือ Cloudflare proxy เปิดอยู่ (สำหรับ HTTP challenge)
# ตรวจสอบว่า domain ชี้มาที่ IP ถูกต้อง
dig +short app.example.com
# ทดสอบว่า port 80 เปิดจากภายนอก
curl -I http://app.example.com
# ดู log error ของ NPM
docker compose logs app | grep -i "error\|cert\|acme"
# ถ้าใช้ Cloudflare → ตั้ง SSL Mode เป็น "Full" ไม่ใช่ "Off"
# หรือปิด Cloudflare proxy (orange cloud) ชั่วคราวระหว่างขอ cert
Error: "502 Bad Gateway" หลัง proxy host ตั้งค่าแล้ว
NPM เชื่อมต่อ service ปลายทางไม่ได้ ตรวจสอบ IP และ port ที่กรอก รวมถึง service ปลายทางว่ารันอยู่จริง
# ทดสอบจากภายใน container ของ NPM
docker exec -it nginx-proxy-manager-app-1 sh -c "curl http://172.17.0.1:9000"
# ถ้าใช้ host.docker.internal บน Linux ต้องเพิ่ม extra_hosts ใน compose:
# extra_hosts:
# - "host.docker.internal:host-gateway"
# ตรวจสอบ service ปลายทางรันอยู่
docker ps | grep portainer
ss -tlnp | grep 9000
Error: Admin UI port 81 เข้าไม่ได้จากเบราว์เซอร์
Firewall บน VPS บล็อก port 81 ไว้ หรือ container ยังไม่ start
# เปิด port 81 ชั่วคราวสำหรับ initial setup
sudo ufw allow 81/tcp
# ตรวจสอบ container รันอยู่
docker compose ps
# หลัง setup เสร็จ ควรปิด port 81 จาก internet และ proxy ผ่าน NPM แทน
# สร้าง proxy host: admin.example.com → localhost:81
sudo ufw delete allow 81/tcp
Container restart loop หรือ NPM crash ทุกครั้งที่ start
มักเกิดจาก data directory มีสิทธิ์ไม่ถูกต้อง, port conflict, หรือ disk เต็ม
# ดู exit log
docker compose logs --tail=50 app
# ตรวจสอบ disk space (SQLite เต็มทำให้ crash)
df -h
# reset data เมื่อ config เสียหาย (ระวัง: ลบทุกอย่าง)
docker compose down
sudo rm -rf ./data ./letsencrypt
docker compose up -d
คำถามที่พบบ่อย
NPM ต่างจาก Traefik และ Caddy อย่างไร?
NPM เน้น ความง่ายผ่าน Web UI เหมาะสำหรับคนที่ไม่ต้องการแก้ config file Traefik ใช้ label บน Docker container สำหรับ auto-discovery เหมาะกับ environment ที่ service เปลี่ยนบ่อย Caddy ตั้งค่าง่ายผ่าน Caddyfile และมี HTTPS อัตโนมัติในตัวโดยไม่ต้องพึ่ง Let's Encrypt UI NPM ชนะด้านความเป็นมิตรกับมือใหม่ แต่ Traefik ยืดหยุ่นกว่าสำหรับ production Kubernetes
NPM ปลอดภัยพอสำหรับ production ไหม?
ใช้ได้สำหรับ self-hosted และ small-medium production โดยควรทำดังนี้: (1) เปลี่ยน default credentials ทันทีหลัง login ครั้งแรก (2) ปิด port 81 จาก internet หลัง setup เสร็จ — proxy Admin UI ผ่าน NPM เองแทน (3) อัปเดต image สม่ำเสมอด้วย docker compose pull && docker compose up -d (4) ใช้ Access List กับทุก service ที่ sensitive
Backup และ Restore ข้อมูล NPM ทำอย่างไร?
ข้อมูลทั้งหมดอยู่ใน 2 directory คือ ./data (config, SQLite DB) และ ./letsencrypt (SSL certs) สำรองทั้ง 2 folder ก็พอ ไม่จำเป็นต้อง export database:
# Backup
tar -czf npm-backup-$(date +%Y%m%d).tar.gz ./data ./letsencrypt
# Restore (บน server ใหม่)
tar -xzf npm-backup-20260903.tar.gz
docker compose up -d
Let's Encrypt cert มีอายุ 90 วัน NPM ต่ออายุให้อัตโนมัติ แต่ถ้า backup cert ไว้จะไม่ต้องรอออกใหม่เมื่อ restore ทันที