# caddy/ (episode 10, extended in episode 12) Custom Caddy image with the rate limiting plugin, the CrowdSec bouncer and the CrowdSec AppSec handler compiled in (see `crowdsec/README.md`). The stock `caddy:2-alpine` has no `rate_limit` directive; it is not a runtime flag, the module must be built into the binary with `xcaddy`. `compose.yml` carries both `image:` and `build: ./caddy`, so `docker compose build caddy` produces `selfhostedops/caddy:crowdsec` locally and `docker compose up -d` uses it. Do not build on a small box while the stack is running - a Go build can push a 2 GB machine into OOM and take the live services down with it. Build it elsewhere (a bigger machine, or a throwaway host) and move it over: docker build -t selfhostedops/caddy:crowdsec ./caddy docker save selfhostedops/caddy:crowdsec | ssh root@box docker load Measured in episode 12: the build with all three modules took 60 s on a 12 GB build host, the image is 158 MB (the rate-limit-only image was 154 MB). The modules are pinned to the release tag (`@v0.14.1`), not `@main` as the upstream README suggests, so a rebuild next month gives the same binary.