Skip to content

Docker & Caddy

Everything-in-Docker stack on pwa-host

flowchart TB
  Net((:80/:443)) --> Caddy[fatbot-caddy<br/>caddy:alpine]
  Caddy -->|host.docker.internal:3030| Web[fruitplug-web-dev]
  Caddy -->|host.docker.internal:3040| Docs[fruitplug-docs]
  Caddy -->|host.docker.internal:8000| FB[fatbot-dashboard]
  Caddy -->|host.docker.internal:8001| FBD[fatbot-docs]
  Caddy -->|host.docker.internal:8050| FBF[footballdesk-app]

One Caddy, multiple apps, one cert manager.

Caddyfile

Bind-mounted into the fatbot-caddy container at /etc/caddy/Caddyfile from:

C:\Users\User\Desktop\Caddy\Caddyfile

Current contents:

fatbot.ai, www.fatbot.ai {
    reverse_proxy host.docker.internal:8000
}

docs.fatbot.ai {
    reverse_proxy host.docker.internal:8001
}

football.fatbot.ai {
    reverse_proxy host.docker.internal:8050
}

dev.fruitplug.co.uk {
    reverse_proxy host.docker.internal:3030
}

docs.fruitplug.co.uk {
    reverse_proxy host.docker.internal:3040
}

Editing + reloading

# Edit
notepad C:\Users\User\Desktop\Caddy\Caddyfile

# Validate (always first!)
MSYS_NO_PATHCONV=1 docker exec fatbot-caddy \
  caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile

# Hot reload (zero-downtime)
MSYS_NO_PATHCONV=1 docker exec fatbot-caddy \
  caddy reload --config /etc/caddy/Caddyfile --adapter caddyfile

MSYS_NO_PATHCONV

Git Bash on Windows rewrites /etc/caddy/... into C:\Program Files\Git\etc\caddy\.... Always prefix docker exec with MSYS_NO_PATHCONV=1.

Containers we manage

fruitplug-web-dev (the PWA)

docker ps --filter name=fruitplug-web-dev
docker logs -f fruitplug-web-dev
docker restart fruitplug-web-dev

# rebuild image after package.json changes
docker build -f infra/dev.Dockerfile -t fruitplug-web:dev .
# then restart the container — see infra/docker-compose.prod.yml for the
# canonical `docker run` flags including bind mounts + env vars

Image: fruitplug-web:dev (built from infra/dev.Dockerfile). Bind-mounts the entire apps/web directory + packages/ for hot reload with webpack polling (WATCHPACK_POLLING=true, poll: 800 in next.config.ts).

fruitplug-docs (this wiki)

docker ps --filter name=fruitplug-docs
docker logs -f fruitplug-docs
docker restart fruitplug-docs

Image: fruitplug-docs:local (built from docs/Dockerfile). Runs mkdocs serve --livereload with /docs bind-mounted from the host, so edits to .md files auto-reload the browser.

Backups

The Caddyfile gets a timestamped backup before every edit by convention:

cp Caddyfile "Caddyfile.bak.$(date +%Y%m%d-%H%M%S)"

Existing backups:

ls C:\Users\User\Desktop\Caddy\Caddyfile.bak.*

TLS

Caddy auto-issues Let's Encrypt certs as soon as DNS propagates for a new domain. If you add a block for a new subdomain:

  1. Create the DNS A-record pointing to 147.12.227.117
  2. Add the Caddy block + validate + reload
  3. First HTTPS request triggers cert issuance (usually < 10 s)
  4. curl -sI https://<new>.fruitplug.co.uk returns 200

If Caddy's ACME retry stalls after DNS propagation (it caches failures), restart the container to force a fresh attempt:

docker restart fatbot-caddy