Technology Encyclopedia Home >How to Install Nginx Proxy Manager on a VPS — GUI-Based Reverse Proxy with HTTPS

How to Install Nginx Proxy Manager on a VPS — GUI-Based Reverse Proxy with HTTPS

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.


Table of Contents

  1. NPM vs Manual Nginx Configuration
  2. Prerequisites
  3. Part 1 — Server Setup
  4. Part 2 — Deploy NPM with Docker Compose
  5. Part 3 — Initial Setup
  6. Part 4 — Add Your First Proxy Host
  7. Part 5 — Enable HTTPS for a Proxy Host
  8. Part 6 — Common Use Cases
  9. Part 7 — Access Lists (IP Restriction and Auth)
  10. Part 8 — Streams (TCP/UDP Proxying)
  11. The Gotcha: NPM and System Nginx Conflict
  12. NPM Admin Reference

  • Key Takeaways
  • Use the appropriate Lighthouse application image to skip manual installation steps where available
  • Lighthouse snapshots provide one-click full-server backup before major changes
  • OrcaTerm browser terminal lets you manage the server from any device
  • CBS cloud disk expansion handles growing storage needs without server migration
  • Console-level firewall + UFW = two independent protection layers

NPM vs Manual Nginx Configuration {#npm-vs-nginx}

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

Prerequisites {#prerequisites}

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).


Part 1 — Server Setup {#part-1}

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

Part 2 — Deploy NPM with Docker Compose {#part-2}

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...

Part 3 — Initial Setup {#part-3}

Visit http://YOUR_SERVER_IP:81.

Default credentials:

  • Email: admin@example.com
  • Password: changeme

Change both immediately after first login.

  1. Click your email → Profile → update display name and email
  2. Click Change Password → set a strong password

Part 4 — Add Your First Proxy Host {#part-4}

A proxy host routes a domain name to a backend service.

  1. Click Proxy Hosts → Add Proxy Host

  2. Fill in the Details tab:

    • Domain Names: app.yourdomain.com
    • Scheme: http
    • Forward Hostname/IP: YOUR_SERVER_IP (or container name if on same Docker network)
    • Forward Port: 3000 (port your app listens on)
    • Block Common Exploits: ✓ Enable
    • Websockets Support: Enable if your app uses WebSockets
  3. Click Save

NPM automatically creates an Nginx config and reloads. Your app is now accessible at http://app.yourdomain.com.


Part 5 — Enable HTTPS for a Proxy Host {#part-5}

  1. Click the menu on your proxy host → Edit

  2. Go to the SSL tab:

    • SSL Certificate: Request a new SSL Certificate
    • Domain Names: auto-filled from your proxy host
    • Force SSL: ✓ Enable (redirects HTTP to HTTPS)
    • HTTP/2 Support: ✓ Enable
    • HSTS Enabled: ✓ Enable (recommended)
    • Email address: your email (for Let's Encrypt)
    • I Agree to the Let's Encrypt Terms of Service: ✓
  3. 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.


Part 6 — Common Use Cases {#part-6}

Route multiple apps

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.

Docker container names as hostnames

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).

Custom Nginx config snippets

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";
}

Part 7 — Access Lists (IP Restriction and Auth) {#part-7}

Protect services that shouldn't be publicly accessible:

  1. Access Lists → Add Access List
  2. Give it a name (e.g., "Internal Only")
  3. In Authorization: add username/password for HTTP Basic Auth
  4. In Access: add allowed IP ranges

Assign to a proxy host:

  • Edit proxy host → Details tab → Access List → select your list

Part 8 — Streams (TCP/UDP Proxying) {#part-8}

NPM can proxy non-HTTP TCP/UDP traffic — useful for game servers, databases, or custom protocols:

  1. Streams → Add Stream
  2. Incoming Port: e.g., 25565
  3. Forward Host: 127.0.0.1
  4. Forward Port: 25565
  5. TCP: ✓ Enable

This proxies Minecraft server traffic, for example.


The Gotcha: NPM and System Nginx Conflict {#gotcha}

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.


NPM Admin Reference {#reference}

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

Troubleshooting {#troubleshooting}

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

Frequently Asked Questions {#faq}

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.

Can I expose services on non-standard ports?
Yes — Nginx Proxy Manager supports TCP/UDP port forwarding on any port. Open the corresponding ports in the Lighthouse console firewall and UFW.

Simplify your proxy management today:
👉 Tencent Cloud Lighthouse — Docker-ready Ubuntu VPS
👉 View current pricing and promotions
👉 Explore all active deals and offers