Claude Proxy Router — Operations Workbook
nginx-based Claude API proxy on Hostinger VPS (vps-h1). Routes Claude API requests from internal services and agents, adding authentication via a shared secret. TLS termination via Traefik.
Architecture
Hostinger VPS (72.60.32.61)
└── Container: claude-proxy image: nginx:alpine (or custom)
├── port 80 (internal, Traefik routes :443 → :80 via Docker network)
├── nginx config: /root/claude-proxy-nginx.conf (bind-mounted into container)
└── env: CLAUDE_PROXY_SECRET shared bearer token for inbound auth
Traefik (root-traefik-1)
└── Host(`claude-proxy.vps-h1.infra.zintegrowana.online`) → claude-proxy :80
Public URL: https://claude-proxy.vps-h1.infra.zintegrowana.online
Auth: Authorization: Bearer <CLAUDE_PROXY_SECRET> (all inbound requests)
Upstream: https://api.anthropic.com (all /v1/* requests forwarded)Compose file on server: /root/docker-compose.yml (claude-proxy service defined here)
Compose file in repo: hostinger/docker-compose.yml
nginx config on server: /root/claude-proxy-nginx.conf
nginx config in repo: hostinger/claude-proxy-nginx.conf (should be kept in sync — see Config Management)
Config Management
| File | In repo? | Notes |
|---|---|---|
hostinger/docker-compose.yml | Yes | Traefik labels + service definition |
hostinger/claude-proxy-nginx.conf | Yes | nginx proxy config (must be kept in sync with server) |
/root/claude-proxy-nginx.conf | Server only | Live config — bind-mounted into container |
/root/.env | No | CLAUDE_PROXY_SECRET and other secrets |
Apply nginx config change
# Edit hostinger/claude-proxy-nginx.conf → commit → on vps-h1:
git pull
cp /opt/p24-infra/hostinger/claude-proxy-nginx.conf /root/claude-proxy-nginx.conf
# Reload nginx without restart (zero downtime)
docker compose -f /root/docker-compose.yml exec claude-proxy nginx -s reload
# Or full restart if reload not supported
docker compose -f /root/docker-compose.yml restart claude-proxyDeployment
Fresh install
# On vps-h1 as root
# 1. Ensure .env has CLAUDE_PROXY_SECRET set
# 2. Place nginx config
cp /opt/p24-infra/hostinger/claude-proxy-nginx.conf /root/claude-proxy-nginx.conf
# 3. Start service (Traefik must already be running)
docker compose -f /root/docker-compose.yml up -d claude-proxy
# 4. Verify
curl -o /dev/null -s -w "%{http_code}" \
-H "Authorization: Bearer ${CLAUDE_PROXY_SECRET}" \
https://claude-proxy.vps-h1.infra.zintegrowana.online/health
# Expected: 200Test proxy routing
# Minimal Claude API call through proxy
curl https://claude-proxy.vps-h1.infra.zintegrowana.online/v1/messages \
-H "Authorization: Bearer ${CLAUDE_PROXY_SECRET}" \
-H "Content-Type: application/json" \
-d '{"model":"claude-haiku-4-5","max_tokens":10,"messages":[{"role":"user","content":"ping"}]}'Backup
| Data | Method | Schedule | Destination |
|---|---|---|---|
| nginx config | Git repo (hostinger/claude-proxy-nginx.conf) | On every push | GitHub radieu/p24-infra |
| Compose labels (Traefik routing) | Git repo (hostinger/docker-compose.yml) | On every push | GitHub radieu/p24-infra |
CLAUDE_PROXY_SECRET | GH Secret + .env.local on local workstation | — | Not committed to repo |
| Container state | Stateless — no persistent volumes | — | N/A |
Backup = config fully in git. No persistent data — restore is copy config + compose up.
Restore
Scenario 1: Container crash
docker compose -f /root/docker-compose.yml up -d claude-proxy
# nginx config bind-mounted from /root/claude-proxy-nginx.conf — survives restartScenario 2: Config file lost from server
# Restore from repo
git -C /opt/p24-infra pull
cp /opt/p24-infra/hostinger/claude-proxy-nginx.conf /root/claude-proxy-nginx.conf
docker compose -f /root/docker-compose.yml up -d claude-proxyScenario 3: Full vps-h1 rebuild
# After OS provision + Docker install (see hostinger-runbook.md):
# 1. Clone repo
git clone https://github.com/radieu/p24-infra /opt/p24-infra
# 2. Copy compose file
cp /opt/p24-infra/hostinger/docker-compose.yml /root/docker-compose.yml
# 3. Copy nginx config
cp /opt/p24-infra/hostinger/claude-proxy-nginx.conf /root/claude-proxy-nginx.conf
# 4. Restore .env from local workstation
scp -i C:\Users\konar\.ssh\id_ed25519 \
.env.local root@72.60.32.61:/root/.env
# 5. Start Traefik first, then claude-proxy
docker compose -f /root/docker-compose.yml up -d traefik
docker compose -f /root/docker-compose.yml up -d claude-proxyEstimated RTO: ~5 minutes.
Healthcheck / Monitoring
| Check | Method | Interval | Alert |
|---|---|---|---|
| HTTPS endpoint availability | Blackbox exporter HTTP probe | 30s | EndpointDown alert |
| Container resource usage | cAdvisor on vps-h1 (job cadvisor) | 15s | ContainerCrashLooping |
| Upstream API reachability | nginx upstream health (passive — error log) | — | No dedicated alert |
Manual health check:
# HTTPS check (no auth — just checks Traefik + nginx are up)
curl -o /dev/null -s -w "%{http_code}" \
https://claude-proxy.vps-h1.infra.zintegrowana.online/health
# Authenticated check
curl -o /dev/null -s -w "%{http_code}" \
-H "Authorization: Bearer ${CLAUDE_PROXY_SECRET}" \
https://claude-proxy.vps-h1.infra.zintegrowana.online/health
# Container status on vps-h1
ssh root@72.60.32.61 "docker inspect claude-proxy --format '{{.State.Status}}'"Blackbox probe config: Add to monitoring/prometheus/blackbox.yml:
# module: http_2xx
# target: https://claude-proxy.vps-h1.infra.zintegrowana.online/healthPassword Rotation
CLAUDE_PROXY_SECRET
Rotation frequency: 90 days. Last rotated: see docs/secrets-rotation-log.md.
All callers (local agents, VPS agents, n8n workflows) must be updated atomically. Plan a brief maintenance window or use a dual-secret grace period in nginx config.
# 1. Generate new secret
NEW_SECRET=$(openssl rand -hex 32)
# 2. Update /root/.env on vps-h1
ssh root@72.60.32.61 \
"sed -i 's/CLAUDE_PROXY_SECRET=.*/CLAUDE_PROXY_SECRET=${NEW_SECRET}/' /root/.env"
# 3. Restart claude-proxy to pick up new secret
docker compose -f /root/docker-compose.yml restart claude-proxy
# 4. Update .env.local on local workstation
# 5. Update GH Secret
gh secret set CLAUDE_PROXY_SECRET -b "${NEW_SECRET}" -R radieu/p24-infra
# 6. Update any n8n credentials or agent configs that use the old secret
# (n8n: Settings → Credentials → Claude Proxy Auth → update bearer token)
# 7. Verify callers work with new secret
curl -o /dev/null -s -w "%{http_code}" \
-H "Authorization: Bearer ${NEW_SECRET}" \
https://claude-proxy.vps-h1.infra.zintegrowana.online/health
# 8. Log rotation in docs/secrets-rotation-log.mdDual-secret transition (zero-downtime): temporarily configure nginx to accept both old and new secret, deploy, update all callers, then remove old secret and restart. This avoids a hard cutover window.
Upstream Anthropic API key
The proxy forwards requests to api.anthropic.com. If an ANTHROPIC_API_KEY is injected by the proxy (rather than passed through by callers), rotate it in /root/.env + GH Secret ANTHROPIC_API_KEY and restart claude-proxy. If callers supply their own key in the forwarded request, no action needed at proxy level.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| 502 Bad Gateway | claude-proxy container not running | docker compose -f /root/docker-compose.yml up -d claude-proxy |
| 401 Unauthorized | Wrong or missing CLAUDE_PROXY_SECRET in caller | Verify secret matches /root/.env value |
| 403 from upstream | Anthropic API key invalid or revoked | Check API key in /root/.env; rotate if needed |
| nginx config syntax error on restart | Bad nginx.conf edit | docker run --rm -v /root/claude-proxy-nginx.conf:/etc/nginx/conf.d/proxy.conf:ro nginx nginx -t |
| HTTPS not resolving | Traefik not running or TLS cert missing | Check root-traefik-1 status; verify acme.json |
| High latency | Anthropic API slow or vps-h1 load high | Check docker stats; check Anthropic status page |