📖 Original Reference: Originally published with complete step-by-step storage configurations at alexmaina.dev/qnap-docker-reverse-proxy.
When trying to deploy a Reverse Proxy (like Nginx Proxy Manager, Traefik, or Caddy) inside QNAP's Container Station, Docker will fail with:
Error response from daemon: driver failed programming external connectivity on endpoint: bind: address already in use This happens because QNAP's operating system (QTS or QuTS hero) occupies port 80 and port 443 by default for its administrative interface and built-in web server.
Here is how to liberate both ports cleanly and set up a centralized reverse proxy without breaking access to your NAS.
Step 1: Move QTS Admin System Ports
- Log into your QTS web dashboard.
- Go to Control Panel → System → General Settings.
- Under the System Administration section:
- System port (HTTP): Change from
8080(or80) to8088. - Enable secure connection (HTTPS): Change from
443(or4433) to8443. - Uncheck: "Redirect HTTP connection to HTTPS" if bound to port 80.
- System port (HTTP): Change from
- Click Apply. Your browser will reload at
https://NAS-IP:8443.
Step 2: Disable the Built-in Apache Web Server
Even with admin ports moved, QTS runs a background Apache server that listens on port 80.
- In Control Panel, navigate to Applications → Web Server.
- Uncheck Enable Web Server (or change its listening ports to
8085and8445). - Click Apply.
Ports 80 and 443 are now completely free on the physical network interface!
Step 3: Deploy Nginx Proxy Manager with Docker Compose
Open Container Station → Applications → Create, and paste this clean docker-compose.yml:
version: '3.8' services: npm-app: image: 'jc21/nginx-proxy-manager:latest' container_name: nginx-proxy-manager restart: unless-stopped ports: - '80:80' # Public HTTP port now available - '443:443' # Public HTTPS port now available - '81:81' # NPM Web Admin Dashboard environment: DB_MYSQL_HOST: "npm-db" DB_MYSQL_PORT: 3306 DB_MYSQL_USER: "npm" DB_MYSQL_PASSWORD: "SuperSecureDBPassword2026!" DB_MYSQL_NAME: "npm" volumes: - /share/Container/npm/data:/data - /share/Container/npm/letsencrypt:/etc/letsencrypt depends_on: - npm-db networks: - homelab-net npm-db: image: 'mariadb:10.11' container_name: npm-database restart: unless-stopped environment: MYSQL_ROOT_PASSWORD: "RootSecretPassword2026!" MYSQL_DATABASE: "npm" MYSQL_USER: "npm" MYSQL_PASSWORD: "SuperSecureDBPassword2026!" volumes: - /share/Container/npm/mysql:/var/lib/mysql networks: - homelab-net networks: homelab-net: name: homelab-net driver: bridge Step 4: Route Container Traffic Internally
With all your containers attached to homelab-net:
- You no longer need to expose ports to the host for individual services (WordPress, Nextcloud, etc.).
- Access NPM dashboard at
http://NAS-IP:81(Default credentials:admin@example.com/changeme). - Add a Proxy Host with your domain name and set Forward Hostname to the internal container name (e.g.
wordpress-app) on port80. - Request a free Let's Encrypt SSL certificate directly through NPM.
Author: Alessandro Mainardi — Software Developer & Computer Engineering Student @ UniBo. Explore homelab architectures at alexmaina.dev.
