Create a Docker Reverse Proxy Lab with Nginx Proxy Manager and TLS
Deploy Nginx Proxy Manager with persistent data, route one disposable backend through a real hostname, and validate HTTP-to-HTTPS behavior plus certificate issuance without exposing the admin interface unnecessarily.
Expected Outcome
A working Nginx Proxy Manager Lab that forwards a hostname to a containerized test service, terminates TLS with a validated certificate, and preserves proxy/certificate state outside the application container.
Assumptions
A Linux host with Docker Engine and Docker Compose v2.
A DNS name you control that can resolve to the Lab proxy host.
Network/firewall access appropriate to the certificate-validation method you choose; HTTP validation normally requires inbound port 80 and HTTPS service requires port 443.
This Lab uses a disposable backend so proxy changes do not affect a real application.
Bill of Materials
A reviewed Nginx Proxy Manager image tag.
A reviewed Nginx image tag for the disposable backend.
Persistent directories for Nginx Proxy Manager `/data` and `/etc/letsencrypt`.
Build Steps
- Verify Compose v2 and create the Lab directory
Use the current Docker Compose plugin rather than the retired standalone docker-compose executable.
Changes system state: review before running
docker --version docker compose version mkdir -p ~/npm-proxy-lab/{data,letsencrypt} && cd ~/npm-proxy-lab - Define Nginx Proxy Manager and a disposable backend
Follow the project's current persistent-volume layout. Bind the admin UI to loopback so port 81 is not automatically exposed to the LAN; use an SSH tunnel or local browser when administration is needed. Replace image placeholders before deployment.
Read-only command: verify target and scope
cat > compose.yaml <<'EOF' services: npm: image: docker.io/jc21/nginx-proxy-manager:<reviewed-tag> restart: unless-stopped ports: - "80:80" - "443:443" - "127.0.0.1:81:81" volumes: - ./data:/data - ./letsencrypt:/etc/letsencrypt test-app: image: nginx:<reviewed-tag> restart: unless-stopped EOF docker compose config --quiet - Start the stack and validate local administration
Start both containers and wait for Nginx Proxy Manager's first-run initialization. If administering remotely, create an SSH tunnel such as ssh -L 8181:127.0.0.1:81 <host> and browse to http://127.0.0.1:8181.
Changes system state: review before running
docker compose up -d docker compose ps docker compose logs --tail=100 npm curl -I http://127.0.0.1:81/
- Complete first-run administration
Use the initial Nginx Proxy Manager setup flow and immediately replace bootstrap/default account details with credentials unique to the Lab. Do not treat example credentials from documentation or old tutorials as durable secrets.
- Create the proxy host
In Nginx Proxy Manager, add a Proxy Host for your Lab hostname and forward it to scheme http, host test-app, port 80. Because both containers share the Compose network, the proxy can resolve test-app by service name without publishing the backend port on the host.
- Validate plain HTTP routing before requesting a certificate
Confirm DNS resolves to the intended proxy host and an HTTP request reaches the disposable backend. This separates routing/DNS failures from ACME certificate failures.
Manual or UI step
getent hosts <lab-hostname>
Read-only command: verify target and scope
curl -I http://<lab-hostname>/
- Request and apply TLS
Use Nginx Proxy Manager's SSL tab to request a Let's Encrypt certificate for the exact hostname and enable the HTTPS options appropriate to the Lab after issuance succeeds. Certificate issuance requires the selected ACME challenge to be satisfiable; an internal-only name that cannot be validated publicly may need a DNS challenge or an internal/custom certificate instead of HTTP validation.
- Validate the HTTPS path and redirect behavior
Verify the certificate presented for the hostname and confirm the proxy still reaches the disposable backend over HTTPS. If you enabled Force SSL, confirm HTTP redirects rather than simply failing.
Changes system state: review before running
curl -I https://<lab-hostname>/ curl -I http://<lab-hostname>/ openssl s_client -connect <lab-hostname>:443 -servername <lab-hostname> </dev/null 2>/dev/null | openssl x509 -noout -subject -issuer -dates
- Record the recovery boundary
Nginx Proxy Manager stores application state in /data and certificate material in /etc/letsencrypt. Back up both together before upgrades or host replacement; a Compose file alone is not a recovery plan.
Validation
Docker Compose v2 starts Nginx Proxy Manager and the disposable backend without publishing the backend directly.
The administration interface is bound only to loopback on the host unless you deliberately chose a different management boundary.
The Lab hostname resolves correctly and HTTP routing reaches `test-app` through Nginx Proxy Manager.
HTTPS presents a certificate valid for the Lab hostname and reaches the same backend.
If Force SSL is enabled, HTTP requests redirect to HTTPS.
The operator can identify both persistent paths that must be backed up for recovery.
Troubleshooting
If the proxy host returns 502/504, confirm the backend container is healthy and reachable by service name from the Nginx Proxy Manager container before changing TLS settings.
Read-only command: verify target and scope
docker compose ps docker compose exec -T npm getent hosts test-app docker compose logs --tail=120 npm test-app
If certificate issuance fails, verify DNS and the selected ACME challenge path. Do not disable certificate validation or substitute a self-signed certificate while claiming public Let's Encrypt validation succeeded.
If the admin UI is unreachable remotely, remember that this Lab intentionally binds port 81 to loopback; use the documented SSH tunnel or change the management exposure deliberately after a security review.
Cleanup or Rollback
Remove the disposable Proxy Host and test certificate through Nginx Proxy Manager if they are no longer needed.
Stop the Lab with `docker compose down`; preserve `data` and `letsencrypt` unless you intentionally want to erase proxy configuration and certificates.
Remove any temporary router/firewall exposure you created solely for this Lab after validation is complete.
Next Improvements
Place the Nginx Proxy Manager admin interface behind a dedicated management network or authenticated access path before broader use.
Back up and restore-test the `data` and `letsencrypt` paths before upgrading Nginx Proxy Manager.
Add one non-critical real service only after DNS, certificate, backend routing, and rollback behavior are understood.
