Nginx Proxy Manager puts a friendly web UI on top of Nginx: point domain names at your containers and it handles SSL certificates, redirect rules, and access lists.

Prerequisites

  • Docker Engine + Compose plugin (see the Compose guide)
  • A domain name you control, with DNS you can edit — needed for the SSL step

1. Create the project folder

sudo mkdir -p /opt/npm
cd /opt/npm

2. Write the Compose file

services:
  npm:
    image: jc21/nginx-proxy-manager:latest
    container_name: npm
    restart: unless-stopped
    ports:
      # These two MUST be public — the proxy's whole job is answering
      # HTTP/HTTPS requests from the internet
      - "80:80"
      - "443:443"
      # Admin UI bound to localhost only; reach it via SSH tunnel
      - "127.0.0.1:81:81"
    volumes:
      - npm-data:/data
      - npm-etc:/etc/letsencrypt

volumes:
  npm-data:
  npm-etc:
The exception that proves the rule. Unlike most apps, a reverse proxy must listen publicly on 80/443 — that's its job. What you protect is the admin UI (port 81), which stays on localhost behind your SSH tunnel. Keep the admin UI off the public internet, always.

3. Start it and open the admin UI

sudo docker compose up -d

On your own computer, tunnel to the admin interface:

ssh -L 8081:localhost:81 your-user@your-server-ip
# then open http://localhost:8081

The documented default login is admin@example.com with password changeme — the UI forces you to set a new email and password on first login. Do that immediately.

4. Point a domain at your first app

  1. In your DNS provider, create an A record: app.example.com → your server IP. Wait for it to resolve (dig +short app.example.com on your machine shows the IP).
  2. In NPM: Hosts → Proxy Hosts → Add. Domain = app.example.com, forward to your app (for a Compose app on the same server: the container name and port, e.g. uptime-kuma:3001, which works because Compose puts containers on a shared network — add NPM to your app's compose network for this).
  3. On the SSL tab choose "Request a new SSL certificate", enable "Force SSL". NPM completes the Let's Encrypt challenge automatically.

5. Updates and backups

cd /opt/npm
sudo docker compose pull && sudo docker compose up -d

Everything that matters lives in the two volumes (npm-data, npm-etc — hosts, and your certificates). Stop the stack and archive both, or fold them into the automated routine in the backups guide.

Troubleshooting

  • SSL challenge fails: DNS not propagated yet, or port 80 blocked. Verify the record resolves publicly and that your provider's firewall (if any) passes port 80.
  • 502 Bad Gateway to a Compose app: NPM can't reach the container — join it to the same Docker network as the app (a networks: block shared by both compose files) or forward to the host IP instead of the container name.
  • Locked out of the UI: you can reset the admin password by following the official docs' database-reset procedure — another reason the admin volume belongs in your backups.

Official documentation: Nginx Proxy Manager setup guide