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-proxy containing:
    • 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: 1 to an alternate upstream. Validate and roll forward gradually.
  • Path-based multi-tenant routing

    • /api/ to one upstream, /ui/ to another. Ensure X-Forwarded-Prefix is 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).
  • 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 -t or caddy validate
    • Dry-run reload (handled in scripts)
    • Keep logs: /var/log/ai-proxy.log, /tmp/nginx-test.out, /tmp/caddy-validate.out
  • 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?