TL;DR
- App listens on
127.0.0.1:4001 with GET /__app_ok returning 200.
- Sidecar NGINX listens on
0.0.0.0:2001 with GET /__gw_ok returning 200 and proxies to the app.
- App + sidecar join external Docker network
net_test; sidecar is not published to the host.
- Test-proxy routes
appX.* to http://appX:2001.
- Gateway forwards to
http://127.0.0.1:7777.
Directory Snapshot (context)
gateway/
sites-enabled/
test-proxy-gateway.conf # forwards to 127.0.0.1:7777
testproxy/
nginx/
nginx.conf
sites-enabled/
test-wildcard.conf # maps appX.* to http://appX:2001
logs/
docker-compose.yml
apps/
appX/
app/nginx.conf # 127.0.0.1:4001 + /__app_ok
sidecar/gw.conf # 0.0.0.0:2001 + /__gw_ok -> 127.0.0.1:4001
docker-compose.yml # app + sidecar join net_test
networks/README.md # name: net_test (external)
A) Add a New App (Sidecar Pattern)
Prep
- App listens on
127.0.0.1:4001 and serves GET /__app_ok.
- Sidecar listens on
0.0.0.0:2001 and serves GET /__gw_ok.
- Sidecar
proxy_pass http://127.0.0.1:4001.
- App + sidecar attach to external Docker network
net_test.
Compose up
docker compose up -d in the app directory.
docker inspect <app> <sidecar> and confirm net_test is attached.
Health checks
- From sidecar:
curl -s http://127.0.0.1:4001/__app_ok -> 200.
- From test-proxy:
curl -s http://appX:2001/__gw_ok -> 200.
Observability
- Sidecar access and error logs exist and are readable.
Rollback
docker compose down in the app directory.
B) Integrate App into Test-Proxy
VHost mapping
- Update
testproxy/nginx/sites-enabled/test-wildcard.conf:
- Add
server_name for the app domain(s).
- Add
proxy_pass http://appX:2001;.
- Keep
location = /__nginx_ok { return 200 'ok'; }.
Validate config
- Ensure there is a single include path (no nested
conf.d/conf.d).
docker exec -it testproxy nginx -t returns OK.
Reload and test
- Recreate or reload the test-proxy container.
curl -H "Host: appX.test.privsec.ch" http://127.0.0.1:7777/__nginx_ok -> 200.
curl -H "Host: appX.test.privsec.ch" http://127.0.0.1:7777/ -> expected 2xx.
Logs
- Tail
testproxy/logs/access.log and testproxy/logs/error.log.
Rollback
- Remove vhost block,
nginx -t, reload.
C) Integrate App at Main Gateway (host)
Server blocks
server_name appX.test.privsec.ch;
:80 -> ACME location + 301 to HTTPS.
:443 -> correct cert + HSTS.
- Forward to test-proxy:
proxy_pass http://127.0.0.1:7777;.
- Preserve headers:
Host, X-Forwarded-*.
Security defaults
- Unknown hosts:
:80 return 444, :443 ssl_reject_handshake.
- If needed:
set_real_ip_from and real_ip_recursive on.
Reload and test
nginx -t && systemctl reload nginx.
curl -I https://appX.test.privsec.ch/ -> expected 200/301/302.
Logs
- Review host access/error logs for upstream status and TLS issues.
Rollback
- Remove vhost, reload, and verify blackhole defaults.
D) Config Change Checklist (any layer)
- Single include path, no duplicate
conf.d.
- Only one
log_format main definition.
- Real IP trust matches upstream hop(s).
- Health endpoints return 200 (
/__app_ok, /__gw_ok, /__nginx_ok).
- Ports consistent: App=4001, Sidecar=2001, Test-proxy=7777.
nginx -t clean; reload without warnings.
E) Promotion Checklist (Test -> Production)
- A/B/C/D checklists pass in test.
- Gateway vhost points to
127.0.0.1:7777.
- TLS cert valid + renewal path documented.
- Monitoring and alerts configured (error rate, p95 latency).
- Backout plan ready (disable vhost; blackhole defaults active).