Arianet cloud load balancer in front of two web servers
A load balancer spreads requests across several servers. If one server fails, the health check notices and traffic goes only to healthy servers; so you can update servers one at a time without taking the site down, and handle more load by just adding servers.
In this tutorial you create two Nginx web servers labelled role=web on a private network, put a load balancer in front of them, and test traffic distribution and a server failure.
Prerequisites
- The Arianet CLI installed and configured with a token that has
cloud:servers:readandcloud:servers:write - An SSH key in
~/.ssh/id_ed25519.pub - Familiarity with private networks
Load balancers can also be created and managed in the panel under Networking → Load balancers («شبکه ← لود بالانسرها»); this tutorial uses the CLI so every step is exact and repeatable.
1. Choose a load balancer type
arianet cloud datacenters
arianet cloud load-balancers types
arianet cloud pricing --datacenter ovh
Each type (for example lb11) has its own maximum number of services (listeners), targets and concurrent connections, and its price is in the price list. Change the type later with change-type.
2. Private network
arianet cloud networks create --name web-net --datacenter ovh \
--ip-range 10.0.0.0/16 --subnet-ip-range 10.0.1.0/24 --wait
Note the network id (<network-id> below).
3. Two web servers
Create a cloud-init file that installs Nginx and writes the server's hostname on the home page, so you can see which server answered each request:
cat > web.yaml <<'EOF'
#cloud-config
packages:
- nginx
runcmd:
- echo "served by $(hostname)" > /var/www/html/index.html
EOF
Create two servers labelled role=web:
arianet cloud servers create --name web --count 2 --datacenter ovh --plan 3 --image ubuntu-24.04 \
--ssh-key "$(cat ~/.ssh/id_ed25519.pub)" --labels role=web \
--network <network-id> --user-data-file web.yaml --wait
--count 2 creates two servers named web and web-2. Pick the plan id with arianet cloud plans --datacenter ovh.
4. Create the load balancer
arianet cloud load-balancers create --name web-lb --datacenter ovh --type lb11 --wait
arianet cloud load-balancers attach-network <lb-id> --network <network-id>
The default algorithm is round_robin (requests in turn). For applications with long-running requests, least_connections (the server with the fewest open connections) fits better:
arianet cloud load-balancers change-algorithm <lb-id> --algorithm least_connections
5. HTTP service with a health check
arianet cloud load-balancers add-service <lb-id> \
--protocol http --listen-port 80 --destination-port 80 \
--health-protocol http --health-port 80 --health-path / \
--health-interval 10 --health-timeout 5 --health-retries 3
listen-port: the port the load balancer accepts requests on.destination-port: the application's port on the servers.- The health check requests
/on each server everyintervalseconds; a server that failsretriestimes leaves the rotation until it is healthy again. Atcphealth check (port open only) is also available. Set the accepted codes with--health-status-codeand the Host header of the check with--health-domain.
6. Add targets by label
arianet cloud load-balancers add-target <lb-id> --label-selector role=web --use-private-ip
A label selector target covers every server labelled role=web; a new server created with the same label joins the load balancer automatically. --use-private-ip sends traffic to the servers over the private network. A target can also be one server (--server <id>) or an IP (--ip).
7. Verify
Target health:
arianet cloud load-balancers health <lb-id>
arianet cloud load-balancers get <lb-id> # shows the public ipv4 / ipv6
Both targets should be healthy. Now send a few requests:
for i in 1 2 3 4; do curl -s http://LB_IP/; done
With round_robin the answers alternate between the two servers' hostnames (for example served by web and served by web-2).
Test a failure: on web-2, stop the web server with sudo systemctl stop nginx. After a few health checks, health shows it as unhealthy and every request goes to web. Bring it back with sudo systemctl start nginx.
8. Close direct access to the servers
Now that traffic arrives over the private network, port 80 on the servers does not need to be open to the internet. Create a cloud firewall for label role=web that only accepts SSH from your IP, and in ufw on each server open port 80 only to the private network:
sudo ufw allow OpenSSH
sudo ufw allow from 10.0.0.0/16 to any port 80 proto tcp
sudo ufw --force enable
After every firewall change, check health again; if targets turn unhealthy, the firewall is blocking the load balancer's traffic.
HTTPS
A service with protocol https on the Arianet load balancer is TLS passthrough: the load balancer passes the encrypted connection to the servers without opening it, and the certificate is installed on the servers themselves.
arianet cloud load-balancers add-service <lb-id> \
--protocol https --listen-port 443 --destination-port 443 \
--health-protocol tcp --health-port 443
sudo ufw allow from 10.0.0.0/16 to any port 443 proto tcp # on each server
- Every server needs a valid certificate for the same domain. Because Let's Encrypt validation requests can reach any of the servers, get the certificate with the DNS-01 method, or get it on one server and copy it to the others (Nginx and Let's Encrypt).
- In the API, an
httpsservice has aredirect_httpoption that answers plain HTTP on port 80 with a 301 toredirect_host, and the HTTP health check has atlsoption (check over HTTPS, without verifying the certificate). Details in the interactive cloud API reference.
Other service options
- Sticky sessions (
--sticky-sessions,--cookie-name): on anhttpservice, each user's requests go to the same server; for applications that keep sessions on the server's disk. - Proxy protocol (
--proxyprotocol): passes the user's real IP to the server with the PROXY protocol. The web server must accept it too (in Nginx:listen 80 proxy_protocol;withset_real_ip_fromandreal_ip_header proxy_protocol;), otherwise every request fails. Only enable it once you have changed the web server configuration. - Private-only load balancer: with
--no-public-interfaceat creation the load balancer gets no public IP; for internal services.
Common errors
| Problem | Fix |
|---|---|
All targets are unhealthy | Test the health check path and port with curl -I http://10.0.1.x/ from another server on the network; ufw or the cloud firewall may block private network traffic. |
| HTTP health check fails but the site opens | The check path returns a non-2xx code (for example 301 or 404). Use another path or set --health-status-code. |
| The label selector target finds no servers | Check the servers' labels with arianet cloud servers list --label-selector role=web. With --use-private-ip, the servers must be on the load balancer's network. |
lb_locked (status 423) | The load balancer is busy with a previous action; retry in a few seconds. |
Invalid certificate in the browser with an https service | The certificate on one of the servers is old or different; all servers need the same valid certificate. |
The same setup with Terraform
The complete Terraform example has exactly this structure: a private network, three web servers labelled role=web, a firewall, a load balancer with an HTTP service and health check, and a label selector target on private IPs. See also Create servers with Terraform and the CLI.
Next steps
Create your load balancer
The Arianet cloud load balancer, with health checks, label targets and private networking, is billed hourly. In the Arianet panel, go to Cloud server / Cloud Computing → Networking → Load balancers, or run the commands above with the CLI.