1. The problem I’m having:
I have Caddy running inside an LXC container created using Nix. I previously had it manage Let’s Encrypt certificates . Now I want to use certificates I manage separately, so I switched to:
tls {
load /home/caddy/tls
}
That directory is mounted from the host and owned inside the container by a group that includes the Caddy user, with full read & execute permissions. sudo -u caddy ls /home/caddy/tls
is fine inside the container. So is sudo systemd-run --uid $(id -u caddy) --gid $(id -u caddy) -p "SupplementaryGroups=" --service-type exec --wait --collect ls /home/caddy/tls
(note the lack of supplementary groups), which shows me the contents of the directory in the system logs. Same for ls
on a single file. There’s a symlink in the directory, but the Caddy user is able to resolve that too (and I can cat
it via systemd-run
).
However, Caddy fails with an error about traversing into the path:
2. Error messages and/or full log output:
Feb 17 02:40:11 margdarshak systemd[1]: Starting Caddy...
Feb 17 02:40:11 margdarshak caddy[729]: {"level":"info","ts":1739740211.6103644,"msg":"using config from file","file":"/etc/caddy/caddy_config"}
Feb 17 02:40:11 margdarshak caddy[729]: {"level":"info","ts":1739740211.613658,"msg":"adapted config to JSON","adapter":"caddyfile"}
Feb 17 02:40:11 margdarshak caddy[729]: {"level":"warn","ts":1739740211.613671,"msg":"Caddyfile input is not formatted; run 'caddy fmt --overwrite' to fix inconsistencies","adapter":"caddyfile","file":"/etc/caddy/caddy_config","line":5}
Feb 17 02:40:11 margdarshak caddy[729]: {"level":"info","ts":1739740211.6162915,"logger":"admin","msg":"admin endpoint started","address":"localhost:2019","enforce_origin":false,"origins":["//[::1]:2019","//127.0.0.1:2019","//localhost:2019"]}
Feb 17 02:40:11 margdarshak caddy[729]: {"level":"info","ts":1739740211.6164439,"logger":"tls.cache.maintenance","msg":"started background certificate maintenance","cache":"0xc0005fa900"}
Feb 17 02:40:11 margdarshak caddy[729]: {"level":"info","ts":1739740211.6164505,"logger":"tls.cache.maintenance","msg":"stopped background certificate maintenance","cache":"0xc0005fa900"}
Feb 17 02:40:11 margdarshak caddy[729]: Error: loading initial config: loading new config: loading http app module: provision http: getting tls app: loading tls app module: provision tls: loading certificates: unable to traverse into path: /home/caddy/tls
Feb 17 02:40:11 margdarshak systemd[1]: caddy.service: Main process exited, code=exited, status=1/FAILURE
3. Caddy version:
2.9.0
4. How I installed and ran Caddy:
services.caddy = {
enable = true;
# The config directory will be overlaid at runtime.
package = pkgs.caddy.withPlugins {
plugins = [
"github.com/caddy-dns/porkbun@v0.2.1"
];
hash = "sha256-5rfdWHT2ah5THFKjcSoN+aTLhnjQYbNFQxQTfXB439I=";
};
dataDir = "/data/caddy";
environmentFile = "/home/caddy/secrets/.env";
};
a. System environment:
NixOS with systemd running inside an unprivileged LXC container managed by Proxmox.
b. Command:
sudo systemctl restart caddy.service
c. Service/unit/compose file:
[Unit]
Description=Caddy
Documentation=https://caddyserver.com/docs/
After=network.target network-online.target
Requires=network-online.target
[Service]
Type=notify
User=caddy
Group=caddy
ExecStart=/nix/store/x19dvl9rn0x7y0jvfgsv50n3a0s234l5-caddy-2.9.0/bin/caddy run --environ --config /etc/caddy/Caddyfile
ExecReload=/nix/store/x19dvl9rn0x7y0jvfgsv50n3a0s234l5-caddy-2.9.0/bin/caddy reload --config /etc/caddy/Caddyfile --force
TimeoutStopSec=5s
LimitNOFILE=1048576
PrivateTmp=true
ProtectSystem=full
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
[Install]
WantedBy=multi-user.target
d. My complete Caddy config:
{
debug
}
*.{$MAIN_DOMAIN} {
tls {
load /home/caddy/tls
}
# tls {
# dns porkbun {
# api_key {env.PORKBUN_API_KEY}
# api_secret_key {env.PORKBUN_API_SECRET_KEY}
# }
# propagation_timeout 60m
# }
@frigate host frigate.{env.MAIN_DOMAIN}
handle @frigate {
reverse_proxy /* frigate.shani.{env.INTERNAL_DOMAIN}:443 {
header_up Host {upstream_hostport}
transport http {
tls_insecure_skip_verify
}
}
}
import sites/*
handle {
abort
}
}