Optimizations: switch to static-web-server + Caddy reverse proxy docs #1

Merged
ahmza merged 4 commits from optimizations into main 2026-07-25 00:19:54 +00:00
Owner

Summary

Infrastructure optimizations for the blog serving stack, plus documentation for fronting it with Caddy.

Changes

Switch static server: nginx → static-web-server

  • Replaced nginx:alpine-slim with the scratch-based ghcr.io/static-web-server/static-web-server:2 image.
  • Image size: 32.3 MB → 23.1 MB (~28% / 9.2 MB smaller).
  • Added sws.toml mirroring the previous nginx behavior (cache-control, SPA index fallback) plus a /health endpoint.
  • Removed the now-unused nginx.conf.

Tune SWS for running behind a reverse proxy

  • Disabled compression / compression-static (the upstream proxy compresses — avoids double work).
  • Disabled security-headers (the proxy owns them — avoids duplicate/conflicting headers).
  • Kept cache-control-headers, page-fallback, and health as origin concerns.

Add Caddy reverse proxy documentation

  • New docs/caddy-reverse-proxy.md covering both deployment targets (Docker Compose on Gimli and the Kubernetes Pi cluster).
  • Uses the custom caddy-cf image with the Cloudflare DNS-01 challenge, so TLS certs are issued without exposing ports 80/443 publicly (suits the Netbird-only infra).
  • Documents the CF_API_TOKEN handling (.env for Docker, Secret for K8s), token scope (Zone:DNS:Edit), and the layer separation between Caddy (TLS/compression/headers) and SWS (static files/cache-control).

Verification

  • Both SWS configs built and smoke-tested in a container: homepage/posts 200, cache-control on assets, SPA fallback, /health, and compression/security-headers correctly off behind the proxy.
  • The Caddyfile was validated against the actual caddy-cf image (caddy adapt produced valid JSON; the ACME/Cloudflare DNS issuer wired correctly).

Notes

  • The README GitLab→Gitea fix is intentionally not in this PR; it remains a separate change on main.
## Summary Infrastructure optimizations for the blog serving stack, plus documentation for fronting it with Caddy. ## Changes ### Switch static server: nginx → static-web-server - Replaced `nginx:alpine-slim` with the scratch-based `ghcr.io/static-web-server/static-web-server:2` image. - **Image size: 32.3 MB → 23.1 MB (~28% / 9.2 MB smaller).** - Added `sws.toml` mirroring the previous nginx behavior (cache-control, SPA index fallback) plus a `/health` endpoint. - Removed the now-unused `nginx.conf`. ### Tune SWS for running behind a reverse proxy - Disabled `compression` / `compression-static` (the upstream proxy compresses — avoids double work). - Disabled `security-headers` (the proxy owns them — avoids duplicate/conflicting headers). - Kept `cache-control-headers`, `page-fallback`, and `health` as origin concerns. ### Add Caddy reverse proxy documentation - New `docs/caddy-reverse-proxy.md` covering both deployment targets (Docker Compose on Gimli and the Kubernetes Pi cluster). - Uses the custom [`caddy-cf`](https://github.com/ahmzaa/caddy-cf) image with the **Cloudflare DNS-01 challenge**, so TLS certs are issued without exposing ports 80/443 publicly (suits the Netbird-only infra). - Documents the `CF_API_TOKEN` handling (`.env` for Docker, `Secret` for K8s), token scope (`Zone:DNS:Edit`), and the layer separation between Caddy (TLS/compression/headers) and SWS (static files/cache-control). ## Verification - Both SWS configs built and smoke-tested in a container: homepage/posts 200, cache-control on assets, SPA fallback, `/health`, and compression/security-headers correctly off behind the proxy. - The Caddyfile was validated against the actual `caddy-cf` image (`caddy adapt` produced valid JSON; the ACME/Cloudflare DNS issuer wired correctly). ## Notes - The README GitLab→Gitea fix is intentionally **not** in this PR; it remains a separate change on `main`.
ahmza added 4 commits 2026-07-25 00:18:29 +00:00
Replace nginx:alpine-slim with the scratch-based static-web-server
image, reducing the final image size ~32MB -> ~23MB (28% smaller).

- Dockerfile: serve from static-web-server:2, copy site to /public
- sws.toml: mirror previous nginx behavior (compression, cache-control,
  SPA index fallback) plus security headers and /health endpoint
- Remove now-unused nginx.conf
The site sits behind a reverse proxy that already handles compression
and security headers. Disable those in SWS to avoid redundant work and
duplicate/conflicting headers, letting each layer own one concern.

- compression / compression-static: off (proxy compresses)
- security-headers: off (proxy sets them)
- cache-control-headers, page-fallback, health: kept (origin concerns)
Document running Caddy in front of the SWS origin for both deployment
targets (Docker Compose on Gimli and the Kubernetes Pi cluster), with
Caddy handling automatic HTTPS, compression, and security headers.

Explains the layer separation: SWS serves static files + Cache-Control,
Caddy owns TLS/compression/security headers (matching sws.toml where
those are disabled). Caddyfile validated with 'caddy validate'.
Switch the Caddy doc to the custom caddy-cf image
(ghcr.io/ahmzaa/caddy-cf) which bundles the Cloudflare DNS module, so
TLS certs are issued via the ACME DNS-01 challenge instead of HTTP-01.
This removes the need to expose ports 80/443 to the public internet,
which suits the Netbird-only infrastructure.

- Add (cloudflare) snippet + import cloudflare to both Caddyfiles
- Read CF_API_TOKEN from env (.env for Docker, Secret for K8s)
- Document token scope (Zone:DNS:Edit) and Cloudflare-hosted DNS
  requirement
- Validated Caddyfile against the caddy-cf image (caddy adapt)
ahmza merged commit aa01def998 into main 2026-07-25 00:19:54 +00:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: ahmza/blog-ahmza-com#1