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

FileIn repo?Notes
hostinger/docker-compose.ymlYesTraefik labels + service definition
hostinger/claude-proxy-nginx.confYesnginx proxy config (must be kept in sync with server)
/root/claude-proxy-nginx.confServer onlyLive config — bind-mounted into container
/root/.envNoCLAUDE_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-proxy

Deployment

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: 200

Test 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

DataMethodScheduleDestination
nginx configGit repo (hostinger/claude-proxy-nginx.conf)On every pushGitHub radieu/p24-infra
Compose labels (Traefik routing)Git repo (hostinger/docker-compose.yml)On every pushGitHub radieu/p24-infra
CLAUDE_PROXY_SECRETGH Secret + .env.local on local workstationNot committed to repo
Container stateStateless — no persistent volumesN/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 restart

Scenario 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-proxy

Scenario 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-proxy

Estimated RTO: ~5 minutes.


Healthcheck / Monitoring

CheckMethodIntervalAlert
HTTPS endpoint availabilityBlackbox exporter HTTP probe30sEndpointDown alert
Container resource usagecAdvisor on vps-h1 (job cadvisor)15sContainerCrashLooping
Upstream API reachabilitynginx 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/health

Password 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.md

Dual-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

SymptomLikely causeFix
502 Bad Gatewayclaude-proxy container not runningdocker compose -f /root/docker-compose.yml up -d claude-proxy
401 UnauthorizedWrong or missing CLAUDE_PROXY_SECRET in callerVerify secret matches /root/.env value
403 from upstreamAnthropic API key invalid or revokedCheck API key in /root/.env; rotate if needed
nginx config syntax error on restartBad nginx.conf editdocker run --rm -v /root/claude-proxy-nginx.conf:/etc/nginx/conf.d/proxy.conf:ro nginx nginx -t
HTTPS not resolvingTraefik not running or TLS cert missingCheck root-traefik-1 status; verify acme.json
High latencyAnthropic API slow or vps-h1 load highCheck docker stats; check Anthropic status page