keepcalculator

06 Numbers and Powers

Host Multiple Websites with Nginx, HAProxy & LXD

Card No. 06·66585 · Updated · Standard formula, shown below

This guide walks you through hosting multiple websites on a single VPS using LXD containers for isolation, HAProxy as the front-end load balancer and SSL terminator, and Nginx inside each container to serve the actual site content. You’ll end up with a scalable setup where each site runs in its own lightweight container, and you can add or remove sites without touching the others.

What You’ll Need

  • A VPS with at least 2 GB RAM and 20 GB disk (more if you plan many sites).
  • Ubuntu 22.04 or 24.04 installed on the VPS (commands assume apt).
  • A domain name with DNS access to point subdomains (or separate domains) to your VPS IP.
  • SSH access to the VPS as a user with sudo privileges.
  • Basic familiarity with the command line and text editors (nano or vim).

Step-by-Step

  1. Update the system and install LXD
    SSH into your VPS and run:

    sudo apt update && sudo apt upgrade -y
    sudo apt install lxd -y

    Initialize LXD with default settings (choose "yes" for clustering? No, select "no" when asked about clustering, and accept the default storage pool options):

    sudo lxd init

    For a simple setup, accept all defaults except when asked about clustering—answer "no".

  2. Create a network bridge for containers
    LXD usually creates a bridge called lxdbr0 automatically. Verify with:

    lxc network list

    If not present, create one:

    lxc network create lxdbr0

    Then attach it to the default profile:

    lxc profile device add default eth0 nic network=lxdbr0
    lxc profile device add default root disk path=/ pool=default

    (The second command may already exist; if it errors, it’s fine.)

  3. Launch a container for your first site
    Pull an Ubuntu image and start a container named site1:

    lxc launch ubuntu:22.04 site1

    Wait a few seconds, then get a shell inside:

    lxc exec site1 -- bash

    Inside the container, update and install Nginx:

    apt update && apt install nginx -y

    Exit the container with exit.

  4. Configure the container’s network
    By default, the container gets an IP from the LXD bridge. Find it with:

    lxc list

    Note the IPv4 address (e.g., 10.10.10.10). You’ll use this as the backend server in HAProxy.

  5. Repeat for additional sites
    For each new site, repeat steps 3-4 with a different container name (e.g., site2, site3). Each container runs its own Nginx instance.
  6. Create a container for HAProxy
    Launch a dedicated container for HAProxy:

    lxc launch ubuntu:22.04 haproxy

    Install HAProxy inside:

    lxc exec haproxy -- bash -c "apt update && apt install haproxy -y"

    Get its IP with lxc list (e.g., 10.10.10.5).

  7. Configure HAProxy
    Edit the HAProxy configuration file inside the container:

    lxc exec haproxy -- nano /etc/haproxy/haproxy.cfg

    Replace the contents with a basic setup that listens on ports 80 and 443 and routes based on the domain name. Below is a minimal example for two sites (replace IPs and domains with yours):

    global
        log /dev/log local0
        maxconn 4096
        user haproxy
        group haproxy
    
    defaults
        log global
        mode http
        option httplog
        option dontlognull
        timeout connect 5000
        timeout client 50000
        timeout server 50000
    
    frontend http-in
        bind *:80
        # Redirect all HTTP to HTTPS
        redirect scheme https code 301 if !{ ssl_fc }
    
    frontend https-in
        bind *:443 ssl crt /etc/haproxy/certs/site1.pem crt /etc/haproxy/certs/site2.pem
        mode http
        # Route based on SNI or Host header
        use_backend site1_backend if { ssl_fc_sni site1.example.com }
        use_backend site2_backend if { ssl_fc_sni site2.example.com }
        default_backend site1_backend
    
    backend site1_backend
        server site1 10.10.10.10:80 check
    
    backend site2_backend
        server site2 10.10.10.11:80 check

    Save and exit.

  8. Set up SSL certificates
    For each domain, you need a combined PEM file containing the certificate and private key. If you have certificates from Let’s Encrypt (e.g., via certbot), combine them:

    cat fullchain.pem privkey.pem > site1.pem

    Place these files in /etc/haproxy/certs/ inside the HAProxy container. You can copy them from your host using lxc file push.

  9. Start HAProxy
    Inside the HAProxy container, restart the service:

    lxc exec haproxy -- systemctl restart haproxy

    Check for errors with lxc exec haproxy -- journalctl -u haproxy.

  10. Configure DNS
    Point each domain (e.g., site1.example.com, site2.example.com) to your VPS’s public IP address using an A record. This is done at your DNS provider.
  11. Open firewall ports
    On the VPS host, allow traffic to the HAProxy container. If using UFW, run:

    sudo ufw allow 80,443/tcp

    Also ensure that the HAProxy container can reach the backend containers (usually allowed by default on the LXD bridge).

  12. Test the setup
    Visit your domains in a browser. You should see the default Nginx page from each container. If not, check the HAProxy logs and the container IPs.

Common Mistakes

  • Forgetting to open ports on the host: Even if HAProxy is listening, the VPS firewall may block incoming traffic. Always allow 80 and 443.
  • Using the wrong backend IP: Container IPs can change after reboot. Use static IPs or update HAProxy config accordingly.
  • Missing SSL certificates: HAProxy won’t start if the certificate files are missing or malformed. Double-check file paths and permissions.
  • Not redirecting HTTP to HTTPS: If you omit the redirect, users may access your site over insecure HTTP.
  • Overlapping container names: LXD container names must be unique. Use descriptive names like site1, site2.

Tips

  • Use static IPs for containers: Configure DHCP reservations in LXD to keep IPs stable. Edit the container’s NIC with lxc config device override.
  • Automate with scripts: Write a shell script to create a new container, install Nginx, and add a backend entry to HAProxy.
  • Monitor resource usage: Use lxc top to see CPU/memory per container. This helps you decide when to scale.
  • Backup your HAProxy config: Keep a copy of haproxy.cfg on the host for quick recovery.
  • Use Let’s Encrypt for free SSL: Automate certificate renewal with certbot’s webroot plugin, then combine and push the PEM to HAProxy.

Troubleshooting

  • Site not loading: Check if HAProxy is running: lxc exec haproxy -- systemctl status haproxy. If not, view logs: lxc exec haproxy -- journalctl -u haproxy -n 50. Look for configuration errors.
  • SSL errors: Ensure the PEM file contains both certificate and key, and that the key matches the certificate. Test with openssl s_client -connect yourdomain:443.
  • Backend unreachable: From the HAProxy container, ping the backend IP: lxc exec haproxy -- ping 10.10.10.10. If it fails, check that the backend container is running and Nginx is listening on port 80.
  • Ports already in use: If HAProxy fails to bind, check if another process is using port 80/443. Use lxc exec haproxy -- ss -tulpn.
  • DNS propagation: After changing DNS, it may take up to 24 hours. Use dig yourdomain to see if it resolves to your VPS IP.

Frequently Asked Questions

Can I use Docker instead of LXD?

Yes, Docker is an alternative, but LXD provides system containers that behave more like VMs, which can be easier for running full Nginx configurations. Both work; choose based on your familiarity.

Do I need a separate HAProxy container?

Not strictly. You could run HAProxy on the host, but using a container keeps the host clean and makes it easier to move or replicate the setup.

How do I add a new site later?

Create a new LXD container, install Nginx, get its IP, add a backend entry to HAProxy, and update the HAProxy certificate file. Then reload HAProxy with systemctl reload haproxy.

Can I use this setup for WordPress sites?

Yes, you can install PHP and MySQL inside each container. Just ensure the container has enough resources. You may also want to use a separate database container for better isolation.

What if I don’t have a domain name?

You can test using the VPS IP directly, but HAProxy routing by domain won’t work. You’d need to use different ports or a single default backend.

Is this setup secure?

LXD containers provide good isolation, but you should still keep the host and containers updated, use strong SSH keys, and consider adding fail2ban. Also, use HTTPS everywhere.

Related

Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.

More cards in this drawer

The whole Numbers and Powers drawer →