Automation & Bash Scripts
Learning Focus
Leave this lesson with a working understanding of automation & bash scripts that you can apply immediately in production.
Production-ready scripts. Store them in /etc/nginx/scripts/ or /usr/local/bin/.
sudo mkdir -p /etc/nginx/scripts
sudo chmod 700 /etc/nginx/scripts
Health Check Script
/etc/nginx/scripts/nginx-health.sh
#!/bin/bash
# Nginx full health check
G='\033[0;32m'; R='\033[0;31m'; NC='\033[0m'
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
svc() { systemctl is-active --quiet nginx && echo -e "${G}ACTIVE${NC}" || echo -e "${R}DOWN${NC}"; }
echo "============= Nginx Health $TIMESTAMP ============="
printf "%-22s %s\n" "Service:" "$(svc)"
printf "%-22s %s\n" "Version:" "$(nginx -v 2>&1)"
printf "%-22s %s\n" "Config test:" "$(nginx -t 2>&1 | tail -1)"
printf "%-22s %s\n" "Workers:" "$(ps aux | grep 'nginx: worker' | grep -v grep | wc -l)"
printf "%-22s %s\n" "PHP-FPM:" "$(systemctl is-active php8.4-fpm)"
printf "%-22s %s\n" "FPM workers:" "$(ps aux | grep php-fpm | grep -v grep | wc -l)"
printf "%-22s %s\n" "Ports:" "$(sudo ss -tlnp | grep nginx | awk '{print $4}' | tr '\n' ' ')"
printf "%-22s %s\n" "Uptime:" "$(ps -p $(cat /run/nginx.pid 2>/dev/null) -o etime= 2>/dev/null || echo N/A)"
echo ""
echo "--- Last 5 error lines ---"
sudo tail -5 /var/log/nginx/error.log
echo "===================================================="
Auto-Restart Watchdog
/etc/nginx/scripts/nginx-watchdog.sh
#!/bin/bash
# Monitor Nginx and restart + alert if down
WEBHOOK="https://hooks.slack.com/services/YOUR/WEBHOOK"
LOG="/var/log/nginx-watchdog.log"
if ! systemctl is-active --quiet nginx; then
systemctl restart nginx
sleep 3
if systemctl is-active --quiet nginx; then
MSG="⚠️ Nginx was DOWN — restarted on $(hostname) at $(date)"
else
MSG="🚨 Nginx is DOWN and FAILED to restart on $(hostname)"
fi
echo "$MSG" >> "$LOG"
curl -s -X POST -H 'Content-type: application/json' \
--data "{\"text\":\"$MSG\"}" "$WEBHOOK" > /dev/null
fi
Config Backup
/etc/nginx/scripts/nginx-backup.sh
#!/bin/bash
# Backup Nginx config and SSL — run before upgrades
BACKUP_DIR="${1:-/root/nginx-backups}"
DATE=$(date +%Y%m%d-%H%M%S)
ARCHIVE="$BACKUP_DIR/nginx-$DATE.tar.gz"
mkdir -p "$BACKUP_DIR"
PATHS=( "/etc/nginx" "/etc/ssl/private" "/etc/ssl/certs" )
tar -czf "$ARCHIVE" "${PATHS[@]}" 2>/dev/null && \
echo "[✓] Backup: $ARCHIVE ($(du -sh "$ARCHIVE" | awk '{print $1}'))" || \
{ echo "[✗] Backup failed"; exit 1; }
# Keep last 10
ls -t "$BACKUP_DIR"/nginx-*.tar.gz 2>/dev/null | tail -n +11 | xargs rm -f
Safe Reload
/etc/nginx/scripts/nginx-reload.sh
#!/bin/bash
# Test config and reload, then verify no new errors
LOG="/var/log/nginx/error.log"
BEFORE=$(wc -l < "$LOG")
sudo nginx -t 2>&1 || { echo "[✗] Config test FAILED — not reloading"; exit 1; }
sudo nginx -s reload
sleep 2
NEW=$(( $(wc -l < "$LOG") - BEFORE ))
[ $NEW -gt 0 ] && { echo "[!] $NEW new log entries:"; tail -n $NEW "$LOG"; } || echo "[✓] No new errors"
systemctl is-active --quiet nginx && echo "[✓] Service active" || echo "[✗] Service DOWN"
New Site Provisioner
/etc/nginx/scripts/new-site.sh
#!/bin/bash
# Provision new Nginx site — usage: sudo bash new-site.sh example.com [php84|proxy:3000]
DOMAIN="${1:?Usage: $0 <domain> [php84|proxy:PORT]}"
TYPE="${2:-php84}"
WEB_ROOT="/var/www/$DOMAIN/public"
CONF="/etc/nginx/sites-available/$DOMAIN.conf"
mkdir -p "$WEB_ROOT"
chown -R www-data:www-data "/var/www/$DOMAIN"
chmod 755 "/var/www/$DOMAIN"
echo "<h1>$DOMAIN</h1>" > "$WEB_ROOT/index.html"
if [[ "$TYPE" == proxy:* ]]; then
PORT="${TYPE#proxy:}"
LOCATION_BLOCK=" location / {\n proxy_pass http://127.0.0.1:$PORT;\n proxy_set_header Host \$host;\n proxy_set_header X-Real-IP \$remote_addr;\n proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;\n proxy_set_header X-Forwarded-Proto \$scheme;\n }"
else
LOCATION_BLOCK=" location / { try_files \$uri \$uri/ /index.php?\$args; }\n location ~ \\.php\$ {\n try_files \$uri =404;\n include fastcgi.conf;\n fastcgi_pass unix:/run/php/php8.4-fpm.sock;\n }"
fi
cat > "$CONF" <<NGINX
server {
listen 80;
server_name $DOMAIN www.$DOMAIN;
return 301 https://\$host\$request_uri;
}
server {
listen 443 ssl http2;
server_name $DOMAIN www.$DOMAIN;
ssl_certificate /etc/ssl/certs/$DOMAIN.crt;
ssl_certificate_key /etc/ssl/private/$DOMAIN.key;
ssl_protocols TLSv1.2 TLSv1.3;
root $WEB_ROOT;
index index.php index.html;
access_log /var/log/nginx/$DOMAIN.access.log;
error_log /var/log/nginx/$DOMAIN.error.log warn;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
$(echo -e "$LOCATION_BLOCK")
location ~ /\\. { deny all; }
}
NGINX
ln -sf "$CONF" "/etc/nginx/sites-enabled/$DOMAIN.conf"
nginx -t && nginx -s reload
echo "[✓] $DOMAIN created — test: curl -H 'Host: $DOMAIN' http://localhost/"
SSL Expiry Checker
/etc/nginx/scripts/ssl-check.sh
#!/bin/bash
# Check SSL expiry for multiple domains
WARN_DAYS=14
for DOMAIN in "$@"; do
EXPIRY=$(echo | openssl s_client -connect "$DOMAIN:443" -servername "$DOMAIN" 2>/dev/null \
| openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2)
[ -z "$EXPIRY" ] && { echo "FAIL: Cannot reach $DOMAIN:443"; continue; }
DAYS=$(( ($(date -d "$EXPIRY" +%s) - $(date +%s)) / 86400 ))
[ $DAYS -lt $WARN_DAYS ] && echo "⚠️ $DOMAIN expires in $DAYS days" || echo "[✓] $DOMAIN — $DAYS days"
done
Log Analysis One-Liners
# Top 10 requested URLs
sudo awk '{print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -10
# Top 10 IPs by request count
sudo awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -10
# 5xx error count
sudo grep '" [5]' /var/log/nginx/access.log | wc -l
# Slow requests (> 1s)
sudo awk '$NF > 1' /var/log/nginx/access.log | wc -l
# 404 URLs
sudo awk '$9 == 404 {print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -10
# Bandwidth used (GB)
sudo awk '{sum+=$10} END {printf "%.2f GB\n", sum/1024/1024/1024}' /var/log/nginx/access.log
Cron Setup
sudo crontab -e
# Watchdog every 2 min
*/2 * * * * sudo bash /etc/nginx/scripts/nginx-watchdog.sh
# Daily backup at 2 AM
0 2 * * * sudo bash /etc/nginx/scripts/nginx-backup.sh >> /var/log/nginx-backup.log 2>&1
# Weekly SSL check on Mondays at 8 AM
0 8 * * 1 bash /etc/nginx/scripts/ssl-check.sh example.com other.com >> /var/log/ssl-check.log 2>&1
opencode AI Workflows
# Analyze error logs
sudo tail -200 /var/log/nginx/error.log | \
opencode "analyze these Nginx errors, group by type, identify root causes, give fixes"
# Generate server block
opencode "write a complete Nginx server block for WordPress at /var/www/example.com/public with PHP 8.4-FPM via UNIX socket, HTTPS redirect, security headers, rate limiting on /wp-login.php, and browser caching"
# Review config for issues
sudo nginx -T | opencode "review this Nginx config for security weaknesses and performance improvements"
# Debug 502 errors
(sudo tail -50 /var/log/nginx/error.log && sudo tail -50 /var/log/php8.4-fpm.log) | \
opencode "my Nginx returns 502 errors, diagnose these logs and give exact fixes"
# Generate PHP-FPM pool config
echo "Server RAM: $(free -m | awk 'NR==2{print $2}')MB" | \
opencode "recommend optimal PHP-FPM pool pm settings for this server for a WordPress site"
# Write a monitoring script
opencode "write a bash script that monitors Nginx every 5 minutes, restarts if down, sends webhook alert, and logs all events to /var/log/nginx-monitor.log"
tmux + opencode Layout
┌───────────────────────┬────────────────────┐
│ │ │
│ vim /etc/nginx/... │ opencode session │
│ (editing config) │ (AI help) │
│ │ │
├───────────────────────┴────────────────────┤
│ sudo tail -f /var/log/nginx/error.log │
│ (live error monitoring) │
└────────────────────────────────────────────┘
Edit → ask AI → check error log → reload → repeat.