Triển khai nhiều app trên VPS dễ dàng với Nginx Proxy Manager

P P T P Chung

Triển khai nhiều ứng dụng trên một VPS bằng Nginx Proxy Manager và Docker Compose

Một VPS nhỏ 2GB RAM hoàn toàn có thể chạy nhiều ứng dụng: blog, API, dashboard nội bộ, n8n, uptime monitor, app staging… Vấn đề không nằm ở “có chạy được không”, mà là quản lý domain, HTTPS, port, container, backup, update sao cho gọn.

Cách phổ biến: mỗi app chạy bằng Docker Compose, còn Nginx Proxy Manager đứng trước làm reverse proxy, cấp SSL Let’s Encrypt, định tuyến domain/subdomain về đúng container.

Ví dụ:

blog.example.com → container WordPress – api.example.com → container Node.js – n8n.example.com → container n8n – status.example.com → container Uptime Kuma

Ưu điểm: ít đụng cấu hình Nginx tay, thêm app nhanh, rollback dễ, tách biệt môi trường tốt.


Kiến trúc tổng quan

Mô hình đơn giản:

Internet
   ↓
Domain DNS
   ↓
VPS public IP
   ↓
Nginx Proxy Manager :80/:443
   ↓
Docker network
   ↓
App containers

Nginx Proxy Manager, viết tắt NPM, nhận toàn bộ request HTTP/HTTPS từ ngoài internet. Sau đó nó forward request đến container tương ứng qua Docker network nội bộ.

Điểm quan trọng: chỉ NPM cần mở port 80 và 443 ra internet. Các ứng dụng phía sau không cần public port, trừ khi bạn có lý do đặc biệt.


Chuẩn bị VPS

VPS nên có:

– Ubuntu 22.04/24.04 hoặc Debian ổn định – RAM từ 1GB cho vài app nhẹ, 2GB+ dễ thở hơn – Docker + Docker Compose plugin – Domain trỏ về IP VPS – Firewall mở 80, 443, SSH

Cài Docker nhanh:

curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER

Đăng xuất/đăng nhập lại để user dùng được Docker không cần sudo.

Kiểm tra:

docker version
docker compose version

Firewall cơ bản với UFW:

sudo ufw allow OpenSSH
sudo ufw allow 80
sudo ufw allow 443
sudo ufw enable

Cài Nginx Proxy Manager bằng Docker Compose

Tạo thư mục:

mkdir -p ~/apps/npm
cd ~/apps/npm

Tạo 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

Truy cập:

http://YOUR_SERVER_IP:81

Tài khoản mặc định thường là:

Email: [email protected]
Password: changeme

Đăng nhập xong cần đổi email/password ngay.

Lưu ý bảo mật: port 81 là giao diện admin. Tốt nhất chỉ mở tạm, hoặc giới hạn bằng firewall/VPN. Nếu để public, dùng mật khẩu mạnh, bật thêm lớp bảo vệ nếu có reverse proxy riêng hoặc firewall theo IP.

Tạo Docker network dùng chung

Ở file trên đã tạo network tên proxy. Các app muốn được NPM truy cập cần join vào network này.

Kiểm tra:

docker network ls

Bạn sẽ thấy network proxy.

Nguyên tắc:

– NPM nằm trong network proxy – App cũng nằm trong network proxy – NPM forward đến container_name:port – App không cần expose port ra host


Triển khai app thứ nhất: ví dụ whoami

Tạo app test cực nhẹ:

mkdir -p ~/apps/whoami
cd ~/apps/whoami
docker-compose.yml:
services:
  whoami:
    image: traefik/whoami
    container_name: whoami
    restart: unless-stopped
    networks:
      - proxy

networks: proxy: external: true

Chạy:

docker compose up -d

Container này lắng nghe port 80 nội bộ.

Vào NPM:

1. HostsProxy Hosts 2. Add Proxy Host 3. Domain Names: whoami.example.com 4. Scheme: http 5. Forward Hostname/IP: whoami 6. Forward Port: 80 7. Tab SSL: – Request a new SSL Certificate – Force SSL – HTTP/2 Support – Agree Let’s Encrypt Terms 8. Save

DNS whoami.example.com phải trỏ A record về IP VPS.

Nếu đúng, truy cập:

https://whoami.example.com

Triển khai nhiều ứng dụng

Mỗi app nên có thư mục riêng:

~/apps/
  npm/
  app1/
  app2/
  n8n/
  uptime-kuma/
  wordpress/

Ví dụ app Node.js:

services:
  api:
    image: node:20-alpine
    container_name: api
    working_dir: /app
    command: sh -c "npm install && npm start"
    restart: unless-stopped
    volumes:
      - ./app:/app
    environment:
      NODE_ENV: production
    networks:
      - proxy

networks: proxy: external: true

Trong NPM:

api.example.com → http://api:3000

Ví dụ Uptime Kuma:

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

networks: proxy: external: true

Trong NPM:

status.example.com → http://uptime-kuma:3001

Pattern giữ nguyên:

– Mỗi app có container_name – Join network proxy – Không map port host nếu không cần – NPM forward bằng tên container


DNS: domain chính, subdomain, wildcard

Cách dễ nhất: mỗi app một subdomain.

Trong DNS provider:

A  example.com          YOUR_VPS_IP
A  blog.example.com     YOUR_VPS_IP
A  api.example.com      YOUR_VPS_IP
A  status.example.com   YOUR_VPS_IP

Nếu nhiều app, dùng wildcard:

A  *.example.com        YOUR_VPS_IP

Sau đó trong NPM vẫn cần tạo từng Proxy Host tương ứng.

Wildcard DNS giúp khỏi tạo record mới liên tục, nhưng không tự động cấu hình proxy.


HTTPS với Let’s Encrypt

NPM giúp xin và gia hạn SSL tự động. Khi tạo Proxy Host, chỉ cần tab SSL:

– Chọn Request a new SSL Certificate – Bật Force SSL – Bật HTTP/2 Support – Nhập email – Agree Terms – Save

Nếu cấp SSL lỗi, kiểm tra:

– Domain đã trỏ đúng IP chưa – Port 80/443 mở chưa – VPS có firewall/cloud firewall chặn không – App proxy host có domain đúng không – Đang dùng AAAA IPv6 sai không

Let’s Encrypt cần truy cập được server qua internet để xác minh domain.


Volume và dữ liệu bền vững

Container có thể xoá tạo lại. Dữ liệu quan trọng phải nằm trong volume hoặc bind mount.

Ví dụ:

volumes:
  - ./data:/app/data

Ưu điểm bind mount dạng ./data:

– Dễ backup – Dễ kiểm tra – Dễ migrate sang VPS khác

Cần backup tối thiểu:

~/apps/npm/data
~/apps/npm/letsencrypt
~/apps/*/data
.env files
docker-compose.yml

Backup đơn giản:

tar -czf apps-backup.tar.gz ~/apps

Thực tế nên backup ra máy khác hoặc object storage. Backup nằm cùng VPS không cứu được khi VPS hỏng.


Quản lý biến môi trường bằng .env

Không hard-code secret trong compose nếu có thể.

Ví dụ .env:

POSTGRES_PASSWORD=change_me
APP_URL=https://app.example.com

Compose:

services:
  app:
    image: example/app
    environment:
      APP_URL: ${APP_URL}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}

Quyền file:

chmod 600 .env

Không commit .env lên Git.


Cập nhật ứng dụng

Với image Docker:

cd ~/apps/app-name
docker compose pull
docker compose up -d
docker image prune -f

Với NPM:

cd ~/apps/npm
docker compose pull
docker compose up -d

Không update mù với app có database quan trọng. Trước khi update:

1. Đọc release notes nếu app lớn 2. Backup volume/database 3. Update 4. Kiểm tra log

Xem log:

docker logs -f container_name

Hoặc:

docker compose logs -f

Những lỗi thường gặp

502 Bad Gateway

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

Kiểm tra:

docker ps
docker network inspect proxy
docker logs app_container

Nguyên nhân hay gặp:

– Sai Forward Hostname – Sai port nội bộ – App chưa join network proxy – Container crash – App chỉ listen 127.0.0.1 thay vì 0.0.0.0

SSL không cấp được

Thường do DNS/firewall.

Kiểm tra:

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

Đảm bảo IP trả về đúng VPS.

Port bị chiếm

NPM cần port 80443.

Kiểm tra:

sudo ss -tulpn | grep ':80|:443'

Nếu Apache/Nginx host đang chạy, dừng nó:

sudo systemctl disable --now nginx apache2

Bảo mật tối thiểu

Không cần phức tạp, nhưng đừng bỏ qua nền tảng:

– SSH dùng key, tắt password login nếu có thể – Đổi port admin NPM hoặc chặn 81 bằng firewall – Chỉ public 80/443 – Dùng mật khẩu mạnh cho mọi dashboard – Backup định kỳ – Update OS và container – Không để database public port – Không chạy container privileged nếu không cần

Nếu app nội bộ, cân nhắc thêm access list trong NPM hoặc đặt sau VPN.


Kết luận

Nginx Proxy Manager + Docker Compose là combo thực dụng để chạy nhiều ứng dụng trên một VPS: dễ triển khai, dễ cấp HTTPS, dễ tách app, dễ backup. Công thức cốt lõi rất ngắn: NPM giữ port 80/443, mỗi app chạy trong Compose riêng, cùng join network proxy, rồi map domain đến container_name:port.

Đừng over-engineer từ đầu. Một VPS, một thư mục ~/apps, một network proxy, backup đều đặn — vậy là đủ cho phần lớn dự án cá nhân, staging, tool nội bộ, hoặc sản phẩm nhỏ. Khi tải tăng, hãy tách database, thêm monitoring, rồi mới nghĩ đến cluster.

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!