Copy/paste this compose.yaml
, it should show you that it works correctly.
services:
reverse-proxy:
image: caddy:2.8
container_name: caddy
configs:
- source: caddyfile
target: /etc/caddy/Caddyfile
- source: snippet-outer
target: /etc/caddy/snippets/outer
- source: snippet-inner
target: /etc/caddy/snippets/inner
ports:
- "80:80"
- "443:443"
# Normally you'd use `volumes` to provide these files
# Used only for easy example via a single copy/paste `compose.yaml` file,
# All config files embedded into `compose.yaml`
configs:
snippet-inner:
content: |
(respond-snippet) {
respond "Hello"
}
snippet-outer:
content: |
import inner
caddyfile:
content: |
import snippets/outer
# This will fail since inner is imported twice
# (direct via * and also indirectly via outer)
#import snippets/*
:80 {
import respond-snippet
}
Docker and container state gotchas
If you ran into your issue while using Caddy in a container, it’s important to remember that depending on what you’re doing a container may be started (or rather resumed from a stop) or restarted, which can sometimes cause problems as changes you make after are not always going to be applied.
For example with the Docker Compose example above, I’m using the configs
feature. If you Ctrl + C
+ docker compose up
or docker compose restart
, even if you had made a change to that respond snippet text in compose.yaml
, you’ll notice that the change is not applied. This is because the original container created was not removed.
To avoid that, you can force recreate a container like so:
docker compose down && docker compose up
docker compose up --force-recreate
You can run into similar problems with volumes, especially if the Dockerfile
itself has the VOLUME
directive. That will create an anonymous volume for every new container instance (it will be removed after disposing the container if you start it with docker run --rm
), while Docker Compose works a little differently and makes extra effort to preserve the volume to the Compose project service it was created for, you need to find it (docker volumes ls
isn’t too helpful there) and remove it explicitly, otherwise even when you create a new container for that same compose service like shown above, that anonymous volume retains the old content.
For bind mount volumes, you can run into a similar problem if you are bind mounting files directly instead of directories. In this scenario depending how you modify a file the inode might change on the host while the bind mount is tied to the old inode (this is not dockers fault, its how bind mounts work on linux AFAIK), recreating the container should update the inode. To avoid this problem bind mount directories only if file content will change (the container itself cannot write changes to a file bind mount IIRC, which is another caveat).
Likewise, even without volumes a container will persist it’s internal filesystem state until destroyed. That can be another source of confusion if you expected it to be reset when you stopped/restarted a container but didn’t recreate it when bringing it back up