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
- A Debian server with Docker and the Docker Compose plugin installed. (See Install-docker-and-docker-compose-on-debian-server-using-a-script)
- Domain names with DNS
Arecords pointing to your server's public IP address. - A user with docker privileges.
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.
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
- Create the
nginxdirectory and navigate into it:
-
Create a
docker-compose.ymlfile: ??? 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 -
Spin up the global proxy stack:
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.
- Navigate to your project directory (e.g.,
/home/debian/project1). - Create a
.envfile containing your notification email:
- 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
- Spin up the application stack:
Verification
Once both stacks are running:
-
nginx-proxydetects the new containers on thewebproxynetwork. -
acme-companionrequests SSL certificates for the configuredLETSENCRYPT_HOSTdomains. - Access
https://project1.comandhttps://project2.comin 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.