- Posted on
- • Artificial Intelligence
Artificial Intelligence Reverse Proxy Automation
- Author
-
-
- User
- linuxbash
- Posts by this author
- Posts by this author
-
Artificial Intelligence Reverse Proxy Automation (with Bash)
Tired of hand-editing reverse proxy configs every time you add or move a service? If your homelab or production stack changes weekly, your Nginx/Caddy rules, TLS, headers, and rate limits can turn into fragile snowflakes. What if a small Bash script could ask an AI to propose the right reverse-proxy config, validate it, and apply it safely with zero downtime?
This article shows how to automate reverse-proxy configuration with a guardrailed workflow that uses Bash, curl, jq, and an LLM endpoint (local or cloud). You’ll get:
A working pattern you can adapt to Nginx or Caddy
Step-by-step scripts for discovery → generation → validation → reload
Safe rollbacks and systemd timers
Installation commands for apt, dnf, and zypper
Why this matters
Reverse proxies are text-based DSLs. LLMs are excellent at text transformation when guided with strict prompts and validation.
Dynamic services change fast—humans make mistakes. Automated generation plus linting and dry-run checks reduce outage risk.
Bash-first approach: portable, transparent, and easy to integrate into existing Linux operations.
What we’ll build
- A directory
/etc/ai-proxycontaining:- An inventory of backends (JSON you control)
- A Bash script that: 1) Prompts an LLM to emit a reverse-proxy config 2) Extracts only the config, validates it, and reloads the proxy 3) Falls back safely if validation fails
- Optional systemd service/timer for periodic reconciliation
Use Nginx or Caddy (choose one). Both are covered.
Prerequisites and installation
Pick your proxy. Don’t run both on the same ports (80/443) at the same time unless you know what you’re doing.
Common tools (required for both):
curl
jq
Nginx stack:
nginx
certbot + nginx plugin (optional, if you want automated TLS via Certbot)
Caddy stack:
- caddy (has built-in automatic HTTPS via ACME/Let’s Encrypt)
Install with your package manager.
Apt (Debian/Ubuntu):
sudo apt update
# Common
sudo apt install -y curl jq
# Nginx + Certbot
sudo apt install -y nginx certbot python3-certbot-nginx
# OR Caddy
sudo apt install -y caddy
Dnf (Fedora/RHEL-derived; ensure EPEL on RHEL/CentOS for some packages if needed):
sudo dnf -y install curl jq
sudo dnf -y install nginx certbot python3-certbot-nginx
# OR
sudo dnf -y install caddy
Zypper (openSUSE/SLE; ensure appropriate repositories are enabled):
sudo zypper refresh
sudo zypper install -y curl jq
sudo zypper install -y nginx certbot python3-certbot-nginx
# OR
sudo zypper install -y caddy
Note:
- Caddy may grab ports 80/443 right away if enabled; stop or disable it if using Nginx during setup.
Step 1: Create an inventory of services
Keep a source of truth in JSON. You can start simple and expand as you go. Example:
/etc/ai-proxy/inventory.json
[
{
"server_name": "app.example.com",
"upstream": "127.0.0.1:5000",
"tls": true,
"rate_limit_rps": 20
},
{
"server_name": "grafana.example.com",
"upstream": "127.0.0.1:3000",
"tls": true,
"basic_auth": {
"user": "admin",
"htpasswd_file": "/etc/ai-proxy/htpasswd"
}
}
]
Optional: Create an htpasswd file for basic auth on some backends:
# Using openssl (available by default on most distributions)
printf "admin:$(openssl passwd -apr1 'supersecret')\n" | sudo tee /etc/ai-proxy/htpasswd >/dev/null
sudo chmod 640 /etc/ai-proxy/htpasswd
sudo chown root:root /etc/ai-proxy/htpasswd
Tip: You can also discover candidates with ss and manually map them to domains:
ss -lntp | awk 'NR>1 {print $4, $7}'
Step 2: Set up an LLM endpoint (local or cloud)
This guide uses a generic “OpenAI-compatible” HTTP API so it works with many providers (local and remote). Export environment variables:
export LLM_URL="https://api.openai.com" # Or your self-hosted endpoint base URL
export LLM_API_KEY="sk-..." # Or a local-model token if required
export LLM_MODEL="gpt-4o-mini" # Or the model your endpoint supports
If you’re using a local provider (e.g., open-source server exposing a compatible API), adjust LLM_URL, omit the API key if unnecessary, and set LLM_MODEL accordingly.
Step 3: AI-assisted config generation with guardrails
We’ll write one script and show Nginx-first. A Caddy version is included below.
/etc/ai-proxy/ai_gen_proxy_nginx.sh
#!/usr/bin/env bash
set -euo pipefail
INVENTORY="/etc/ai-proxy/inventory.json"
OUT_TXT="/etc/ai-proxy/llm_output.txt"
TMP_CONF="/etc/nginx/conf.d/ai-proxy.conf.tmp"
FINAL_CONF="/etc/nginx/conf.d/ai-proxy.conf"
LOG="/var/log/ai-proxy.log"
mkdir -p /etc/ai-proxy
touch "$LOG"
if [[ -z "${LLM_URL:-}" || -z "${LLM_MODEL:-}" ]]; then
echo "$(date -Is) [ERROR] LLM_URL and LLM_MODEL must be set" | tee -a "$LOG" >&2
exit 1
fi
SYSTEM_PROMPT="You are an expert Nginx config generator. Output ONLY a single fenced code block labeled nginx containing a valid Nginx server config using the provided JSON inventory.
Rules:
- Use server blocks for each entry with server_name and proxy_pass to upstream.
- If tls=true, assume certificates handled externally (do not include certbot commands). Listen on 443 ssl and redirect 80->443.
- Add sane defaults: proxy_set_header Host, X-Real-IP, X-Forwarded-For, timeouts.
- If basic_auth is provided, enable auth_basic and auth_basic_user_file.
- If rate_limit_rps provided, add a per-server zone and limit_req with burst=10 nodelay.
- NEVER emit comments or text outside the single nginx code block."
USER_PROMPT=$(jq -c . "$INVENTORY")
set +x
AUTH_HEADER=()
if [[ -n "${LLM_API_KEY:-}" ]]; then
AUTH_HEADER=(-H "Authorization: Bearer ${LLM_API_KEY}")
fi
RESP=$(curl -sS \
"${LLM_URL%/}/v1/chat/completions" \
-H "Content-Type: application/json" \
"${AUTH_HEADER[@]}" \
-d @- <<EOF
{
"model": "${LLM_MODEL}",
"messages": [
{"role":"system","content": ${SYSTEM_PROMPT@Q}},
{"role":"user","content": ${USER_PROMPT@Q}}
],
"temperature": 0.2
}
EOF
)
echo "$RESP" | jq -r '.choices[0].message.content' > "$OUT_TXT"
# Extract code block labeled nginx
awk '
/```nginx/ {inblock=1; next}
/```/ && inblock {inblock=0; next}
inblock {print}
' "$OUT_TXT" > "$TMP_CONF"
# Basic sanity check: must contain at least one server block
if ! grep -qE '^\s*server\s*\{' "$TMP_CONF"; then
echo "$(date -Is) [ERROR] No server block found in generated config" | tee -a "$LOG" >&2
exit 2
fi
# Validate Nginx config (temporary file is already in conf.d)
if ! nginx -t >/tmp/nginx-test.out 2>&1; then
echo "$(date -Is) [ERROR] nginx -t failed. See /tmp/nginx-test.out" | tee -a "$LOG" >&2
exit 3
fi
# Atomic move to final and reload
install -m 0644 "$TMP_CONF" "$FINAL_CONF"
systemctl reload nginx
echo "$(date -Is) [INFO] Applied and reloaded nginx successfully" | tee -a "$LOG"
Make it executable:
sudo mkdir -p /etc/ai-proxy
sudo chown root:root /etc/ai-proxy
sudo chmod 700 /etc/ai-proxy
sudo tee /etc/ai-proxy/ai_gen_proxy_nginx.sh >/dev/null <<'EOS'
# (paste the script above)
EOS
sudo chmod +x /etc/ai-proxy/ai_gen_proxy_nginx.sh
Run it once:
sudo /etc/ai-proxy/ai_gen_proxy_nginx.sh
If using Certbot with Nginx for TLS:
# Point DNS records to this host first.
sudo certbot --nginx -d app.example.com -d grafana.example.com
Nginx note: The AI will generate server blocks that assume certificates are in place (or that you’re handling TLS externally). Let the first run create HTTP-only or redirect blocks, then use Certbot to obtain and insert cert paths. Subsequent AI updates will keep the structure.
Step 3 (Caddy alternative): AI-assisted Caddyfile generation
Caddy has native HTTPS automation. Swap in this script if you prefer Caddy.
/etc/ai-proxy/ai_gen_proxy_caddy.sh
#!/usr/bin/env bash
set -euo pipefail
INVENTORY="/etc/ai-proxy/inventory.json"
OUT_TXT="/etc/ai-proxy/llm_output.txt"
TMP_CONF="/etc/caddy/Caddyfile.tmp"
FINAL_CONF="/etc/caddy/Caddyfile"
LOG="/var/log/ai-proxy.log"
mkdir -p /etc/ai-proxy
touch "$LOG"
if [[ -z "${LLM_URL:-}" || -z "${LLM_MODEL:-}" ]]; then
echo "$(date -Is) [ERROR] LLM_URL and LLM_MODEL must be set" | tee -a "$LOG" >&2
exit 1
fi
SYSTEM_PROMPT="You are an expert Caddyfile generator. Output ONLY a single fenced code block labeled caddy containing a valid Caddyfile using the provided JSON inventory.
Rules:
- For each entry, create a site block on server_name with reverse_proxy to upstream.
- Enforce HTTPS (Caddy will manage certificates automatically).
- Add common headers (X-Forwarded-For, X-Forwarded-Proto).
- If basic_auth is provided, use basicauth with hashed password file if possible; otherwise, skip auth.
- If rate_limit_rps provided, use Caddy rate limiting via route and rate_limit directive (or note unsupported if version lacks it).
- NEVER emit comments or text outside the single caddy code block."
USER_PROMPT=$(jq -c . "$INVENTORY")
set +x
AUTH_HEADER=()
if [[ -n "${LLM_API_KEY:-}" ]]; then
AUTH_HEADER=(-H "Authorization: Bearer ${LLM_API_KEY}")
fi
RESP=$(curl -sS \
"${LLM_URL%/}/v1/chat/completions" \
-H "Content-Type: application/json" \
"${AUTH_HEADER[@]}" \
-d @- <<EOF
{
"model": "${LLM_MODEL}",
"messages": [
{"role":"system","content": ${SYSTEM_PROMPT@Q}},
{"role":"user","content": ${USER_PROMPT@Q}}
],
"temperature": 0.2
}
EOF
)
echo "$RESP" | jq -r '.choices[0].message.content' > "$OUT_TXT"
awk '
/```caddy/ {inblock=1; next}
/```/ && inblock {inblock=0; next}
inblock {print}
' "$OUT_TXT" > "$TMP_CONF"
# Validate Caddyfile
if ! caddy validate --adapter caddyfile --config "$TMP_CONF" >/tmp/caddy-validate.out 2>&1; then
echo "$(date -Is) [ERROR] caddy validate failed. See /tmp/caddy-validate.out" | tee -a "$LOG" >&2
exit 3
fi
install -m 0644 "$TMP_CONF" "$FINAL_CONF"
# Prefer caddy reload for seamless updates
if caddy reload --adapter caddyfile --config "$FINAL_CONF" >/tmp/caddy-reload.out 2>&1; then
echo "$(date -Is) [INFO] Applied and reloaded caddy successfully" | tee -a "$LOG"
else
# Fallback to systemd reload
systemctl reload caddy
echo "$(date -Is) [INFO] Applied and systemd-reloaded caddy" | tee -a "$LOG"
fi
Make executable:
sudo tee /etc/ai-proxy/ai_gen_proxy_caddy.sh >/dev/null <<'EOS'
# (paste the script above)
EOS
sudo chmod +x /etc/ai-proxy/ai_gen_proxy_caddy.sh
Run it once:
sudo /etc/ai-proxy/ai_gen_proxy_caddy.sh
Step 4: Automate with systemd
Create a service and timer that periodically reconciles config. Point ExecStart at your chosen script (Nginx or Caddy variant).
/etc/systemd/system/ai-proxy.service
[Unit]
Description=AI Reverse Proxy Reconciler
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
Environment=LLM_URL=https://api.openai.com
Environment=LLM_MODEL=gpt-4o-mini
# Environment=LLM_API_KEY=sk-... # Better: put in an EnvironmentFile with correct permissions
ExecStart=/etc/ai-proxy/ai_gen_proxy_nginx.sh
# OR for Caddy:
# ExecStart=/etc/ai-proxy/ai_gen_proxy_caddy.sh
User=root
Group=root
/etc/systemd/system/ai-proxy.timer
[Unit]
Description=Run AI Reverse Proxy Reconciler every 30 minutes
[Timer]
OnBootSec=2m
OnUnitActiveSec=30m
Persistent=true
[Install]
WantedBy=timers.target
Enable and start:
sudo systemctl daemon-reload
sudo systemctl enable --now ai-proxy.timer
sudo systemctl list-timers | grep ai-proxy
Keep secrets out of unit files:
sudo bash -c 'cat > /etc/sysconfig/ai-proxy <<EOF
LLM_URL=https://api.openai.com
LLM_MODEL=gpt-4o-mini
LLM_API_KEY=sk-...
EOF'
sudo chmod 600 /etc/sysconfig/ai-proxy
sudo sed -i 's|^Environment=.*||' /etc/systemd/system/ai-proxy.service
echo 'EnvironmentFile=/etc/sysconfig/ai-proxy' | sudo tee -a /etc/systemd/system/ai-proxy.service
sudo systemctl daemon-reload
Step 5: Real-world patterns to try
Canary/blue-green with headers
- Ask the AI to add a location that routes traffic with
X-Canary: 1to an alternate upstream. Validate and roll forward gradually.
- Ask the AI to add a location that routes traffic with
Path-based multi-tenant routing
/api/to one upstream,/ui/to another. EnsureX-Forwarded-Prefixis set if your app needs it.
Basic auth on internal tools
- Protect Grafana or admin panels. Store htpasswd securely.
Rate limiting on noisy endpoints
- Per-server or location-based
limit_req(Nginx) or Caddy’s limiter (version-dependent).
- Per-server or location-based
Strict security headers
- HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy. Have the AI include them, then verify with securityheaders.com or curl -I.
Examples (Nginx snippets the AI is likely to produce):
server {
listen 80;
server_name app.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name app.example.com;
# ssl_certificate /etc/letsencrypt/live/app.example.com/fullchain.pem;
# ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem;
add_header Strict-Transport-Security "max-age=31536000" always;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
location / {
proxy_pass http://127.0.0.1:5000;
}
}
Safety notes and debugging
Never trust generated config blindly. Always:
- Validate:
nginx -torcaddy validate - Dry-run reload (handled in scripts)
- Keep logs:
/var/log/ai-proxy.log,/tmp/nginx-test.out,/tmp/caddy-validate.out
- Validate:
Scope the AI: Strong system prompts reduce surprises.
Principle of least privilege: Avoid exposing internal-only services publicly unless intended.
Rollback plan: If generation fails, the script won’t replace the existing config.
Troubleshooting quick hits
Port conflicts: Make sure only one of Nginx or Caddy is bound to 80/443.
SELinux/AppArmor: On Fedora/RHEL/SUSE, ensure proxy can read configs and any htpasswd files.
DNS/TLS: Point A/AAAA records to your host before enabling HTTPS. With Certbot (Nginx), run
certbot --nginx .... With Caddy, it will request certificates automatically when domains resolve correctly.
Conclusion / Call to Action
Reverse proxy configs are perfect for AI-assisted automation: they’re structured, repetitive, and testable. Wrap an LLM with strict prompts, validate the output, and reload safely. Start small—one domain—and expand.
Next steps:
Choose Nginx or Caddy and run the scripts once
Move your inventory to source control
Add more policies (WAF rules, headers, canaries)
Swap in your preferred LLM provider or local model
If this helped, share your experience or improvements. What did your AI-generated config fix that surprised you?