Caddy not working admin site in Quadlet

1. The problem I’m having:

I’m trying to run Caddy using Podman Quadlets, but when it launches it doesn’t seem to launch the admin site and has a generic page served for the default website. My configuration works if I run it as a standalone server.

2. Error messages and/or full log output:

From journalctl -u caddy:

Apr 29 13:39:31 castellan systemd[1]: Starting caddy.service - Caddy...
Apr 29 13:39:31 castellan podman[16053]: 2026-04-29 13:39:31.998919851 +0000 UTC m=+0.037900032 container create c92ec7a6304dc41f834e2d129c1d42e14d1df5bdfc965128475c49dc540eef83 (image=>
Apr 29 13:39:32 castellan podman[16053]: 2026-04-29 13:39:31.983098472 +0000 UTC m=+0.022078682 image pull 2ad4f81b9a44d531eff80b8d8c65c495c8a867648dc9a3e3720224d7418b3f90 docker.io/cad>
Apr 29 13:39:32 castellan podman[16053]: 2026-04-29 13:39:32.108669969 +0000 UTC m=+0.147650168 container init c92ec7a6304dc41f834e2d129c1d42e14d1df5bdfc965128475c49dc540eef83 (image=do>
Apr 29 13:39:32 castellan podman[16053]: 2026-04-29 13:39:32.114503183 +0000 UTC m=+0.153483363 container start c92ec7a6304dc41f834e2d129c1d42e14d1df5bdfc965128475c49dc540eef83 (image=d>
Apr 29 13:39:32 castellan systemd[1]: Started caddy.service - Caddy.
Apr 29 13:39:32 castellan caddy[16053]: c92ec7a6304dc41f834e2d129c1d42e14d1df5bdfc965128475c49dc540eef83
Apr 29 13:39:32 castellan systemd-caddy[16132]: {"level":"info","ts":1777469972.1413317,"msg":"maxprocs: Leaving GOMAXPROCS=2: CPU quota undefined"}
Apr 29 13:39:32 castellan systemd-caddy[16132]: {"level":"info","ts":1777469972.1413484,"msg":"GOMEMLIMIT is updated","GOMEMLIMIT":3604912128,"previous":9223372036854775807}
Apr 29 13:39:32 castellan systemd-caddy[16132]: {"level":"info","ts":1777469972.1413553,"msg":"using config from file","file":"/etc/caddy/Caddyfile"}
Apr 29 13:39:32 castellan systemd-caddy[16132]: {"level":"info","ts":1777469972.1413567,"msg":"adapted config to JSON","adapter":"caddyfile"}
Apr 29 13:39:32 castellan systemd-caddy[16132]: {"level":"info","ts":1777469972.142165,"logger":"admin","msg":"admin endpoint started","address":"localhost:2019","enforce_origin":false,>
Apr 29 13:39:32 castellan systemd-caddy[16132]: {"level":"warn","ts":1777469972.142373,"logger":"http.auto_https","msg":"server is listening only on the HTTP port, so no automatic HTTPS>
Apr 29 13:39:32 castellan systemd-caddy[16132]: {"level":"warn","ts":1777469972.1425374,"logger":"http","msg":"HTTP/2 skipped because it requires TLS","network":"tcp","addr":":80"}
Apr 29 13:39:32 castellan systemd-caddy[16132]: {"level":"warn","ts":1777469972.1425438,"logger":"http","msg":"HTTP/3 skipped because it requires TLS","network":"tcp","addr":":80"}
Apr 29 13:39:32 castellan systemd-caddy[16132]: {"level":"info","ts":1777469972.1425457,"logger":"http.log","msg":"server running","name":"srv0","protocols":["h1","h2","h3"]}
Apr 29 13:39:32 castellan systemd-caddy[16132]: {"level":"info","ts":1777469972.142752,"msg":"autosaved config (load with --resume flag)","file":"/config/caddy/autosave.json"}
Apr 29 13:39:32 castellan systemd-caddy[16132]: {"level":"info","ts":1777469972.1427574,"msg":"serving initial configuration"}
Apr 29 13:39:32 castellan systemd-caddy[16132]: {"level":"info","ts":1777469972.1424441,"logger":"tls.cache.maintenance","msg":"started background certificate maintenance","cache":"0x59>
Apr 29 13:39:32 castellan systemd-caddy[16132]: {"level":"info","ts":1777469972.1439652,"logger":"tls","msg":"storage cleaning happened too recently; skipping for now","storage":"FileSt>
Apr 29 13:39:32 castellan systemd-caddy[16132]: {"level":"info","ts":1777469972.144066,"logger":"tls","msg":"finished cleaning storage units"}

Results:

$ curl http://localhost:2019
curl: (7) Failed to connect to localhost port 2019 after 0 ms: Couldn't connect to server
$ curl http://localhost
<!DOCTYPE html>
<html>
<head>
	<title>Caddy works!</title>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<link rel="icon" href="data:,">
	<style>
		* {
			box-sizing: border-box;
			padding: 0;
			margin: 0;
		}
...

3. Caddy version:

2.11.2

4. How I installed and ran Caddy:

Podman Quadlet

a. System environment:

Distributor ID:	Ubuntu
Description:	Ubuntu 24.04.4 
LTSRelease:	24.04
Codename:	noble

b. Command:

systemctl restart caddy.service

c. Container file:

[Unit]
Description=Caddy

[Container]
Image=docker.io/caddy:2.11
Volume=/opt/castellan/data/caddy/data:/data
Volume=/opt/castellan/code/caddy:/etc/conf
PublishPort=80:80
PublishPort=443:443
PublishPort=2019:2019

[Install]
WantedBy=default.target

d. My complete Caddy config:

:80 {
   respond "Hello, World!"
}

This looks like a container setup issue rather than Caddy failing to start.

The logs show Caddy is loading /etc/caddy/Caddyfile but your Quadlet mounts your config directory to /etc/conf so the Caddyfile you showed is probably not being used. That would explain why you are seeing the default “Caddy works!” page. Try mounting it to /etc/caddy instead, for example:

Volume=/opt/castellan/code/caddy:/etc/caddy:ro

The admin endpoint is also starting but it is bound to localhost:2019 inside the container, not on the host. Publishing 2019:2019 will not make that reachable from host localhost unless Caddy binds the admin endpoint to an address reachable from outside the container, for example 0.0.0.0:2019. Only do that if you actually need host access to the admin API.

That was it. I had the wrong directory linked.

I was confused because of the log {"level":"info","ts":1777469972.1413553,"msg":"using config from file","file":"/etc/caddy/Caddyfile"} since it says it’s using the file /etc/caddy/Caddyfile but apparently it’s not because the file doesn’t exist. I expected it to say it couldn’t find a config file, not that it’s using a nonexistent one.

/etc/caddy/Caddyfile already exists in the container. When you mount your own file over it, you replace the original. If you mount your file somewhere else, the default one stays intact.

That’s a weird decision. Can Caddy not run without a Caddyfile?

Explain why it’s a weird decision using a default config file, while all the documentation tells you how to provide your own, yet you decide not to do so?

Why would you ship a container with a toy config that’s guaranteed to break a production system without a warning in the log if something goes screwy in the deployment? Better the container fails to launch, surely.

Why would you YOLO your deployment instead of reading the documentation?