feat(deploy): optional Caddy TLS reverse-proxy compose layer#60
Merged
Conversation
Production deployments now have a one-file overlay that puts Caddy
in front of the api:
sudo docker compose -f docker-compose.yml -f docker-compose.tls.yml up -d
What Caddy handles automatically:
- Let's Encrypt cert provisioning + renewal (ACME)
- HTTP → HTTPS redirect on :80
- HTTP/2 on :443 (and HTTP/3 via UDP :443)
- X-Forwarded-{For,Proto,Host} headers, plus X-Real-IP
- HSTS (Caddy default, max-age=31536000)
- gzip compression
- Structured access logs to stdout
The overlay rebinds the api service off the host port so Caddy is
the only thing reachable from outside the box — defense in depth
against accidentally exposing the api on the wrong interface.
Config is single-Caddyfile (caddy/Caddyfile) with two env vars:
- TLS_DOMAIN (required) — FQDN, or 'localhost' for self-signed
via Caddy's built-in CA (useful for local HSTS/cookie testing)
- TLS_EMAIL (optional) — passed to Let's Encrypt for renewal
notices
.env.example documents both. README gets a "Behind TLS (production)"
section + the bring-up one-liner.
Verified via \`docker compose -f docker-compose.yml -f
docker-compose.tls.yml config\` — the layered config parses
clean. Live ACME bring-up not done in this session (would need a
real FQDN pointing at this host).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CryptoJones
added a commit
that referenced
this pull request
May 18, 2026
Continues the docs-sync pattern from #44 and #53. CHANGELOG [Unreleased] now reflects: Added: - Sequelize associations (#54) - Integration test harness (#55) - Postman collection (#59) - TLS reverse-proxy compose (#60) - docker-compose.override.yml committed (#56) Fixed: - setup/TimeTracker.sql idempotency (#57) — removes the \`docker compose down -v\` workaround Docs: - Integration bring-up flow (#56, #58) - README sections for Testing and Behind TLS Co-authored-by: Aaron K. Clark <akclark@thenetwerk.net> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds opt-in
docker-compose.tls.yml+caddy/Caddyfileso production deployments can terminate TLS without manual cert wrangling. Caddy handles ACME, redirect, HSTS, X-Forwarded-* headers, and gzip out of the box..env.examplegetsTLS_DOMAIN+TLS_EMAILdocumented. README gets a "Behind TLS (production)" subsection.For local TLS dev (HSTS, secure-cookie testing): set
TLS_DOMAIN=localhostand Caddy uses its built-in self-signed CA — no ACME.Test plan
docker compose -f docker-compose.yml -f docker-compose.tls.yml configvalidates cleanapirebinds off the host port when layered with tls.yml (verified inconfigoutput)Proudly Made in Nebraska. Go Big Red! 🌽 https://xkcd.com/2347/