When building mission-critical web applications, having a failover mechanism is crucial. Many developers mistakenly think they need complex load balancing setups when they actually just need simple high availability. The requirement here is clear: route all traffic to Server A, and only failover to Server B when A becomes unavailable.
Here's the fundamental upstream configuration that achieves this:
upstream backend { server primary.example.com:80 max_fails=3 fail_timeout=30s; server backup.example.com:80 backup; }
The magic happens with these directives:
max_fails
: Number of unsuccessful attempts before marking server as unavailablefail_timeout
: Time period for max_fails and duration server is marked as downbackup
: Marks this server as backup - only used when primary fails
Here's a full configuration you can drop into your nginx.conf:
http { upstream myapp { server 192.168.1.100:8080 max_fails=3 fail_timeout=30s; server 192.168.1.101:8080 backup; } server { listen 80; server_name example.com; location / { proxy_pass http://myapp; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } } }
To verify your setup works:
- Start both backend servers
- Make requests - all should go to primary
- Stop primary server
- After fail_timeout period, requests should automatically switch to backup
- Bring primary back up - traffic should return automatically
For better visibility, add these to your location block:
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504; proxy_intercept_errors on;
This ensures nginx properly handles various failure scenarios.
When setting up a reverse proxy with Nginx, you might need a failover mechanism where traffic is automatically redirected to a backup server if the primary server fails. Unlike load balancing, which distributes traffic across multiple servers, failover focuses on high availability by using a secondary server only when the primary is unavailable.
Here's a simple Nginx configuration to achieve failover without load balancing:
http {
upstream backend {
server primary.example.com:80 max_fails=1 fail_timeout=5s;
server backup.example.com:80 backup;
}
server {
listen 80;
location / {
proxy_pass http://backend;
proxy_next_upstream error timeout http_500 http_502 http_503 http_504;
}
}
}
The important directives in this setup are:
max_fails
: Number of unsuccessful attempts before marking server as unavailablefail_timeout
: Time period during which max_fails must occurbackup
: Marks the server as backup (only used when primary fails)proxy_next_upstream
: Specifies which errors should trigger failover
For more reliable failover, you can implement active health checks:
http {
upstream backend {
zone backend 64k;
server primary.example.com:80 max_fails=3 fail_timeout=30s;
server backup.example.com:80 backup;
}
server {
listen 80;
location / {
proxy_pass http://backend;
health_check interval=5s fails=3 passes=1 uri=/health;
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
}
}
}
If your application requires session persistence, you'll need to handle this carefully during failover:
http {
upstream backend {
ip_hash;
server primary.example.com:80 max_fails=3 fail_timeout=30s;
server backup.example.com:80 backup;
}
}
Always test your failover configuration:
- Start Nginx with your configuration
- Verify traffic goes to primary server
- Simulate failure (stop web service or block port)
- Verify traffic fails over to backup
- Restore primary and verify traffic returns
Watch out for these issues:
- Insufficient timeout values causing premature failover
- Backup server not properly synchronized with primary
- DNS caching affecting failover behavior
- Missing proper health check endpoints
Add these to your configuration for better visibility:
http {
log_format upstream_log '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'rt=$request_time uct="$upstream_connect_time" '
'uht="$upstream_header_time" urt="$upstream_response_time" '
'upstream_addr=$upstream_addr';
server {
access_log /var/log/nginx/access.log upstream_log;
error_log /var/log/nginx/error.log warn;
}
}