Skillversitas

Nginx Reverse Proxy

A reverse proxy receives requests from clients and forwards them to backend servers. The MicroVM acts as a front-end proxy that directs traffic to the backend application.

Architecture

Client (Internet)
       |
    [MicroVM Nginx]
       |
    [Backend App:3000]

Basic Configuration

cat > /etc/nginx/sites-available/proxy.conf << 'PROXY'
upstream backend {
    server 127.0.0.1:3000;
}

server {
    listen 80;
    server_name app.example.com;

    location / {
        proxy_pass http://backend;
        proxy_http_version 1.1;

        # Headers
        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;

        # WebSocket support
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        # Timeout
        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;

        # Buffer
        proxy_buffering on;
        proxy_buffer_size 4k;
        proxy_buffers 8 4k;
    }

    # Serve static files directly from Nginx
    location /static/ {
        alias /var/www/static/;
        expires 30d;
    }

    # Health check
    location /health {
        proxy_pass http://backend/health;
        access_log off;
    }
}
PROXY

ln -s /etc/nginx/sites-available/proxy.conf /etc/nginx/sites-enabled/
nginx -t && nginx -s reload

Reverse Proxy to Node.js

# The Node.js app runs on port 3000
cat > /etc/nginx/sites-available/nodejs.conf << 'NODEJS'
upstream nodeapp {
    server 127.0.0.1:3000;
}

server {
    listen 80;
    server_name api.example.com;

    client_max_body_size 50M;

    location / {
        proxy_pass http://nodeapp;
        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;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }

    location /api/ {
        proxy_pass http://nodeapp/api/;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}
NODEJS

Reverse Proxy to Python (Gunicorn/uvicorn)

cat > /etc/nginx/sites-available/python.conf << 'PYTHON'
upstream pythonapp {
    server 127.0.0.1:8000;
}

server {
    listen 80;
    server_name app.example.com;

    location / {
        proxy_pass http://pythonapp;
        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_connect_timeout 300s;
        proxy_read_timeout 300s;
    }
}
PYTHON

Reverse Proxy to PHP-FPM

cat > /etc/nginx/sites-available/php.conf << 'PHP'
server {
    listen 80;
    server_name php.example.com;
    root /var/www/html;
    index index.php index.html;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        fastcgi_pass unix:/run/php/php8.2-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
        fastcgi_read_timeout 300;
    }

    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
        expires 30d;
        add_header Cache-Control "public, immutable";
    }
}
PHP

Rate Limiting

cat > /etc/nginx/conf.d/ratelimit.conf << 'RATELIMIT'
# Define zone
limit_req_zone $binary_remote_addr zone=apilimit:10m rate=10r/s;

server {
    listen 80;

    location /api/ {
        limit_req zone=apilimit burst=20 nodelay;
        proxy_pass http://backend;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}
RATELIMIT

Caching Reverse Proxy

cat > /etc/nginx/conf.d/proxycache.conf << 'CACHE'
proxy_cache_path /var/cache/nginx/proxy levels=1:2
    keys_zone=backend_cache:10m max_size=100m
    inactive=60m use_temp_path=off;

server {
    listen 80;

    location / {
        proxy_pass http://backend;
        proxy_cache backend_cache;
        proxy_cache_valid 200 302 10m;
        proxy_cache_valid 404 1m;
        proxy_cache_key "$scheme$request_method$host$request_uri";

        add_header X-Cache-Status $upstream_cache_status;

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}
CACHE

MicroVM with the Backend on Another VM

# Run Nginx on MicroVM 1
qemu-system-x86_64 \
  -M microvm -enable-kvm -cpu host -m 256m -smp 1 \
  -kernel vmlinuz \
  -append "console=ttyS0 root=/dev/vda rw" \
  -drive id=rootfs,file=nginx-rootfs.img,format=raw,if=none \
  -device virtio-blk-device,drive=rootfs \
  -netdev tap,id=net0,ifname=tap0,script=no,downscript=no \
  -device virtio-net-device,netdev=net0 \
  -nographic -serial stdio

# Run the Node.js app on MicroVM 2
qemu-system-x86_64 \
  -M microvm -enable-kvm -cpu host -m 512m -smp 2 \
  -kernel vmlinuz \
  -append "console=ttyS0 root=/dev/vda rw" \
  -drive id=rootfs,file=node-rootfs.img,format=raw,if=none \
  -device virtio-blk-device,drive=rootfs \
  -netdev tap,id=net0,ifname=tap1,script=no,downscript=no \
  -device virtio-net-device,netdev=net0 \
  -nographic -serial stdio

# Nginx config (proxy to VM2's IP: 10.0.0.3)
upstream backend {
    server 10.0.0.3:3000;
}

Health Check

# Upstream with health checks (Nginx Plus or the healthcheck module)
upstream backend {
    zone backend 64k;
    server 127.0.0.1:3000;
    server 127.0.0.1:3001;
}

# Active health check script
cat > /etc/nginx/healthcheck.sh << 'HEALTH'
#!/bin/bash
curl -sf http://127.0.0.1:3000/health || exit 1
HEALTH
chmod +x /etc/nginx/healthcheck.sh

Monitoring

# Check the logs in real time
tail -f /var/log/nginx/access.log

# Check the cache status (if using caching)
curl -I http://localhost/ | grep X-Cache-Status

# Check the upstream status
curl http://localhost/nginx_status

Troubleshooting

502 Bad Gateway

# Check whether the backend is running
ss -tlnp | grep 3000

# Check the error log
tail /var/log/nginx/error.log

# Make sure proxy_pass is correct
proxy_pass http://127.0.0.1:3000;

504 Gateway Timeout

# Increase the timeouts
proxy_connect_timeout 300s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;