Skip to content

Host Multiple Docker Projects behind a Shared Nginx Proxy

This guide explains how to configure a single Debian VPS to run multiple independent Docker Compose projects, each accessible via its own domain name (e.g., https://project1.com and https://project2.com) using a single shared Nginx reverse proxy and automated Let's Encrypt SSL certificates.

Prerequisites

Directory Structure

To decouple the global proxy setup from the application repositories, organize your server's home directory as follows:

/home/debian/
├── nginx/           # Global Nginx reverse proxy and Let's Encrypt companion
├── project1/        # First application stack (e.g., django, postgres)
└── project2/        # Second application stack

Step 1: Create a Shared Docker Network

Create a shared Docker network named webproxy that acts as the bridge between the global proxy container and your individual projects.

docker network create webproxy

Why an external network?

Creating an external network allows independent docker-compose.yml stacks to communicate with each other securely without exposing internal ports to the host machine.


Step 2: Configure the Global Nginx Proxy

  1. Create the nginx directory and navigate into it:
mkdir -p /home/debian/nginx && cd /home/debian/nginx
  1. Create a docker-compose.yml file: ??? details "Click to expand docker-compose.yml for nginx-proxy"

    services:
      nginx-proxy:
        image: nginxproxy/nginx-proxy
        container_name: nginx-proxy
        restart: always
        ports:
          - "80:80"
          - "443:443"
        volumes:
          - certs:/etc/nginx/certs:ro
          - vhost:/etc/nginx/vhost.d
          - html:/usr/share/nginx/html
          - /var/run/docker.sock:/tmp/docker.sock:ro
        networks:
          - webproxy
    
      acme-companion:
        image: nginxproxy/acme-companion
        container_name: nginx-proxy-acme
        restart: always
        environment:
          - DEFAULT_EMAIL=your-default-email@example.com
        volumes:
          - certs:/etc/nginx/certs:rw
          - vhost:/etc/nginx/vhost.d:rw
          - html:/usr/share/nginx/html:rw
          - acme:/etc/acme.sh
          - /var/run/docker.sock:/var/run/docker.sock:ro
        depends_on:
          - nginx-proxy
        networks:
          - webproxy
    
    volumes:
      certs:
      vhost:
      html:
      acme:
    
    networks:
      webproxy:
        external: true
    

  2. Spin up the global proxy stack:

docker compose up -d

Step 3: Configure Your Applications

For each project (e.g., project1 and project2), configure the application service to connect to the external webproxy network and specify the proxy routing variables.

  1. Navigate to your project directory (e.g., /home/debian/project1).
  2. Create a .env file containing your notification email:
LETSENCRYPT_EMAIL=your-email@example.com
  1. Configure your application's docker-compose.yml:
services:
  django-app:
    build:
      context: ./django-app
      dockerfile: Dockerfile.prod
    # Note: Do NOT expose host ports 80/443 here. The proxy container handles external traffic.
    environment:
      - VIRTUAL_HOST=project1.com
      - VIRTUAL_PORT=8000
      - LETSENCRYPT_HOST=project1.com
      - LETSENCRYPT_EMAIL=${LETSENCRYPT_EMAIL}
    depends_on:
      db:
        condition: service_healthy
    networks:
      - default    # For communication with local database / redis
      - webproxy   # For communication with the shared Nginx proxy

  db:
    image: postgres:16-alpine
    # Local service configurations...

networks:
  webproxy:
    external: true
  1. Spin up the application stack:
docker compose up -d

Verification

Once both stacks are running:

  • nginx-proxy detects the new containers on the webproxy network.
  • acme-companion requests SSL certificates for the configured LETSENCRYPT_HOST domains.
  • Access https://project1.com and https://project2.com in your browser. Both should display their respective application interfaces with active SSL lock icons.

Auto-renewal

The Let's Encrypt companion will automatically renew certificates 30 days before they expire.