What's Actually Happening
Nginx returns 503 Service Unavailable when backend services are overloaded or temporarily unavailable. Backend cannot handle requests, rate limits are hit, or services are under maintenance.
The Error You'll See
503 Service Unavailable:
<html>
<head><title>503 Service Temporarily Unavailable</title></head>
<body>
<center><h1>503 Service Temporarily Unavailable</h1></center>
<hr><center>nginx</center>
</body>
</html>Nginx error log:
```bash tail -f /var/log/nginx/error.log
limiting requests, excess 50.100 by zone "api_limit" ```
Why This Happens
- 1.Backend overloaded - Backend cannot handle request volume
- 2.Rate limiting - Request rate limit exceeded
- 3.Backend queue full - Backend connection queue full
- 4.Insufficient workers - Not enough backend workers
- 5.Resource exhaustion - Backend out of memory or CPU
- 6.Maintenance mode - Backend under maintenance
- 7.Backend crashing - Backend crashing repeatedly
Step 1: Check Nginx Error Logs
```bash tail -f /var/log/nginx/error.log
grep -i "503|limit|unavailable" /var/log/nginx/error.log | tail -20
journalctl -u nginx -f | grep -i "503|limit" ```
Step 2: Check Backend Load
```bash systemctl status backend
top -p $(pgrep -f backend)
ps aux | grep backend
# Check backend processes: pgrep -c backend-process
# Backend resource usage: pidstat -p $(pgrep -f backend) 1 5 ```
Step 3: Check Backend Queue
```bash # For PHP-FPM: php-fpm -tt 2>&1 | grep -i listen
ss -tlnp | grep php-fpm
# Check PHP-FPM status: curl http://localhost/php-fpm-status
# For Gunicorn: curl http://localhost/server-status
# Check connection queue: netstat -an | grep backend-port | grep SYN ```
Step 4: Check Rate Limiting
```bash cat /etc/nginx/nginx.conf | grep -A 10 limit_req
cat /etc/nginx/conf.d/*.conf | grep -A 5 limit_req_zone
# Rate limit configuration: limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
# Current rate limit status: # Check nginx stub_status curl http://localhost/nginx_status ```
Step 5: Check Backend Configuration
```bash # PHP-FPM workers: cat /etc/php-fpm.d/www.conf | grep -E "pm\.(max_children|start_servers)"
# Gunicorn workers: cat /etc/systemd/system/gunicorn.service | grep workers
# uWSGI workers: cat /etc/uwsgi/apps-enabled/app.ini | grep workers
# Node.js cluster: # Check cluster worker count ```
Step 6: Increase Backend Capacity
```bash # PHP-FPM: # Increase workers: pm.max_children = 50 pm.start_servers = 10
systemctl restart php-fpm
# Gunicorn: # Increase workers: ExecStart=/usr/bin/gunicorn --workers 4 --threads 2 app:app
systemctl restart gunicorn ```
Step 7: Adjust Rate Limits
```bash # Increase rate limit: limit_req_zone $binary_remote_addr zone=api_limit:10m rate=100r/s;
# Or allow burst: limit_req zone=api_limit burst=50 nodelay;
# Or disable rate limit temporarily: # Comment out limit_req directives
nginx -t systemctl reload nginx ```
Step 8: Check Backend Resources
```bash free -h
df -h
iostat -x 1
# Check backend memory: ps aux | grep backend | awk '{sum += $6} END {print sum/1024 " MB"}'
# Check CPU: top -b -n 1 | grep -A 5 "PID USER" ```
Step 9: Implement Caching
```bash # Enable proxy caching: proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=api_cache:10m max_size=1g inactive=60m;
location /api/ { proxy_pass http://backend; proxy_cache api_cache; proxy_cache_valid 200 10m; proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504; }
nginx -t systemctl reload nginx ```
Step 10: Monitor Service Availability
```bash watch -n 5 'curl -s -o /dev/null -w "%{http_code}" http://localhost/api/health'
# Monitor backend status: watch -n 5 systemctl status backend
# Monitor nginx status: watch -n 5 'curl -s http://localhost/nginx_status'
# Monitor resources: watch -n 5 'free -h && echo && df -h' ```
Nginx 503 Service Unavailable Checklist
| Check | Command | Expected |
|---|---|---|
| Error logs | tail error.log | Specific error |
| Backend load | top/systemctl | Normal |
| Rate limits | nginx config | Not exceeded |
| Workers | backend config | Sufficient |
| Resources | free/df | Available |
| Queue | netstat/ss | Not full |
Verify the Fix
```bash curl -I http://localhost/api/endpoint
systemctl status backend
tail -f /var/log/nginx/error.log | grep -i 503
top -p $(pgrep -f backend)
curl -s http://localhost/nginx_status
free -h ```
Related Issues
- [Fix Nginx 502 Bad Gateway](/articles/fix-nginx-502-bad-gateway)
- [Fix Nginx Upstream Timeout](/articles/fix-nginx-upstream-timeout)
- [Fix PHP-FPM Service Unavailable](/articles/fix-php-fpm-service-unavailable)