Caddy, containers, and the case for boring infrastructure
Why a compact deployment stack is often the right trade when you want to spend more time building the application.
Give each layer one job
The public edge needs to terminate TLS, redirect HTTP, and forward requests. The application needs to answer application requests. Keeping those responsibilities separate makes both configurations easier to inspect.
Inside Compose, the application can stay on a private network. Caddy is the only service that publishes ports to the VPS, so there is no accidental second public entrypoint to secure.
blog.example.com {
encode zstd gzip
reverse_proxy web:3000
}Let the hostname be configuration
A reusable production stack should not bake a personal domain into the image. Pass the hostname as an environment value, keep it next to the deployment configuration, and let Caddy request the matching certificate.
DNS still has to point that hostname at the VPS. DNS selects the server; Caddy selects the container after the request arrives. Confusing those two boundaries is a common reason a healthy container remains invisible from the internet.
Persist the state that matters
The application image should be disposable. Caddy's data volume is different: it stores certificate material and must survive container recreation. Compose named volumes make that intent obvious.
Source code, build artifacts, and dependency caches can always be recreated. Production secrets and certificate state need deliberate ownership and backup decisions.
Boring is a feature
A compact stack has fewer places for configuration to disagree. It can be rebuilt from the repository, understood with standard logs, and operated with a handful of commands.
There is a point where a larger platform becomes the correct answer. Until the service actually reaches that point, boring infrastructure protects the scarce resource: attention.