If you’re like me, you probably have a dozen ‘side projects’—some are just landing pages, some are Next.js apps, and others are simple Python scripts. Paying for a separate VPS or hosting plan for each one is a waste of money. The most efficient solution is hosting multiple sites on one VPS using Docker.
In my experience, the biggest hurdle for beginners isn’t the Docker part; it’s the networking. How does the server know which request goes to which container when everything is hitting the same IP address? That’s where the ‘Reverse Proxy’ comes in. In this guide, I’ll walk you through the core concepts and a practical setup to get your sites live.
Core Concepts: How it Actually Works
Before we touch the terminal, you need to understand the architectural flow. When you host multiple sites on one machine, you can’t simply map every container to port 80 (the standard HTTP port) because only one application can listen to a port at a time.
The Reverse Proxy
Think of a reverse proxy as a traffic cop. It sits at the edge of your server, listens on port 80 and 443, and reads the Host header of the incoming request. If the request is for blog.yourdomain.com, the proxy sends it to Container A. If it’s for app.yourdomain.com, it goes to Container B.
Docker Networks
To make this communication seamless, we use a shared Docker network. Instead of exposing every container’s port to the public internet (which is a security risk), we keep the application containers ‘private’ and only let the reverse proxy talk to them over the internal Docker network.
Getting Started: The Prerequisites
Before we deploy, make sure you have the following ready:
- A VPS (I personally recommend deploying on Hetzner for the best price-to-performance ratio).
- A domain name with DNS access (Cloudflare is my go-to).
- Docker and Docker Compose installed on your server.
Your First Multi-Site Project: Step-by-Step
I recommend using Nginx Proxy Manager (NPM) for beginners. It provides a clean UI to manage your SSL certificates and routing without editing complex .conf files.
Step 1: Create a Shared Network
First, create a network that both the proxy and your websites will join:
docker network create nginx-proxy-net
Step 2: Deploy Nginx Proxy Manager
Create a docker-compose.yml file for your proxy:
version: '3.8'
services:
app:
image: 'jc21/nginx-proxy-manager:latest'
restart: unless-stopped
ports:
- '80:80'
- '81:81'
- '443:443'
volumes:
- ./data:/data
- ./letsencrypt:/etc/letsencrypt
networks:
- nginx-proxy-net
networks:
nginx-proxy-net:
external: true
Step 3: Deploy Your Websites
Now, let’s deploy two different sites. For each site, create a separate folder and a docker-compose.yml. Notice that we do not map ports to the host machine; we only connect to the shared network.
Site 1 (Portfolio):
services:
portfolio:
image: nginx:alpine
networks:
- nginx-proxy-net
networks:
nginx-proxy-net:
external: true
Site 2 (Blog):
services:
blog:
image: ghost:latest
networks:
- nginx-proxy-net
networks:
nginx-proxy-net:
external: true
As shown in the architecture diagram at the top of this guide, the request now flows: Internet → Nginx Proxy Manager → Internal Docker Network → Your Specific Container.
Step 4: Configure Routing in the UI
Log into your NPM dashboard (port 81), go to ‘Proxy Hosts’ → ‘Add Proxy Host’, and enter your domain. For the ‘Forward Hostname’, use the Docker service name (e.g., portfolio or blog) instead of an IP address. Docker’s internal DNS handles the rest.
Common Mistakes to Avoid
- Exposing All Ports: Don’t map ports like
8080:80for every site. This opens unnecessary holes in your firewall. Let the proxy handle all external traffic. - Hardcoding IPs: Never use the internal Docker IP (like 172.18.0.x) in your proxy settings. These IPs change every time a container restarts. Always use the service name.
- Forgetting Volume Backups: If you lose the
/datafolder of your proxy, you lose all your SSL certificates and routing rules. Back these up!
Learning Path: Scaling Your Setup
Once you’ve mastered the basics of hosting multiple sites on one VPS using Docker, you’ll likely find that managing YAML files manually becomes tedious. I suggest exploring these paths:
- PaaS Alternatives: If you want a “Heroku-like” experience on your own VPS, check out my Coolify vs Dokku comparison to see which automation tool fits your workflow.
- Custom Registries: As you build more images, you’ll want a place to store them. Learn how to host a private docker registry to speed up deployments.
- CI/CD: Integrate GitHub Actions to automatically trigger a
docker compose pull && docker compose up -dwhenever you push code.
Recommended Tools
| Tool | Purpose | Why I Use It |
|---|---|---|
| Nginx Proxy Manager | Reverse Proxy | GUI for SSL and Routing |
| Portainer | Container Mgmt | Visualizing container health |
| Watchtower | Auto-updates | Keeps images updated automatically |
Ready to take control of your infrastructure? Start by setting up your first VPS and deploying a simple reverse proxy today.