HAProxy Configuration for KVM Services Public Access
This guide describes how to configure HAProxy on the Entirius KVM Server to publicly expose services running in KVM instances, such as n8n deployed using the N8N Deployment guide.
This guide assumes you have completed:
- KVM Server Install - Ubuntu 24.04 server with KVM, HAProxy, and WebVirtCloud
- N8N Deployment - At least one n8n instance deployed using entirius-scripts-kvm-deployer
You should have:
- HAProxy already installed and configured with basic settings
- One or more KVM instances running services (e.g., n8n on port 80)
- VM instances accessible on the bridge network (br0)
HAProxy will act as a reverse proxy to route incoming requests to appropriate KVM instances based on:
- Host headers (domain-based routing)
- Path prefixes (path-based routing)
- Port-based routing (different services on different ports)
First, identify the IP addresses of your KVM instances:
# List all running VMs
sudo virsh list --all
# Get IP address for specific VM (replace with your VM name)
sudo virsh domifaddr vm-company-production-n8n
# Example output:
# Name MAC address Protocol Address
# -------------------------------------------------------------------------------
# vnet0 52:54:00:xx:xx:xx ipv4 192.168.122.100/24
Add these IPs to your /etc/hosts file for easy management:
sudo vim /etc/hosts
Add entries for your KVM instances:
# KVM Instances
192.168.122.100 n8n-prod.local
192.168.122.101 n8n-staging.local
192.168.122.102 api-service.local
Edit the HAProxy configuration:
sudo cp /etc/haproxy/haproxy.cfg /etc/haproxy/haproxy.cfg.backup
sudo vim /etc/haproxy/haproxy.cfg
Here’s a complete configuration example that exposes multiple KVM services:
global
log stdout local0
chroot /var/lib/haproxy
stats socket /run/haproxy/admin.sock mode 660 level admin
stats timeout 30s
user haproxy
group haproxy
daemon
defaults
mode http
log global
option httplog
option dontlognull
option log-health-checks
option forwardfor
option http-server-close
timeout connect 5000
timeout client 50000
timeout server 50000
errorfile 400 /etc/haproxy/errors/400.http
errorfile 403 /etc/haproxy/errors/403.http
errorfile 408 /etc/haproxy/errors/408.http
errorfile 500 /etc/haproxy/errors/500.http
errorfile 502 /etc/haproxy/errors/502.http
errorfile 503 /etc/haproxy/errors/503.http
errorfile 504 /etc/haproxy/errors/504.http
# HAProxy Statistics Interface
listen stats
bind *:8404
stats enable
stats uri /stats
stats refresh 30s
stats admin if TRUE
# Frontend for HTTP traffic
frontend http_frontend
bind *:80
# Domain-based routing for n8n instances
acl is_n8n_prod hdr(host) -i n8n.yourdomain.com
acl is_n8n_staging hdr(host) -i n8n-staging.yourdomain.com
# Path-based routing examples
acl is_api path_beg /api
acl is_webhooks path_beg /webhook
# Route to appropriate backends
use_backend n8n_production if is_n8n_prod
use_backend n8n_staging if is_n8n_staging
use_backend api_services if is_api
use_backend webhook_services if is_webhooks
# Default backend (you can customize this)
default_backend webvirtcloud_backend
# Frontend for HTTPS traffic
frontend https_frontend
bind *:443 ssl crt /etc/haproxy/ssl/haproxy.pem
# Same ACL rules as HTTP
acl is_n8n_prod hdr(host) -i n8n.yourdomain.com
acl is_n8n_staging hdr(host) -i n8n-staging.yourdomain.com
acl is_api path_beg /api
acl is_webhooks path_beg /webhook
# Route to appropriate backends
use_backend n8n_production if is_n8n_prod
use_backend n8n_staging if is_n8n_staging
use_backend api_services if is_api
use_backend webhook_services if is_webhooks
# Default backend
default_backend webvirtcloud_backend
# Backend for production n8n instance
backend n8n_production
balance roundrobin
option httpchk GET /healthz
http-check expect status 200
server n8n-prod 192.168.122.100:80 check
# Backend for staging n8n instance
backend n8n_staging
balance roundrobin
option httpchk GET /healthz
http-check expect status 200
server n8n-staging 192.168.122.101:80 check
# Backend for API services
backend api_services
balance roundrobin
option httpchk GET /health
http-check expect status 200
server api1 192.168.122.102:8000 check
server api2 192.168.122.103:8000 check
# Backend for webhook services
backend webhook_services
balance roundrobin
server webhook1 192.168.122.100:80 check
# Backend for WebVirtCloud (management interface)
backend webvirtcloud_backend
balance roundrobin
server webvirtcloud 127.0.0.1:8080 check
If you haven’t already configured SSL certificates, you can use Let’s Encrypt or create self-signed certificates:
sudo mkdir -p /etc/haproxy/ssl
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout /etc/haproxy/ssl/haproxy.pem \
-out /etc/haproxy/ssl/haproxy.pem \
-subj "/C=US/ST=State/L=City/O=Organization/CN=yourdomain.com"
sudo chown haproxy:haproxy /etc/haproxy/ssl/haproxy.pem
sudo chmod 600 /etc/haproxy/ssl/haproxy.pem
# Install certbot
sudo apt install certbot
# Stop HAProxy temporarily
sudo systemctl stop haproxy
# Generate certificate
sudo certbot certonly --standalone -d n8n.yourdomain.com -d n8n-staging.yourdomain.com
# Combine certificate and key for HAProxy
sudo cat /etc/letsencrypt/live/n8n.yourdomain.com/fullchain.pem \
/etc/letsencrypt/live/n8n.yourdomain.com/privkey.pem > \
/etc/haproxy/ssl/haproxy.pem
sudo chown haproxy:haproxy /etc/haproxy/ssl/haproxy.pem
sudo chmod 600 /etc/haproxy/ssl/haproxy.pem
# Start HAProxy
sudo systemctl start haproxy
Test the HAProxy configuration:
# Test configuration syntax
sudo haproxy -f /etc/haproxy/haproxy.cfg -c
If the configuration is valid, reload HAProxy:
# Reload HAProxy with new configuration
sudo systemctl reload haproxy
# Check HAProxy status
sudo systemctl status haproxy
# View HAProxy logs
sudo journalctl -u haproxy -f
Configure your DNS to point to your server:
# DNS A Records (replace with your server's public IP)
n8n.yourdomain.com IN A YOUR.SERVER.PUBLIC.IP
n8n-staging.yourdomain.com IN A YOUR.SERVER.PUBLIC.IP
api.yourdomain.com IN A YOUR.SERVER.PUBLIC.IP
For local testing, add entries to your local /etc/hosts:
YOUR.SERVER.IP n8n.yourdomain.com
YOUR.SERVER.IP n8n-staging.yourdomain.com
YOUR.SERVER.IP api.yourdomain.com
Ensure your firewall allows the necessary ports:
# Allow HTTP and HTTPS traffic
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# Allow HAProxy stats (optional, restrict as needed)
sudo ufw allow 8404/tcp
# Check firewall status
sudo ufw status
Test your configuration:
# Test HTTP access to n8n
curl -H "Host: n8n.yourdomain.com" http://YOUR.SERVER.IP/
# Test HTTPS access
curl -k -H "Host: n8n.yourdomain.com" https://YOUR.SERVER.IP/
# Test API endpoint
curl -H "Host: api.yourdomain.com" http://YOUR.SERVER.IP/api/health
# Check HAProxy stats
curl http://YOUR.SERVER.IP:8404/stats
If you have multiple n8n instances for high availability:
backend n8n_production
balance roundrobin
option httpchk GET /healthz
http-check expect status 200
server n8n-prod-1 192.168.122.100:80 check
server n8n-prod-2 192.168.122.101:80 check weight 2
server n8n-prod-3 192.168.122.102:80 check backup
For applications requiring session persistence:
backend n8n_production
balance roundrobin
cookie SERVERID insert indirect nocache
server n8n-prod-1 192.168.122.100:80 check cookie n8n1
server n8n-prod-2 192.168.122.101:80 check cookie n8n2
To prevent abuse:
frontend http_frontend
# Rate limiting: 10 requests per 10 seconds per IP
stick-table type ip size 100k expire 30s store http_req_rate(10s)
http-request track-sc0 src
http-request deny if { sc_http_req_rate(0) gt 10 }
Access the stats page at: http://YOUR.SERVER.IP:8404/stats
This provides real-time monitoring of:
- Backend server status
- Request rates and response times
- Connection counts
- Health check results
Monitor HAProxy logs:
# Follow HAProxy logs
sudo journalctl -u haproxy -f
# Analyze access patterns
sudo grep "n8n.yourdomain.com" /var/log/haproxy.log | tail -20
# Check for errors
sudo grep "HTTP/5" /var/log/haproxy.log
503 Service Unavailable
- Check if KVM instances are running:
sudo virsh list - Verify instance IPs:
sudo virsh domifaddr VM_NAME - Test direct access:
curl http://192.168.122.100:80
- Check if KVM instances are running:
SSL Certificate Issues
- Check certificate validity:
openssl x509 -in /etc/haproxy/ssl/haproxy.pem -text -noout - Verify certificate permissions:
ls -la /etc/haproxy/ssl/
- Check certificate validity:
Health Check Failures
- Check if health check endpoints exist
- Verify firewall rules on KVM instances
- Test health checks manually:
curl http://192.168.122.100/healthz
# Check HAProxy configuration
sudo haproxy -f /etc/haproxy/haproxy.cfg -c
# View real-time connections
sudo ss -tlnp | grep haproxy
# Check backend server connectivity
sudo netcat -zv 192.168.122.100 80
# Monitor HAProxy stats via CLI
echo "show stat" | sudo socat stdio /run/haproxy/admin.sock
- Access Control: Restrict HAProxy stats page access
- SSL Configuration: Use strong SSL/TLS settings
- Rate Limiting: Implement rate limiting for public endpoints
- Monitoring: Set up alerts for service failures
- Firewall: Ensure only necessary ports are exposed
- Deploy new KVM instance using entirius-scripts-kvm-deployer
- Get instance IP:
sudo virsh domifaddr NEW_VM_NAME - Add backend configuration to HAProxy
- Add routing rules to frontend
- Test and reload HAProxy configuration
# Create renewal script
sudo vim /etc/cron.monthly/renew-haproxy-ssl
#!/bin/bash
certbot renew --quiet
cat /etc/letsencrypt/live/n8n.yourdomain.com/fullchain.pem \
/etc/letsencrypt/live/n8n.yourdomain.com/privkey.pem > \
/etc/haproxy/ssl/haproxy.pem
chown haproxy:haproxy /etc/haproxy/ssl/haproxy.pem
chmod 600 /etc/haproxy/ssl/haproxy.pem
systemctl reload haproxy
sudo chmod +x /etc/cron.monthly/renew-haproxy-ssl
This configuration provides a robust reverse proxy setup for exposing KVM-based services publicly while maintaining security and monitoring capabilities.
