DEV Community

QNAP Container Station: Free Up Ports 80 & 443 for Docker Reverse Proxy

📖 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 (or 80) to 8088.
  • Enable secure connection (HTTPS): Change from 443 (or 4433) to 8443.
  • Uncheck: "Redirect HTTP connection to HTTPS" if bound to port 80.

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 8085 and 8445).
  • 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: a****@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 port 80.
  • 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.

Read on DEV Community ↗ ← Back to News

Comments

No comments yet. Start the discussion.