Plan: Split nginx.conf into modular config files¶
Status: DONE¶
Current Phase: All phases complete¶
Last Updated: 2026-03-17 00:01¶
Problem¶
nginx/nginx.conf is 830 lines — a single monolith containing shared http settings, CORS maps, upstream definitions, rate-limiting, logging, gzip, and both the production and development server blocks. Any change to one environment risks breaking the other, and the file far exceeds the 200-line limit.
Goal¶
Split into 3 files:
| File | Contents | ~Lines |
|---|---|---|
nginx/nginx.conf |
events, http block with shared settings (maps, upstreams, rate-limits, logging, gzip, HTTP→HTTPS redirect), include conf.d/*.conf; |
~140 |
nginx/conf.d/prod.conf |
Production HTTPS server block (api.indoxhub.com on 443) |
~380 |
nginx/conf.d/dev.conf |
Development HTTPS server block (dev-api.indoxhub.com on 443) |
~310 |
What stays where¶
Stays in nginx/nginx.conf (shared)¶
events {}block (lines 4–8)client_max_body_size,keepalive_*(lines 12–16)- All
mapblocks:$indoxrouter_prod_origin,$indoxrouter_dev_origin,$dev_localhost_origin,$indoxrouter_dev_combined,$service_name(lines 21–119) - Both
upstreamblocks:indoxrouter_backend,indoxrouter_backend_dev(lines 65–77) resolver(line 80)limit_req_zonedirectives (lines 83–84)log_format,access_log,error_log(lines 87–95)gzipblock (lines 98–112)- HTTP→HTTPS redirect
server {}on port 80 (lines 122–137) — serves both domains - New directive:
include /etc/nginx/conf.d/*.conf;
Moves to nginx/conf.d/prod.conf¶
- The entire
server { listen 443 ssl; server_name api.indoxhub.com; ... }block (lines 140–519) - Includes: SSL config, security headers, /health, /status, /static/, /mobile-auth-test, /admin/ (IP-restricted), /v1/, /api/v1/, catch-all /
- All
@indoxrouter_*named locations (down, api_down, admin_down, static_fallback)
Moves to nginx/conf.d/dev.conf¶
- The entire
server { listen 443 ssl; server_name dev-api.indoxhub.com; ... }block (lines 522–828) - Includes: SSL config, security headers, /health, /status, /static/, /v1/, /api/v1/, catch-all /
- All
@indoxrouter_dev_*named locations - Note: dev has no /admin/ block or /mobile-auth-test — key structural difference from prod
Files affected¶
| Action | File | Change |
|---|---|---|
| Rewrite | nginx/nginx.conf |
Remove both 443 server blocks, add include |
| Create | nginx/conf.d/prod.conf |
Prod HTTPS server block |
| Create | nginx/conf.d/dev.conf |
Dev HTTPS server block |
| Modify | nginx/Dockerfile |
Add RUN rm -f /etc/nginx/conf.d/default.conf + COPY conf.d/ /etc/nginx/conf.d/ |
| Modify | nginx/docker-compose.yml |
Add volume ./conf.d/:/etc/nginx/conf.d/:ro |
| Modify | .github/workflows/NGINX_deploy.yml |
Copy conf.d/ alongside nginx.conf |
Not touched¶
docker/local/nginx-local.conf— separate local config, unrelatedDEV_restart_nginx.yml,PROD_restart_nginx.yml— restart-only workflows, no file copy
Phases¶
- [x] Phase 1: Create
nginx/conf.d/prod.confandnginx/conf.d/dev.conf - Extract prod server block (lines 140–519) →
nginx/conf.d/prod.conf - Extract dev server block (lines 522–828) →
nginx/conf.d/dev.conf - Each file is a standalone
server { ... }block (no wrappinghttp { }) -
Original
nginx.confuntouched in this phase -
[x] Phase 2: Rewrite
nginx/nginx.confas shared-only - Keep: events, http shell, maps, upstreams, resolver, rate-limit zones, logging, gzip, port-80 redirect server
- Remove: both 443 server blocks
-
Add:
include /etc/nginx/conf.d/*.conf;at the end of thehttp {}block (before closing}) -
[x] Phase 3: Update Dockerfile, docker-compose.yml, and CI workflow
nginx/Dockerfile:- Add
RUN rm -f /etc/nginx/conf.d/default.conf(remove alpine default) - Add
COPY conf.d/ /etc/nginx/conf.d/
- Add
nginx/docker-compose.yml:- Add volume:
./conf.d/:/etc/nginx/conf.d/:ro
- Add volume:
-
.github/workflows/NGINX_deploy.yml: -
[x] Phase 4: Validate
- Verify all files are within line limits
- Verify no logic changes by comparing the resulting concatenated config with the original
nginx -tsyntax check (same as CI does)- Confirm the workflow trigger path
nginx/**already coversconf.d/changes (it does)
Risks and mitigations¶
| Risk | Mitigation |
|---|---|
include *.conf loads alphabetically — order could matter |
nginx server blocks match by server_name, not file order. Safe. |
nginx:alpine ships with /etc/nginx/conf.d/default.conf |
Add RUN rm -f /etc/nginx/conf.d/default.conf in Dockerfile before our COPY |
CI deploy must create conf.d/ on the host |
Add mkdir -p /opt/router-nginx/conf.d in the deploy step |
Shared map / upstream blocks referenced by both envs |
Keep them in nginx.conf — they're http-level directives, visible to all included files |
Notes¶
- This is a pure structural refactor — zero functional changes to routing, CORS, SSL, or upstream behavior.
- The HTTP→HTTPS redirect server (port 80) handles both
api.indoxhub.comanddev-api.indoxhub.comin one block — stays in the shared file. - The
docker-compose.ymlvolume mount forconf.d/means changes to individual env configs can be hot-tested without rebuilding the image.