Virtualization and ContainersDocker StacksIntermediate60-120 minutesLab

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.

Last reviewed8/19/2026
reverse proxy stacksTLS validationhomelab CI and rollback
Docker Compose v2Nginx Proxy ManagerNginx test serviceLet's EncryptTLS

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

  1. 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
  2. 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
  3. 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/
  4. 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.

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

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

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

References

Keep Moving

Build on what you just completed

Continue with a related Lab or return to this build path for a different implementation.