ติดตั้ง Nginx Proxy Manager จัดการ Reverse Proxy ด้วย Web UI บน VPS

ติดตั้ง Nginx Proxy Manager จัดการ Reverse Proxy ด้วย Web UI

อัปเดต 3 กันยายน 2026 · Docker · Nginx · Reverse Proxy · SSL

ถ้าคุณรัน 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 สำหรับจัดการ:

ความต้องการระบบ

ติดตั้ง 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
Note: NPM ไม่ต้องการ database ภายนอก — ใช้ SQLite เก็บข้อมูลใน ./data อย่างไรก็ตามถ้าต้องการ HA หรือ multi-node ควรเปลี่ยนเป็น MySQL/MariaDB โดยเพิ่ม environment variable ใน compose file

เข้าใช้งาน Admin UI ครั้งแรก

เปิดเบราว์เซอร์ไปที่ http://YOUR-VPS-IP:81 แล้ว login ด้วย default credentials:

ระบบจะบังคับให้เปลี่ยน email และ password ทันทีหลัง login ครั้งแรก อย่าตั้ง password อ่อนแอ เพราะ Admin UI มีสิทธิ์ควบคุม traffic ทั้งหมดของ server

เพิ่ม Proxy Host (Forward Domain → Service)

ตัวอย่าง: forward app.example.com ไปยัง Portainer ที่รันบน localhost:9000

  1. ไปที่ Proxy Hosts → Add Proxy Host
  2. กรอก Domain Names: app.example.com
  3. Forward Hostname/IP: 172.17.0.1 (Docker host IP) หรือ host.docker.internal
  4. Forward Port: 9000
  5. เปิด Block Common Exploits
  6. ไปแท็บ SSL → Request a new SSL Certificate → เปิด Force SSL และ HTTP/2 Support
  7. กรอก Email สำหรับ Let's Encrypt → Save
Tip: ถ้า service รันใน Docker container เดียวกัน ใช้ชื่อ service แทน IP เช่น 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:

  1. ไปที่ SSL Certificates → Add SSL Certificate → Let's Encrypt
  2. กรอก Domain: *.example.com และ example.com
  3. เปิด Use a DNS Challenge → เลือก Cloudflare
  4. กรอก Cloudflare API Token (ต้องมีสิทธิ์ Zone:DNS:Edit)
  5. Save — NPM จะ verify ผ่าน DNS TXT record และออก cert ให้อัตโนมัติ

ตั้งค่า Access List ป้องกัน Service

ถ้ามี service ที่ไม่ต้องการให้ทุกคนเข้าถึง เช่น Grafana dashboard หรือ admin panel:

  1. ไปที่ Access Lists → Add Access List
  2. ตั้งชื่อ เช่น internal-only
  3. แท็บ Authorization — เพิ่ม username/password สำหรับ Basic Auth
  4. แท็บ Access — ระบุ IP ที่อนุญาต (เช่น 192.168.1.0/24) หรือปล่อยว่างถ้าใช้ Basic Auth อย่างเดียว
  5. ไปที่ 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'
Tip: ถ้า SSL ออกไม่ได้ ให้ตรวจสอบว่า port 80 ไม่ถูก process อื่นใช้งานอยู่ เพราะ Let's Encrypt HTTP-01 challenge ต้องการ port 80 เปิดและเข้าถึงได้จากภายนอก

แก้ปัญหาที่พบบ่อย

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 ทันที