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 containersNginx 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 versionFirewall cơ bản với UFW:
sudo ufw allow OpenSSH
sudo ufw allow 80
sudo ufw allow 443
sudo ufw enableCài Nginx Proxy Manager bằng Docker Compose
Tạo thư mục:
mkdir -p ~/apps/npm
cd ~/apps/npmTạ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 -dTruy cập:
http://YOUR_SERVER_IP:81Tà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: port81 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 lsBạ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/whoamidocker-compose.yml:
services:
whoami:
image: traefik/whoami
container_name: whoami
restart: unless-stopped
networks:
- proxy
networks:
proxy:
external: true
Chạy:
docker compose up -dContainer này lắng nghe port 80 nội bộ.
Vào NPM:
1. Hosts → Proxy 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.comTriể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:3000Ví 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:3001Pattern 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_IPNếu nhiều app, dùng wildcard:
A *.example.com YOUR_VPS_IPSau đó 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.ymlBackup đơn giản:
tar -czf apps-backup.tar.gz ~/appsThự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.comCompose:
services:
app:
image: example/app
environment:
APP_URL: ${APP_URL}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}Quyền file:
chmod 600 .envKhô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 -fVới NPM:
cd ~/apps/npm
docker compose pull
docker compose up -dKhô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_nameHoặc:
docker compose logs -fNhữ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_containerNguyê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 80 và 443.
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 apache2Bả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.
Bình luận (0)
Chưa có bình luận. Hãy là người đầu tiên!