import Navbar from '@/components/Navbar'; import Footer from '@/components/Footer'; import DocsLayout from '@/components/docs/DocsLayout'; export default function DocsTroubleshootingPage() { return (
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.
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:
/healthz — liveness. Returns 200 if the DashCaddy process is up./readyz — readiness. 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:
/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.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:
docker ps, docker logs)curl localhost:port)dig, nslookup)openssl s_client, browser cert store)The sections below cover each layer in detail with the commands and fixes for the most common failures.
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.
.lab zones. Point the client's DNS at Technitium, or use Tailscale MagicDNS / split-DNS for remote clients.lab vs lab. is a different zone.{`# 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.
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.
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).
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
If openssl s_client shows 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.
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.
curl localhost:2019/config/ on the host)docker logs caddy or your Caddy service logs — for upstream connection errors and reload failures.{`# 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`}
If a service shows Unhealthy or Down on the dashboard, the problem is the container itself. Go straight to Docker.
docker ps -a — is the container running, restarting, or exited?docker logs <container> — look for crash loops, missing files, bad config, or auth failures.{`# 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`}
If DashCaddy itself is slow or unresponsive, the cause is usually resource pressure on the host or an overloaded dependency.
htop, free -h, and df -h. DashCaddy is lightweight, but a host running dozens of containers can starve it.iostat -x 1 for high %util.docker info and systemctl status docker reveal daemon-level issues.{`# Quick host health snapshot
free -h && df -h | grep -E "^/dev|Filesystem"
docker stats --no-stream
uptime`}
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.
| Error | Likely cause | Fix |
|---|---|---|
NET::ERR_CERT_AUTHORITY_INVALID |
Client does not trust the DashCA root certificate | Install the root CA from the DashCA page on the client device |
502 Bad Gateway |
Caddy route points at a wrong/unreachable upstream port | Check the Caddyfile-as-Code view; fix the upstream host:port; re-apply |
504 Gateway Timeout |
Upstream is up but too slow to respond within the proxy timeout | Inspect container logs; increase Caddy proxy timeout if the app legitimately needs more time |
| Hostname does not resolve | Client is not using Technitium as its resolver, or the record was not created | Point client DNS at Technitium; verify the record exists; re-run DNS step |
DNS_TOKEN_INVALID |
Technitium API token expired or revoked | Regenerate the token in Technitium; update it under Settings → DNS |
PROXY_CADDY_UNREACHABLE |
Caddy Admin API (localhost:2019) is down or firewalled | Restart Caddy; confirm the Admin API port is open to DashCaddy |
DEPLOY_PORT_CONFLICT |
Another container already holds the requested host port | Stop the conflicting container or choose a different port |
LICENSE_EXPIRED |
Premium license expired past the 7-day grace period | Renew from Settings → Licensing; free-tier features remain available |
LICENSE_MACHINE_LIMIT |
License already bound to another machine | Deactivate on the old host before activating on the new one |
AUTH_PERMISSION_DENIED |
User/API key lacks the RBAC role for the action | Assign the needed role in Settings → Users |
| WebSocket updates stall | A reverse proxy or firewall is dropping the WS upgrade | Allow WebSocket upgrades on the DashCaddy route in Caddy/firewall |
429 Too Many Requests |
API client exceeded the per-token rate limit | Back off and retry after Retry-After; switch polling to WS/Prometheus |
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.
If you have worked through the layers above and are still stuck, the following resources can help:
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.