August 14, 2026 · by CLIQHOST
Running multiple web applications in Docker containers on the same server is great — until you need to expose them all to the internet. Opening individual ports for each app is messy, insecure, and hard to manage. The industry-standard solution is Nginx as a reverse proxy: a single entry point that accepts all HTTP/HTTPS traffic and routes it to the correct container based on the domain name or URL path.
In this guide, you'll learn how to configure Nginx as a reverse proxy for Docker containers on a NVMe VPS or SSD VPS, step by step, with practical configuration examples and HTTPS setup.
Without a reverse proxy, every Docker container needs to listen on a unique port (e.g. 3001, 3002, 8080). This leads to:
http://yourdomain.com:3001Nginx as a reverse proxy fixes all of this: it receives all traffic on ports 80 and 443, then forwards it internally to the right container — clean, secure, and scalable.
Before you start, make sure you have:
If Nginx isn't installed yet:
sudo apt update
sudo apt install nginx -y
sudo systemctl enable nginx
sudo systemctl start nginx
Verify it's running:
sudo systemctl status nginx
Let's say you have two applications:
Start the containers with ports bound to localhost only:
# App1
docker run -d --name app1 -p 127.0.0.1:3000:3000 myapp1:latest
# App2
docker run -d --name app2 -p 127.0.0.1:5000:5000 myapp2:latest
Important: By binding to
127.0.0.1:PORT, the container port is only reachable from the server itself — not from the outside world. Nginx (also on the server) will forward traffic to it.
Or using Docker Compose (docker-compose.yml):
version: '3.8'
services:
app1:
image: myapp1:latest
ports:
- "127.0.0.1:3000:3000"
restart: unless-stopped
app2:
image: myapp2:latest
ports:
- "127.0.0.1:5000:5000"
restart: unless-stopped
docker compose up -d
Create a separate configuration file for each domain in /etc/nginx/sites-available/.
sudo nano /etc/nginx/sites-available/app1.yourdomain.com
server {
listen 80;
server_name app1.yourdomain.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
}
sudo nano /etc/nginx/sites-available/app2.yourdomain.com
server {
listen 80;
server_name app2.yourdomain.com;
location / {
proxy_pass http://127.0.0.1:5000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Enable both configurations:
sudo ln -s /etc/nginx/sites-available/app1.yourdomain.com /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/app2.yourdomain.com /etc/nginx/sites-enabled/
Test and reload Nginx:
sudo nginx -t
sudo systemctl reload nginx
Install Certbot and obtain free SSL certificates:
sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d app1.yourdomain.com -d app2.yourdomain.com
Certbot will automatically update your Nginx files to add SSL blocks and redirect HTTP → HTTPS.
After completion, the config for app1.yourdomain.com will look like:
server {
listen 443 ssl;
server_name app1.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/app1.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/app1.yourdomain.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
server {
listen 80;
server_name app1.yourdomain.com;
return 301 https://$host$request_uri;
}
For commercial or business projects, explore OV/EV SSL certificates from CLIQHOST for enhanced trust and validation.
A cleaner, more production-ready setup is to run Nginx inside a Docker container and communicate with other containers via an internal Docker network — no ports exposed on the host.
version: '3.8'
networks:
webnet:
driver: bridge
services:
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d
- ./certbot/www:/var/www/certbot
- ./certbot/conf:/etc/letsencrypt
networks:
- webnet
restart: unless-stopped
app1:
image: myapp1:latest
expose:
- "3000"
networks:
- webnet
restart: unless-stopped
app2:
image: myapp2:latest
expose:
- "5000"
networks:
- webnet
restart: unless-stopped
In this setup, proxy_pass in Nginx uses the service name instead of 127.0.0.1:
location / {
proxy_pass http://app1:3000;
}
Docker's internal DNS automatically resolves app1 to the container's IP — elegant and secure.
client_max_body_size 20M;
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
gzip on;
gzip_types text/plain application/json application/javascript text/css;
gzip_min_length 1000;
limit_req_zone $binary_remote_addr zone=api:10m rate=30r/m;
location /api/ {
limit_req zone=api burst=10 nodelay;
proxy_pass http://app1:3000;
}
For advanced server hardening and ongoing maintenance, check out CLIQHOST's server management services — professional Linux administration handled for you.
| Problem | Solution |
|---|---|
502 Bad Gateway |
Docker container is not running or wrong port |
404 Not Found |
server_name doesn't match the accessed domain |
SSL_ERROR_RX_RECORD_TOO_LONG |
Nginx listens on 443 but no SSL block configured |
| Nginx won't start | Run sudo nginx -t for detailed error output |
Check Nginx logs:
sudo tail -f /var/log/nginx/error.log
sudo tail -f /var/log/nginx/access.log
Check container status:
docker ps
docker logs app1
Nginx as a reverse proxy for Docker containers is a powerful, flexible combination that lets you run dozens of applications on a single server — all secured with HTTPS, accessible via clean domain names, and isolated from one another.
To get the most out of this setup, you need a server with sufficient resources. Explore NVMe VPS plans or managed dedicated servers from CLIQHOST — reliable infrastructure with professional support included.
Have questions or need a custom configuration? Contact the CLIQHOST team — we're here to help.
Real reviews from customers who trust CLIQHOST for performance, reliability and expert technical support.
"We moved our online shop from a foreign host and the difference is night and day — pages load instantly and support replies in minutes, in Romanian."
"Migrated 12 client sites to CLIQHOST. Free migration, zero downtime, and the cPanel setup is exactly what my team needed. Highly recommend."
"Our NVMe VPS handles traffic spikes without a sweat. Full root, local datacenter, and billing in MDL — everything we wanted from a provider."