I like Nginx config files. I've written enough of them to feel comfortable with the syntax. But when I'm setting up a new service for someone else who needs to manage it, handing them an Nginx config file and saying "edit this" isn't a great experience.
Nginx Proxy Manager solves that. The same reverse proxy setup that normally requires editing config files becomes: enter the domain name, enter the backend address, check "SSL — Let's Encrypt", click Save. Two minutes. It generates and manages the certificates automatically.
The main conflict to watch for: if you already have Nginx or another service on port 80 and 443, NPM needs those ports. This guide covers how to handle that cleanly.
This guide deploys Nginx Proxy Manager on Ubuntu 22.04 using Docker Compose.
I use Nginx Proxy Manager on Tencent Cloud Lighthouse when I'm running multiple apps on one server and want to manage routing without maintaining complex Nginx config files. Tip: use Lighthouse's Docker CE application image when creating your instance — Docker is pre-installed, so you can skip the Docker setup step and get straight to deploying NPM. NPM's automatic Let's Encrypt certificate management pairs well with Lighthouse's console-level firewall — you configure ports 80 and 443 in the Lighthouse console, and NPM handles all the certificate and proxy configuration from its web UI.
- Key Takeaways
| Nginx Proxy Manager | Manual Nginx | |
|---|---|---|
| SSL setup | Two clicks | certbot command + config edit |
| Adding a new app | Fill a form | Edit config, enable site, reload |
| Wildcard certificates | Supported via DNS challenge | Supported |
| Access control | Built-in IP/auth UI | Manual configuration |
| Learning curve | Low | Medium |
| Flexibility | High (but less than manual) | Full |
| Best for | Multiple apps, non-sysadmins | Maximum control |
| Requirement | Notes |
|---|---|
| Cloud server | Tencent Cloud Lighthouse Ubuntu 22.04 |
| Docker + Compose | Installed |
| Ports 80, 443, 81 available | NPM needs 80/443 for proxying, 81 for admin UI |
Important: NPM runs on port 80 and 443 directly. If you have a system Nginx running, you need to stop it first (see Gotcha section).
ssh ubuntu@YOUR_SERVER_IP
sudo apt update && sudo apt upgrade -y
# Install Docker
curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker $USER
newgrp docker
# Open firewall ports
sudo ufw allow ssh
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 81/tcp # NPM admin UI (restrict later)
sudo ufw enable
# Stop system Nginx if running (see Gotcha)
sudo systemctl stop nginx
sudo systemctl disable nginx
mkdir -p ~/apps/npm && cd ~/apps/npm
Create docker-compose.yml:
version: '3.8'
services:
nginx-proxy-manager:
image: jc21/nginx-proxy-manager:latest
container_name: nginx-proxy-manager
restart: unless-stopped
ports:
- "80:80" # HTTP
- "443:443" # HTTPS
- "81:81" # Admin UI
volumes:
- ./data:/data
- ./letsencrypt:/etc/letsencrypt
environment:
# Optional: use an external database instead of SQLite
# DB_MYSQL_HOST: npm-db
# DB_MYSQL_PORT: 3306
# DB_MYSQL_USER: npm
# DB_MYSQL_PASSWORD: npm_password
# DB_MYSQL_NAME: npm
# Optional: external MySQL database (recommended for production)
# npm-db:
# image: mariadb:10.11
# restart: unless-stopped
# environment:
# MYSQL_ROOT_PASSWORD: root_password
# MYSQL_DATABASE: npm
# MYSQL_USER: npm
# MYSQL_PASSWORD: npm_password
# volumes:
# - npm_db:/var/lib/mysql
#
# volumes:
# npm_db:
Start NPM:
docker compose up -d
docker compose logs -f
# Wait for: Nginx Proxy Manager is running...
Visit http://YOUR_SERVER_IP:81.
Default credentials:
admin@example.comchangemeChange both immediately after first login.
A proxy host routes a domain name to a backend service.
Click Proxy Hosts → Add Proxy Host
Fill in the Details tab:
app.yourdomain.comhttpYOUR_SERVER_IP (or container name if on same Docker network)3000 (port your app listens on)Click Save
NPM automatically creates an Nginx config and reloads. Your app is now accessible at http://app.yourdomain.com.
Click the ⋮ menu on your proxy host → Edit
Go to the SSL tab:
Click Save
NPM runs certbot automatically, obtains the certificate, configures HTTPS, and sets up auto-renewal. Takes about 30 seconds.
Your app is now at https://app.yourdomain.com with a valid certificate.
Create separate proxy hosts for each app:
| Domain | Forward to |
|---|---|
blog.yourdomain.com |
127.0.0.1:2368 (Ghost) |
status.yourdomain.com |
127.0.0.1:3001 (Uptime Kuma) |
git.yourdomain.com |
127.0.0.1:3000 (Gitea) |
api.yourdomain.com |
127.0.0.1:1337 (Strapi) |
Each gets its own SSL certificate and HTTPS redirect. All managed from one UI.
When NPM and your apps are on the same Docker network, you can use container names instead of IP addresses:
# Create a shared network
docker network create shared_network
# Add NPM to this network
docker network connect shared_network nginx-proxy-manager
# Add your app to this network
docker network connect shared_network my-app-container
Then in NPM proxy host: Forward Hostname = my-app-container (container name resolves via Docker DNS).
NPM allows adding custom Nginx directives. In the Advanced tab of any proxy host:
# Example: increase upload limit
client_max_body_size 100m;
# Example: cache static assets
location ~* \.(js|css|png|jpg|gif|ico|woff)$ {
expires 30d;
add_header Cache-Control "public";
}
Protect services that shouldn't be publicly accessible:
Assign to a proxy host:
NPM can proxy non-HTTP TCP/UDP traffic — useful for game servers, databases, or custom protocols:
127.0.0.1This proxies Minecraft server traffic, for example.
NPM runs its own Nginx inside the Docker container and binds to ports 80 and 443. If a system Nginx is also running and bound to those ports, NPM will fail to start.
Check if system Nginx is running:
sudo systemctl status nginx
# or
sudo ss -tlnp | grep ':80'
If system Nginx is running:
sudo systemctl stop nginx
sudo systemctl disable nginx
Then start NPM: docker compose up -d
If you need both (e.g., system Nginx for some things and NPM for others), run NPM on different ports (e.g., 8080/8443) and put system Nginx in front of NPM. This is more complex and usually not necessary — just use NPM for everything or system Nginx for everything.
| Section | What you can manage |
|---|---|
| Dashboard | Overview of all proxy hosts, certificates, streams |
| Proxy Hosts | Add/edit/remove HTTP(S) proxy rules |
| Redirection Hosts | Domain redirects (e.g., www → non-www) |
| 404 Hosts | Custom 404 pages for unmatched domains |
| Streams | TCP/UDP port forwarding |
| Access Lists | IP restrictions and HTTP Basic Auth |
| SSL Certificates | Manage Let's Encrypt and custom certs |
| Users | Add admin team members |
| Issue | Likely Cause | Fix |
|---|---|---|
| Connection refused | Service not running or wrong port | Check systemctl status SERVICE and verify firewall rules |
| Permission denied | Wrong file ownership or permissions | Check file ownership with ls -la and use chown/chmod to fix |
| 502 Bad Gateway | Backend service not running | Restart the backend service; check logs with journalctl -u SERVICE |
| SSL certificate error | Certificate expired or domain mismatch | Run sudo certbot renew and verify domain DNS points to server IP |
| Service not starting | Config error or missing dependency | Check logs with journalctl -u SERVICE -n 50 for specific error |
| Out of disk space | Logs or data accumulation | Run df -h to identify usage; clean logs or attach CBS storage |
| High memory usage | Too many processes or memory leak | Check with htop; consider upgrading instance plan if consistently high |
| Firewall blocking traffic | Port not open in UFW or Lighthouse console | Open port in Lighthouse console firewall AND sudo ufw allow PORT |
How does Nginx Proxy Manager differ from a simple SSH tunnel?
SSH tunnels are connection-specific and not persistent without extra tools. Nginx Proxy Manager is a dedicated service that runs continuously, supports multiple protocols, and provides a more robust and manageable solution for ongoing tunnel needs.
Is the tunnel traffic encrypted?
Traffic through the Lighthouse server itself can be encrypted depending on the protocol used. For sensitive applications, use TLS/SSL on top of the tunnel for end-to-end encryption.
How do I make the tunnel persistent after server reboots?
Set up both the server and client components as systemd services with Restart=on-failure. They'll start automatically on boot and recover from crashes.
What is the performance overhead of using Nginx Proxy Manager?
Minimal for most use cases. Latency adds the round-trip time through the relay server. Throughput is limited by the Lighthouse server's bandwidth and network speed.
Simplify your proxy management today:
👉 Tencent Cloud Lighthouse — Docker-ready Ubuntu VPS
👉 View current pricing and promotions
👉 Explore all active deals and offers