Agent Caddy Routes
How the agent container manages Caddy route files for deployed applications.
The API writes Caddy route files for local deployments to the fixed
/caddy/routes directory. Each deployed application gets its own .caddy file
in this directory.
Route Directory
The local route directory is fixed at /caddy/routes (the API creates it on
startup). On remote servers, routes are written to /etc/caddy/routes — over
SSH for SSH-mode servers, or by the agent for agent-mode servers.
The directory must exist and be writable by the process managing Caddy. The API creates it automatically on startup.
Caddyfile Import Directive
The main Caddyfile at /etc/caddy/Caddyfile includes all route files via a glob import:
import /etc/caddy/routes/*.caddy
Each .caddy file is a standalone Caddy site block. Caddy reads all matching files at startup and merges them into its routing table.
Route File Format
A route file contains a single Caddy site block. The filename matches the project slug:
my-app.caddy
The file content defines the hostname, logging, and reverse proxy target:
my-app.localhost:80 {
log {
output stdout
format json
}
reverse_proxy my-app-abc12345:3000 {
header_up Host {upstream_hostport}
}
}
- Hostname:
<project-slug>.<base-domain>(e.g.,my-app.localhost:80ormy-app.example.com) - Container name:
<project-slug>-<deployment-id-prefix>(e.g.,my-app-abc12345) - Port: The application’s internal port (the platform default is
3000, or the project’s configured port)
For custom domains, multiple hostnames are comma-separated on the first line:
my-app.localhost:80, blog.example.com:80 {
log {
output stdout
format json
}
reverse_proxy my-app-abc12345:3000 {
header_up Host {upstream_hostport}
}
}
Route Lifecycle
Deploy
When a deployment completes successfully:
- The API builds a Caddy snippet using
buildCaddySnippet()fromapps/api/src/utils/domain-verifier.ts. - The snippet is written to
/caddy/routes/<project-slug>.caddy. - Caddy is reloaded via
caddy reload --config /etc/caddy/Caddyfile. - A route record is persisted to the database with status
active.
For remote agent servers, the control plane sends a reload_routes job to the agent. The agent writes the file and reloads Caddy on the remote host. For SSH-mode servers, the control plane writes the file and reloads Caddy over SSH instead.
Rollback
Rollback creates a new deployment with the old image tag. The existing route file is overwritten with the new container name, and Caddy is reloaded.
Delete
When a deployment is destroyed:
- A
reload_routesjob is queued withaction: "remove". - The agent deletes the
.caddyfile from the routes directory. - Caddy is reloaded, removing the route from the routing table.
On the control plane side, removeIngressRouteFile() in apps/api/src/utils/ingress.ts handles cleanup for both local and remote modes.
Custom Domain Addition
When a verified custom domain is added to a project, addToCaddyRoute() in apps/api/src/utils/domain-verifier.ts modifies the existing route file to include the new hostname. It reads the current content, appends the domain to the first line, and reloads Caddy.
Reload vs Restart
Dequel uses caddy reload, not caddy restart:
- Reload (
caddy reload --config /etc/caddy/Caddyfile) applies configuration changes without dropping active connections. Existing requests continue to be served. - Restart would terminate all connections and re-initialize Caddy, causing downtime.
Reload is triggered after every route file change (deploy, rollback, delete, custom domain update).
Troubleshooting
Route files not appearing
- Check that the routes directory exists (
/caddy/routeslocally,/etc/caddy/routeson remote servers). - Verify the API process has write permissions to the directory.
- For agent servers, ensure the agent is running and reachable (check heartbeat status in the dashboard).
Caddy not picking up changes
- Confirm the route file exists in the routes directory:
ls /etc/caddy/routes/. - Manually reload Caddy:
docker exec dequel-caddy caddy reload --config /etc/caddy/Caddyfile. - Check Caddy logs for configuration errors:
docker logs dequel-caddy.
502 Bad Gateway
The route file points to a container name that does not exist or is not on the same Docker network. Verify the container is running and connected to dequel_net:
docker ps --filter name=<project-slug>
docker network inspect dequel_net
Custom domain not routing
Ensure the domain is verified in the Dequel dashboard and the DNS record points to the correct IP. Check that the hostname appears in the route file’s first line.