Nginx Reverse Proxy and SSL
Brief description: Install Nginx, configure reverse proxy for all services, and obtain SSL certificates.
What You'll Learn
- How to install Nginx and Certbot
- How to configure port forwarding on Proxmox host
- How to configure reverse proxy for backend services
- How to obtain and configure SSL certificates
Prerequisites
- [ ] VM-103 (nginx-proxy) created with IP 192.168.192.20
- [ ] All backend VMs installed (Rocket.Chat, Nextcloud, etc.)
- [ ] Domain names pointing to your server IP
- [ ] Ports 80 and 443 forwarded to nginx-proxy VM
Estimated Time
1-2 hours
Step-by-Step Instructions
Step 1: Install Nginx and Certbot
SSH to nginx-proxy VM:
Update and install:
sudo apt update && sudo apt upgrade -y
sudo apt install nginx certbot python3-certbot-nginx -y
sudo systemctl enable --now nginx
Verify:
Test:
Should show "Welcome to nginx!"
Step 2: Port Forwarding on Proxmox Host
On the Proxmox host (SSH in), set up port forwarding so internet traffic reaches the nginx-proxy VM:
# Forward public 80 → Nginx VM port 80
iptables -t nat -A PREROUTING -i vmbr0 -p tcp --dport 80 -j DNAT --to-destination 192.168.192.20:80
iptables -t nat -A PREROUTING -i vmbr0 -p tcp --dport 443 -j DNAT --to-destination 192.168.192.20:443
# Allow the forwarded traffic
iptables -A FORWARD -p tcp -d 192.168.192.20 --dport 80 -j ACCEPT
iptables -A FORWARD -p tcp -d 192.168.192.20 --dport 443 -j ACCEPT
Make persistent (create script):
Add the 4 iptables lines above, save, then:
Test from remote computer:
Should show the default Nginx welcome page.
Step 3: DNS Setup
You need real domains/subdomains pointed to your server IP. Examples:
| Domain | A Record |
|---|---|
| nextcloud.yourdomain.com | YOUR_PUBLIC_IP |
| chat.yourdomain.com | YOUR_PUBLIC_IP |
| auth.yourdomain.com | YOUR_PUBLIC_IP |
| docs.yourdomain.com | YOUR_PUBLIC_IP |
DNS Required for Let's Encrypt
Without domains pointing to your server, Let's Encrypt won't work (HTTP-01 challenge needs public port 80 open and domain resolving correctly).
Use dig or online DNS checker to verify DNS propagation.
Step 4: Obtain Let's Encrypt Certificates
Run Certbot to get certificates:
Follow prompts: - Enter email (for renewal reminders) - Agree to terms of service - Choose redirect (recommended: yes → auto-redirect HTTP to HTTPS)
Certbot will create/update /etc/nginx/sites-available/ with SSL configuration.
Step 5: Configure Nginx Server Blocks
Certbot creates basic configs, but you may need to refine them for proper proxying.
Nextcloud Configuration
Edit or create:
server {
listen 80;
server_name nextcloud.yourdomain.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name nextcloud.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/nextcloud.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/nextcloud.yourdomain.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
client_max_body_size 10G;
client_body_timeout 300s;
# Proxy to Nextcloud VM (snap uses port 80 internally)
location / {
proxy_pass http://192.168.192.102:80;
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_set_header X-Forwarded-Host $host;
proxy_read_timeout 3600s;
proxy_redirect off;
}
# Handle .well-known for CalDAV/CardDAV
location /.well-known/carddav { return 301 $scheme://$host/remote.php/dav; }
location /.well-known/caldav { return 301 $scheme://$host/remote.php/dav; }
location ^~ /.well-known { return 301 $scheme://$host/index.php$uri; }
}
Rocket.Chat Configuration
server {
listen 80;
server_name chat.yourdomain.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name chat.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/chat.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/chat.yourdomain.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
# Rocket.Chat runs on port 3000 by default
location / {
proxy_pass http://192.168.192.101: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_read_timeout 86400;
proxy_redirect off;
}
}
Keycloak Configuration (if using)
server {
listen 80;
server_name auth.yourdomain.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name auth.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/auth.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/auth.yourdomain.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
location / {
proxy_pass http://192.168.192.106:8080;
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;
}
}
MkDocs Configuration (if using)
server {
listen 80;
server_name docs.yourdomain.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name docs.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/docs.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/docs.yourdomain.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
location / {
proxy_pass http://192.168.192.104:8000;
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;
}
}
Step 6: Enable Configs and Test
sudo ln -s /etc/nginx/sites-available/nextcloud.yourdomain.com /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/chat.yourdomain.com /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/auth.yourdomain.com /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/docs.yourdomain.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Step 7: Configure Backend Firewall (if UFW enabled)
On each backend VM, allow traffic from nginx-proxy:
# On Rocket.Chat VM
sudo ufw allow from 192.168.192.20 to any port 3000
# On Nextcloud VM
sudo ufw allow from 192.168.192.20 to any port 80
# On Keycloak VM
sudo ufw allow from 192.168.192.20 to any port 8080
# On MkDocs VM
sudo ufw allow from 192.168.192.20 to any port 8000
Verification
- [ ] Nginx installed and running
- [ ] Port forwarding working (curl public IP shows nginx page)
- [ ] DNS pointing to server IP
- [ ] SSL certificates obtained for all domains
- [ ] All server blocks enabled
- [ ] HTTPS access working for all services