DashCaddy Documentation

Troubleshooting

Because DashCaddy sits across runtime, DNS, reverse proxy, certificates, and dashboard state, the fastest way to debug it is layer by layer instead of guessing. This guide walks each layer with the common failures and fixes.

DashCaddy orchestrates several independent layers — a container runtime, a DNS server, a reverse proxy, a certificate authority, and its own API and dashboard. When a service is unreachable, the failure is almost always in exactly one of these layers while the others are healthy. This guide gives you a structured, layer-by-layer diagnostic procedure with the exact commands to run and the fixes to apply.

The single most important habit: localize before you fix. Resist the urge to restart everything. Use the health endpoints to narrow down which layer is broken, then dig into that layer with the commands below. You will solve problems far faster than by reloading the whole stack.

Health check endpoints — start here

Every investigation begins with the built-in probes. They tell you whether the DashCaddy process itself is healthy and whether its dependencies are wired up, in two seconds:

  • /healthzliveness. Returns 200 if the DashCaddy process is up.
  • /readyzreadiness. Returns 200 only when DashCaddy can serve traffic, including connectivity to Docker, Caddy, and DNS where configured.
# Print just the HTTP status codes
curl -s -o /dev/null -w "healthz: %{http_code}\n" https://dashcaddy-host/healthz
curl -s -o /dev/null -w "readyz:  %{http_code}\n" https://dashcaddy-host/readyz

Interpret the result:

  • Both 200 — DashCaddy and its dependencies are up. The problem is downstream of the platform (the service itself, DNS, cert trust, or the client).
  • /healthz 200, /readyz fails — the process is up but a dependency is unreachable: Docker socket, Caddy Admin API, or Technitium DNS. Read the /readyz body for which dependency failed.
  • /healthz fails — the DashCaddy process itself is down. Check docker ps and docker logs dashcaddy.

The debug order — work bottom-up

When a specific service is unreachable, walk the stack from the container outward to the client. Each step depends on the one before it, so the first failing step is your root cause:

  1. Backend container — is it running and healthy? (docker ps, docker logs)
  2. Backend port — is the service listening and reachable on the host? (curl localhost:port)
  3. Reverse proxy route — did Caddy apply the route correctly? (Caddyfile-as-Code view, Admin API)
  4. DNS — does the hostname resolve to the right host? (dig, nslookup)
  5. Certificate trust — does the client trust the CA? (openssl s_client, browser cert store)
  6. Dashboard / API state — does DashCaddy reflect reality? (compare UI vs. actual container state)

The sections below cover each layer in detail with the commands and fixes for the most common failures.

DNS issues

DNS problems show up as “hostname does not resolve” or “resolves to the wrong address.” Because DashCaddy uses Technitium for internal zones, the most common cause is a client using a public resolver that does not know about your private zones.

  • Check: is the client using Technitium as its resolver? Public resolvers (8.8.8.8, 1.1.1.1) will not resolve internal .lab zones. Point the client's DNS at Technitium, or use Tailscale MagicDNS / split-DNS for remote clients.
  • Check: is the record present in the correct zone? DNS automation fails silently when the zone name is wrong — a record in lab vs lab. is a different zone.
  • Check: is the Technitium API token valid and scoped for writes? An expired or read-only token will let records appear to “work” in the UI but fail to actually create.
  • Fix: re-run the DNS step from the service's action menu, or recreate the record manually in Technitium and let DashCaddy reconcile.
# Query Technitium directly (bypass the client's resolver)
dig @technitium-host media.lab +short
nslookup media.lab technitium-host

# Check what the client's resolver returns (may differ)
dig media.lab +short

# Trace the full resolution path
dig media.lab +trace

If dig @technitium-host returns the right IP but dig media.lab does not, the client is not using Technitium. If Technitium itself returns nothing, the record was never created — check the token and zone, then recreate it.

TLS / certificate problems

Certificate problems show up as browser warnings (NET::ERR_CERT_AUTHORITY_INVALID) or TLS handshake failures. There are two distinct causes, and the fix is different for each.

Cause 1: client does not trust the internal CA

For internal (.lab) services, Caddy uses its internal CA and DashCA distributes the root certificate. The root cert must be installed as a trusted CA on each client device — not just the server. Download it from the DashCA page and follow the per-platform instructions (macOS Keychain, Windows certmgr, Linux update-ca-certificates, mobile profiles).

Cause 2: certificate issuance failed

If Caddy could not reach its CA at deploy time (internal CA down, or ACME unreachable for public domains), no certificate is issued and the TLS handshake fails outright. Confirm the Caddy Admin API is reachable, then redeploy or re-trigger TLS for the service.

# Inspect the certificate a server presents
echo | openssl s_client -connect media.lab:443 -servername media.lab 2>/dev/null \
  | openssl x509 -noout -issuer -subject -dates

# Verify the chain against a specific CA bundle
openssl s_client -connect media.lab:443 -CAfile /path/to/dashca-root.crt </dev/null

If openssl s_clientshows the issuer is Caddy's internal CA and your browser still warns, the root cert is not installed on that client. If s_client shows no certificate at all, issuance failed — check Caddy.

Tip: After installing the root CA, restart the browser. Chrome and Firefox maintain separate trust stores on some platforms — Firefox may need the import done from its own settings rather than the OS store.

Reverse proxy debugging (Caddy)

If the service is up, the port is reachable, and DNS resolves, but the URL returns 502, 504, or does not route, the problem is in the Caddy layer. DashCaddy drives Caddy through its Admin API, so two things can go wrong: the Admin API is unreachable, or the generated config is wrong.

  • Check: is the Caddy Admin API reachable from the DashCaddy API server? (curl localhost:2019/config/ on the host)
  • Check: does the Caddy route point at the correct upstream host:port? Use the Caddyfile-as-Code view to inspect the generated config.
  • Check: Caddy logs — docker logs caddy or your Caddy service logs — for upstream connection errors and reload failures.
  • Fix: re-apply the route from the service's action menu; DashCaddy reconciles the Caddy configuration atomically. If the config is invalid, DashCaddy rejects it before Caddy ever sees it.
# Query the live Caddy config via the Admin API
curl -s localhost:2019/config/ | jq

# Find the route for a specific hostname
curl -s localhost:2019/config/ | jq '.. | .match? // empty | select(.host[]? | contains("media.lab"))'

# Tail Caddy logs for upstream errors
docker logs caddy --tail 50 -f

Container health

If a service shows Unhealthy or Down on the dashboard, the problem is the container itself. Go straight to Docker.

  • Check: docker ps -a — is the container running, restarting, or exited?
  • Check: docker logs <container> — look for crash loops, missing files, bad config, or auth failures.
  • Check: the container's healthcheck (if defined). DashCaddy surfaces container healthchecks in the UI; a failing healthcheck means the app is up but not ready (e.g. still migrating a database).
  • Check: are volumes mounted and environment variables correct? Bad secrets (wrong DB password, missing API key) are the most common cause of immediate exits.
# List all containers including stopped ones
docker ps -a --filter "name=media"

# Tail recent logs
docker logs media --tail 100

# Inspect the healthcheck status and exit codes
docker inspect media --format '{{.State.Health.Status}} {{.State.ExitCode}}'

# Check resource usage if the container is OOM-killing
docker stats --no-stream media

Performance issues

If DashCaddy itself is slow or unresponsive, the cause is usually resource pressure on the host or an overloaded dependency.

  • Host resources: check CPU, memory, and disk with htop, free -h, and df -h. DashCaddy is lightweight, but a host running dozens of containers can starve it.
  • Disk I/O: slow disks make Docker operations (deploy, inspect, logs) sluggish. Check iostat -x 1 for high %util.
  • Docker daemon load: a wedged Docker daemon slows every operation. docker info and systemctl status docker reveal daemon-level issues.
  • DNS latency: if Technitium is overloaded or remote, every DNS operation in DashCaddy slows down. Check Technitium's own health and resource usage.
  • Polling overhead: if you have many scripts polling the REST API, switch them to the WebSocket channel or Prometheus endpoint to reduce load.
# Quick host health snapshot
free -h && df -h | grep -E "^/dev|Filesystem"
docker stats --no-stream
uptime

Common error messages

The table maps the most frequently seen errors to their likely cause and fix. For the full catalog of structured error codes across all modules, see the API guide.

ErrorLikely causeFix
NET::ERR_CERT_AUTHORITY_INVALIDClient does not trust the DashCA root certificateInstall the root CA from the DashCA page on the client device
502 Bad GatewayCaddy route points at a wrong/unreachable upstream portCheck the Caddyfile-as-Code view; fix the upstream host:port; re-apply
504 Gateway TimeoutUpstream is up but too slow to respond within the proxy timeoutInspect container logs; increase Caddy proxy timeout if the app legitimately needs more time
Hostname does not resolveClient is not using Technitium as its resolver, or the record was not createdPoint client DNS at Technitium; verify the record exists; re-run DNS step
DNS_TOKEN_INVALIDTechnitium API token expired or revokedRegenerate the token in Technitium; update it under Settings → DNS
PROXY_CADDY_UNREACHABLECaddy Admin API (localhost:2019) is down or firewalledRestart Caddy; confirm the Admin API port is open to DashCaddy
DEPLOY_PORT_CONFLICTAnother container already holds the requested host portStop the conflicting container or choose a different port
LICENSE_EXPIREDPremium license expired past the 7-day grace periodRenew from Settings → Licensing; free-tier features remain available
LICENSE_MACHINE_LIMITLicense already bound to another machineDeactivate on the old host before activating on the new one
AUTH_PERMISSION_DENIEDUser/API key lacks the RBAC role for the actionAssign the needed role in Settings → Users
WebSocket updates stallA reverse proxy or firewall is dropping the WS upgradeAllow WebSocket upgrades on the DashCaddy route in Caddy/firewall
429 Too Many RequestsAPI client exceeded the per-token rate limitBack off and retry after Retry-After; switch polling to WS/Prometheus

Debug mode

When the standard checks do not reveal the problem, enable debug logging for verbose output from every layer. Set the LOG_LEVEL environment variable to debug and restart DashCaddy:

# Enable debug logging (docker run)
docker run -d \
  -e LOG_LEVEL=debug \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -p 3000:3000 \
  ghcr.io/dashcaddy/dashcaddy:latest

# Or in docker-compose.yml
services:
  dashcaddy:
    environment:
      - LOG_LEVEL=debug

# Then tail the logs
docker logs dashcaddy -f --tail 200

Debug mode emits detailed logs for Docker operations, Caddy Admin API calls, DNS requests, certificate workflows, and the AI/MCP layer. Reproduce the problem while debug logging is on, then grep the logs for the relevant module. Disable debug mode when done — it is verbose and not recommended for long-term production use.

Support resources

If you have worked through the layers above and are still stuck, the following resources can help:

  • Integrations guideInfrastructure Integrations explains what each layer expects and how to configure it.
  • API error reference — the API and Automation guide lists all 80 structured error codes across 12 modules.
  • InstallationInstallation Guide covers first-run setup and the Smart Defaults Wizard.
  • Premium / licensingPremium Features covers license validation, grace periods, and machine binding.
  • Priority support — Premium license holders get priority support. Open a ticket from Settings → Support in the dashboard.

Mindset

Most DashCaddy problems are really one dependency layer failing while the others are healthy. Use the health endpoints to localize, then walk the debug order from the container outward. Fixing the right layer first is always faster than reloading the whole stack. When in doubt, enable debug mode, reproduce the issue, and read the logs for the failing module — the answer is almost always there.