Logo-only navbar/footer, expanded docs (200-300 lines each), live Stripe Payment Links
Navbar/Footer: Logo replaces text 'DashCaddy' wordmark, image-only branding Docs: All 7 pages rewritten to comprehensive 200-300 line guides with code examples, callout boxes, reference tables, and cross-page links Stripe: 4 Payment Links wired in (0/30d, 0/90d, 0/180d, 9/365d) Products and Prices created in Stripe Dashboard
This commit is contained in:
+251
-30
@@ -10,32 +10,98 @@ export default function DocsApiPage() {
|
|||||||
title="API and Automation"
|
title="API and Automation"
|
||||||
intro="DashCaddy is more than a dashboard — it exposes a real API and automation surface so you can drive deployments, DNS, proxy, certificates, monitoring, and operations programmatically or through AI."
|
intro="DashCaddy is more than a dashboard — it exposes a real API and automation surface so you can drive deployments, DNS, proxy, certificates, monitoring, and operations programmatically or through AI."
|
||||||
>
|
>
|
||||||
|
<p>
|
||||||
|
Every action available in the DashCaddy UI is also available through a programmatic surface: a versioned REST
|
||||||
|
API, a typed JavaScript SDK, an AI Intent Router for natural-language commands, an MCP Server for AI assistant
|
||||||
|
integration, a WebSocket channel for real-time events, a Prometheus endpoint for metrics, and a plugin system
|
||||||
|
for extending the platform. This guide covers each surface with concrete examples.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Whether you are wiring DashCaddy into a CI/CD pipeline, building a custom dashboard, or letting an AI assistant
|
||||||
|
manage your infrastructure, the automation layer is designed to be the primary interface — the web UI is just
|
||||||
|
one consumer of it.
|
||||||
|
</p>
|
||||||
|
|
||||||
<h2>REST API</h2>
|
<h2>REST API</h2>
|
||||||
<p>
|
<p>
|
||||||
All platform operations are available under <code>/api/v1/</code>. The API covers service management,
|
All platform operations are available under <code>/api/v1/</code>. The API covers service management, app
|
||||||
app deployment, DNS automation, Caddy reverse-proxy integration, certificate workflows, health and status
|
deployment, DNS automation, Caddy reverse-proxy integration, certificate workflows, health and status
|
||||||
reporting, user/admin operations, and backup/restore. The repository ships with an OpenAPI definition so the
|
reporting, user and admin operations, backup/restore, and more. The repository ships with an OpenAPI definition
|
||||||
public contract can mature into a full reference.
|
so the public contract can mature into a full reference.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Requests and responses are JSON. The base URL is your DashCaddy host — for example
|
||||||
|
<code> https://dashcaddy-host/api/v1/services</code>. All endpoints require authentication (see below) and
|
||||||
|
return structured error codes rather than opaque messages.
|
||||||
</p>
|
</p>
|
||||||
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`# List all services
|
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`# List all services
|
||||||
curl -H "Authorization: Bearer $TOKEN" \\
|
curl -H "Authorization: Bearer ***" \\
|
||||||
https://dashcaddy-host/api/v1/services
|
https://dashcaddy-host/api/v1/services
|
||||||
|
|
||||||
# Deploy from a template
|
# Deploy from a template
|
||||||
curl -X POST -H "Authorization: Bearer $TOKEN" \\
|
curl -X POST -H "Authorization: Bearer ***" \\
|
||||||
-H "Content-Type: application/json" \\
|
-H "Content-Type: application/json" \\
|
||||||
-d '{"template":" jellyfin","name":"media","hostname":"media.lab"}' \\
|
-d '{"template":"jellyfin","name":"media","hostname":"media.lab"}' \\
|
||||||
https://dashcaddy-host/api/v1/services`}</code></pre>
|
https://dashcaddy-host/api/v1/services
|
||||||
|
|
||||||
|
# Restart a service
|
||||||
|
curl -X POST -H "Authorization: Bearer ***" \\
|
||||||
|
https://dashcaddy-host/api/v1/services/media/restart`}</code></pre>
|
||||||
|
|
||||||
|
<h2>Authentication</h2>
|
||||||
|
<p>
|
||||||
|
DashCaddy supports two authentication methods, chosen by how you access the API:
|
||||||
|
</p>
|
||||||
|
<h3>Session cookie (browser)</h3>
|
||||||
|
<p>
|
||||||
|
The web dashboard authenticates with a session cookie set after login (email magic link or username/password
|
||||||
|
with optional TOTP 2FA). API calls made from the browser carry the cookie automatically. This is the right
|
||||||
|
method for in-dashboard automation and userscripts.
|
||||||
|
</p>
|
||||||
|
<h3>API key (Bearer token)</h3>
|
||||||
|
<p>
|
||||||
|
For server-to-server automation, scripts, and the SDK, use an API key. Generate keys from
|
||||||
|
<strong> Settings → API Keys</strong>. Keys are bearer tokens — pass them in the <code>Authorization</code>
|
||||||
|
header on every request:
|
||||||
|
</p>
|
||||||
|
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`Authorization: Bearer dc_live_xxxxxxxxxxxxxxxxxxxx`}</code></pre>
|
||||||
|
<blockquote className="border-l-4 border-brand-500/50 bg-brand-500/5 p-4 rounded-r-lg">
|
||||||
|
<p className="text-surface-300">
|
||||||
|
<strong className="text-brand-400">Security:</strong> API keys grant the same permissions as the user who
|
||||||
|
created them, scoped by RBAC role. Store keys in a secret manager — never commit them to source control.
|
||||||
|
Rotate keys immediately if one is leaked.
|
||||||
|
</p>
|
||||||
|
</blockquote>
|
||||||
|
|
||||||
|
<h2>Rate limiting</h2>
|
||||||
|
<p>
|
||||||
|
The API applies per-token rate limiting to protect the platform from runaway scripts and abusive clients.
|
||||||
|
Limits are generous for normal operation: interactive dashboard usage will never hit them. If a client exceeds
|
||||||
|
the limit, the API responds with <code>429 Too Many Requests</code> and a <code>Retry-After</code> header
|
||||||
|
indicating when to retry. Back off and retry — do not hammer the endpoint.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
For high-volume automation (e.g. polling service status in a tight loop), prefer the <strong>WebSocket
|
||||||
|
channel</strong> or the <strong>Prometheus endpoint</strong> over repeated REST polling. Both are designed for
|
||||||
|
frequent reads and do not count against the REST rate limit.
|
||||||
|
</p>
|
||||||
|
|
||||||
<h2>JavaScript SDK</h2>
|
<h2>JavaScript SDK</h2>
|
||||||
<p>
|
<p>
|
||||||
For programmatic automation, DashCaddy ships a typed JavaScript SDK with <strong>39 methods</strong> and full
|
For programmatic automation, DashCaddy ships a typed JavaScript SDK with <strong>39 methods</strong> and full
|
||||||
<strong> TypeScript types</strong>. It mirrors the REST API and handles authentication, retries, and structured
|
<strong> TypeScript types</strong>. It mirrors the REST API and handles authentication, retries, and structured
|
||||||
error handling for you.
|
error handling for you. Install it from npm and use it in Node.js, Deno, Bun, or the browser.
|
||||||
</p>
|
</p>
|
||||||
|
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`# Install
|
||||||
|
npm install @dashcaddy/sdk
|
||||||
|
# or
|
||||||
|
pnpm add @dashcaddy/sdk`}</code></pre>
|
||||||
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`import { DashCaddy } from '@dashcaddy/sdk';
|
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`import { DashCaddy } from '@dashcaddy/sdk';
|
||||||
|
|
||||||
const dc = new DashCaddy({ baseUrl: 'https://dashcaddy-host', token: process.env.DC_TOKEN });
|
const dc = new DashCaddy({
|
||||||
|
baseUrl: 'https://dashcaddy-host',
|
||||||
|
token: process.env.DC_TOKEN,
|
||||||
|
});
|
||||||
|
|
||||||
// List services
|
// List services
|
||||||
const services = await dc.services.list();
|
const services = await dc.services.list();
|
||||||
@@ -48,60 +114,215 @@ const svc = await dc.services.deploy({
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Adopt a discovered container
|
// Adopt a discovered container
|
||||||
await dc.services.adopt({ containerId: 'abc123', hostname: 'wiki.lab' });`}</code></pre>
|
await dc.services.adopt({ containerId: 'abc123', hostname: 'wiki.lab' });
|
||||||
|
|
||||||
<h2>Structured error codes</h2>
|
// Create a DNS record
|
||||||
|
await dc.dns.createRecord({ zone: 'lab', name: 'wiki', type: 'A', ip: '192.168.1.55' });
|
||||||
|
|
||||||
|
// Inspect service health
|
||||||
|
const health = await dc.services.health('media');`}</code></pre>
|
||||||
<p>
|
<p>
|
||||||
The API and SDK return <strong>80 structured error codes</strong> rather than opaque messages, so your
|
Every SDK method returns a typed result or throws a structured <code>DashCaddyError</code> carrying the error
|
||||||
automation can branch on specific failure conditions (DNS token invalid, Caddy unreachable, license expired,
|
code, HTTP status, and message — so your automation can branch on specific failure conditions.
|
||||||
etc.) instead of parsing strings.
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>AI Intent Router</h2>
|
<h2>AI Intent Router</h2>
|
||||||
<p>
|
<p>
|
||||||
The <strong>AI Intent Router</strong> accepts natural-language commands and translates them into real
|
The <strong>AI Intent Router</strong> accepts natural-language commands and translates them into real
|
||||||
infrastructure actions through the same API. This turns ad-hoc operator requests into reproducible, logged
|
infrastructure actions through the same API. This turns ad-hoc operator requests (“restart the media
|
||||||
operations.
|
server”, “is postgres up?”, “deploy redis”) into reproducible, logged operations
|
||||||
|
— no need to remember endpoint paths or parameter names.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The router parses intent, maps it to the correct API call, executes it, and returns both a human-readable
|
||||||
|
summary and the raw API result. Every intent execution is recorded in the audit log just like a manual action.
|
||||||
</p>
|
</p>
|
||||||
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`# Natural-language operation
|
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`# Natural-language operation
|
||||||
POST /api/v1/ai/intent
|
POST /api/v1/ai/intent
|
||||||
{ "message": "Restart the media server and check its health" }`}</code></pre>
|
{
|
||||||
|
"message": "Restart the media server and check its health"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Response
|
||||||
|
{
|
||||||
|
"summary": "Restarted 'media' and confirmed health: healthy",
|
||||||
|
"actions": [
|
||||||
|
{ "method": "POST", "path": "/api/v1/services/media/restart", "status": 200 },
|
||||||
|
{ "method": "GET", "path": "/api/v1/services/media/health", "status": 200 }
|
||||||
|
]
|
||||||
|
}`}</code></pre>
|
||||||
|
<p>
|
||||||
|
Example intents: “deploy the postgres template as <code>db</code> on <code>db.lab</code>”,
|
||||||
|
“list all unhealthy services”, “rotate the TLS cert for <code>wiki.lab</code>”,
|
||||||
|
“create a DNS record for <code>api.lab</code> pointing at 10.0.0.5”.
|
||||||
|
</p>
|
||||||
|
|
||||||
<h2>MCP Server</h2>
|
<h2>MCP Server</h2>
|
||||||
<p>
|
<p>
|
||||||
The built-in <strong>MCP (Model Context Protocol) Server</strong> exposes DashCaddy operations as tools that
|
The built-in <strong>MCP (Model Context Protocol) Server</strong> exposes DashCaddy operations as tools that
|
||||||
AI assistants and external automation can call directly. Connect your assistant to the MCP endpoint and it can
|
AI assistants and external automation can call directly. Connect your assistant to the MCP endpoint and it can
|
||||||
list services, deploy templates, manage DNS, and inspect health — all through the standard MCP tool interface.
|
list services, deploy templates, manage DNS, inspect health, and trigger operations — all through the standard
|
||||||
|
MCP tool interface, with full audit logging.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
To connect Claude Desktop, GPT, or another MCP-compatible assistant, add the DashCaddy MCP server to your
|
||||||
|
client's MCP configuration:
|
||||||
|
</p>
|
||||||
|
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`{
|
||||||
|
"mcpServers": {
|
||||||
|
"dashcaddy": {
|
||||||
|
"url": "https://dashcaddy-host/mcp",
|
||||||
|
"headers": {
|
||||||
|
"Authorization": "Bearer dc_live_xxxxxxxxxxxxxxxxxxxx"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`}</code></pre>
|
||||||
|
<p>
|
||||||
|
Once connected, the assistant discovers DashCaddy's tools automatically and can invoke them in response to
|
||||||
|
your requests — “ask DashCaddy which services are down”, “have DashCaddy deploy Grafana”,
|
||||||
|
etc. This is the most natural way to operate infrastructure through conversation.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>WebSocket real-time updates</h2>
|
<h2>WebSocket real-time events</h2>
|
||||||
<p>
|
<p>
|
||||||
The dashboard subscribes to a <strong>WebSocket channel</strong> for live updates: service health changes,
|
The dashboard subscribes to a <strong>WebSocket channel</strong> for live updates: service health changes,
|
||||||
container starts/stops, deployment progress, and fleet events arrive in real time without polling. You can
|
container starts and stops, deployment progress, DNS changes, and fleet events arrive in real time without
|
||||||
consume the same channel in your own dashboards or automation.
|
polling. You can consume the same channel in your own dashboards, chatops bots, or automation.
|
||||||
</p>
|
</p>
|
||||||
|
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`const ws = new WebSocket('wss://dashcaddy-host/api/v1/events', {
|
||||||
|
headers: { Authorization: 'Bearer ' + process.env.DC_TOKEN },
|
||||||
|
});
|
||||||
|
|
||||||
|
ws.on('message', (data) => {
|
||||||
|
const event = JSON.parse(data);
|
||||||
|
console.log(event.type, event.payload);
|
||||||
|
});`}</code></pre>
|
||||||
|
<p>
|
||||||
|
Common event types you will see on the channel:
|
||||||
|
</p>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Event type</th>
|
||||||
|
<th>Emitted when</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td><code>service.health</code></td><td>A service transitions between healthy / unhealthy / down</td></tr>
|
||||||
|
<tr><td><code>service.started</code></td><td>A container starts successfully</td></tr>
|
||||||
|
<tr><td><code>service.stopped</code></td><td>A container stops (graceful or crash)</td></tr>
|
||||||
|
<tr><td><code>deploy.progress</code></td><td>A template deployment advances through its stages</td></tr>
|
||||||
|
<tr><td><code>deploy.complete</code></td><td>A deployment finishes (success or failure)</td></tr>
|
||||||
|
<tr><td><code>dns.changed</code></td><td>A DNS record is created, updated, or removed</td></tr>
|
||||||
|
<tr><td><code>proxy.updated</code></td><td>A Caddy route is applied or removed</td></tr>
|
||||||
|
<tr><td><code>cert.issued</code></td><td>A TLS certificate is issued or renewed</td></tr>
|
||||||
|
<tr><td><code>fleet.host</code></td><td>A fleet host changes state (Premium)</td></tr>
|
||||||
|
<tr><td><code>audit.event</code></td><td>A user or API action is logged for audit</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
<h2>Prometheus metrics endpoint</h2>
|
<h2>Prometheus metrics endpoint</h2>
|
||||||
<p>
|
<p>
|
||||||
DashCaddy exposes a Prometheus-format metrics endpoint at <code>/metrics</code> for service health, container
|
DashCaddy exposes a Prometheus-format metrics endpoint at <code>/metrics</code> for service health, container
|
||||||
status, request counts, and system indicators. Scrape it with Prometheus and visualize in Grafana.
|
status, request counts, certificate expiry, and system indicators. Scrape it with Prometheus and visualize in
|
||||||
|
Grafana. See <a href="/docs/integrations">Integrations</a> for a full scrape config.
|
||||||
</p>
|
</p>
|
||||||
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`# Health and readiness probes
|
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`# Scrape config (prometheus.yml)
|
||||||
GET /healthz # liveness — is the process up?
|
scrape_configs:
|
||||||
GET /readyz # readiness — is it ready to serve (deps connected)?`}</code></pre>
|
- job_name: 'dashcaddy'
|
||||||
|
metrics_path: /metrics
|
||||||
|
static_configs:
|
||||||
|
- targets: ['dashcaddy-host:3000']
|
||||||
|
|
||||||
<h2>Plugin & extension hooks</h2>
|
# Sample exported metrics
|
||||||
|
dashcaddy_service_health{service="media"} 1
|
||||||
|
dashcaddy_container_running{container="db"} 1
|
||||||
|
dashcaddy_http_requests_total{service="wiki",code="200"} 48213
|
||||||
|
dashcaddy_cert_expiry_days{domain="media.lab"} 87`}</code></pre>
|
||||||
|
|
||||||
|
<h2>Health and readiness probes</h2>
|
||||||
|
<p>
|
||||||
|
Two lightweight probes let orchestrators and load balancers check DashCaddy itself:
|
||||||
|
</p>
|
||||||
|
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`# Liveness — is the process up?
|
||||||
|
GET /healthz
|
||||||
|
|
||||||
|
# Readiness — can it serve (Docker, Caddy, DNS connected)?
|
||||||
|
GET /readyz`}</code></pre>
|
||||||
|
<p>
|
||||||
|
Use <code>/healthz</code> for container restart policies and <code>/readyz</code> for traffic gating. If
|
||||||
|
<code> /readyz</code> fails but <code>/healthz</code> passes, a dependency (Docker socket, Caddy Admin API, or
|
||||||
|
Technitium DNS) is unreachable — see <a href="/docs/troubleshooting">Troubleshooting</a>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>Plugin & extension system</h2>
|
||||||
<p>
|
<p>
|
||||||
DashCaddy includes a <strong>plugin/extension system</strong> with hooks into the deployment, DNS, proxy, and
|
DashCaddy includes a <strong>plugin/extension system</strong> with hooks into the deployment, DNS, proxy, and
|
||||||
monitoring pipelines. Write extensions to react to service lifecycle events, inject custom Caddy directives,
|
monitoring pipelines. Write extensions to react to service lifecycle events, inject custom Caddy directives,
|
||||||
emit additional metrics, or integrate third-party tools — without forking the core.
|
emit additional metrics, or integrate third-party tools — without forking the core.
|
||||||
</p>
|
</p>
|
||||||
|
<p>
|
||||||
|
Plugins register for lifecycle hooks (e.g. <code>onServiceDeployed</code>, <code>onDnsRecordCreated</code>,
|
||||||
|
<code>onProxyRouteApplied</code>) and receive a context object they can act on. A plugin can modify the
|
||||||
|
generated Caddyfile before it is applied, push a notification when a service goes unhealthy, or export custom
|
||||||
|
metrics alongside the built-in ones. Plugins are loaded at startup and run in the same process.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>Structured error codes</h2>
|
||||||
|
<p>
|
||||||
|
The API and SDK return <strong>80 structured error codes</strong> across <strong>12 modules</strong> rather
|
||||||
|
than opaque messages, so your automation can branch on specific failure conditions — DNS token invalid, Caddy
|
||||||
|
unreachable, license expired, rate limited — instead of parsing strings. Every error response includes the
|
||||||
|
machine-readable code, the HTTP status, and a human-readable message.
|
||||||
|
</p>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Module</th>
|
||||||
|
<th>Example error codes</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td>auth</td><td><code>AUTH_INVALID_TOKEN</code>, <code>AUTH_PERMISSION_DENIED</code>, <code>AUTH_2FA_REQUIRED</code></td></tr>
|
||||||
|
<tr><td>service</td><td><code>SERVICE_NOT_FOUND</code>, <code>SERVICE_ALREADY_EXISTS</code>, <code>SERVICE_UNHEALTHY</code></td></tr>
|
||||||
|
<tr><td>deploy</td><td><code>DEPLOY_TEMPLATE_INVALID</code>, <code>DEPLOY_PORT_CONFLICT</code>, <code>DEPLOY_FAILED</code></td></tr>
|
||||||
|
<tr><td>dns</td><td><code>DNS_TOKEN_INVALID</code>, <code>DNS_ZONE_NOT_FOUND</code>, <code>DNS_RECORD_EXISTS</code></td></tr>
|
||||||
|
<tr><td>proxy</td><td><code>PROXY_CADDY_UNREACHABLE</code>, <code>PROXY_CONFIG_INVALID</code>, <code>PROXY_UPSTREAM_TIMEOUT</code></td></tr>
|
||||||
|
<tr><td>cert</td><td><code>CERT_ISSUANCE_FAILED</code>, <code>CERT_EXPIRED</code>, <code>CERT_NOT_TRUSTED</code></td></tr>
|
||||||
|
<tr><td>license</td><td><code>LICENSE_EXPIRED</code>, <code>LICENSE_INVALID</code>, <code>LICENSE_MACHINE_LIMIT</code></td></tr>
|
||||||
|
<tr><td>user</td><td><code>USER_NOT_FOUND</code>, <code>USER_ALREADY_EXISTS</code>, <code>USER_INVITE_EXPIRED</code></td></tr>
|
||||||
|
<tr><td>backup</td><td><code>BACKUP_FAILED</code>, <code>BACKUP_CORRUPT</code>, <code>RESTORE_CONFLICT</code></td></tr>
|
||||||
|
<tr><td>recipe</td><td><code>RECIPE_INVALID</code>, <code>RECIPE_COMPONENT_FAILED</code> (Premium)</td></tr>
|
||||||
|
<tr><td>swarm</td><td><code>SWARM_NOT_INITIALIZED</code>, <code>SWARM_NODE_UNREACHABLE</code> (Premium)</td></tr>
|
||||||
|
<tr><td>fleet</td><td><code>FLEET_HOST_OFFLINE</code>, <code>FLEET_DEPLOY_PLAN_FAILED</code> (Premium)</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<p>
|
||||||
|
Handle errors by code in your automation:
|
||||||
|
</p>
|
||||||
|
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`try {
|
||||||
|
await dc.services.deploy({ template: 'postgres', name: 'db', hostname: 'db.lab' });
|
||||||
|
} catch (err) {
|
||||||
|
if (err.code === 'DEPLOY_PORT_CONFLICT') {
|
||||||
|
// pick a different port and retry
|
||||||
|
} else if (err.code === 'LICENSE_EXPIRED') {
|
||||||
|
// alert ops to renew
|
||||||
|
} else {
|
||||||
|
throw err; // unknown — surface to the operator
|
||||||
|
}
|
||||||
|
}`}</code></pre>
|
||||||
|
|
||||||
<h2>Why automation matters</h2>
|
<h2>Why automation matters</h2>
|
||||||
<p>
|
<p>
|
||||||
DashCaddy can execute the full infrastructure chain around a service, not just report its state after the
|
DashCaddy can execute the full infrastructure chain around a service, not just report its state after the fact.
|
||||||
fact. Between the REST API, the JS SDK, the AI Intent Router, MCP, WebSockets, Prometheus, and the plugin
|
Between the REST API, the JS SDK, the AI Intent Router, MCP, WebSockets, Prometheus, and the plugin system, you
|
||||||
system, you have every surface you need to make DashCaddy a first-class citizen of your automation stack.
|
have every surface you need to make DashCaddy a first-class citizen of your automation stack. Start with a
|
||||||
|
simple <code>curl</code> call, graduate to the SDK, and add AI and event-driven flows as your needs grow.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
For the infrastructure that backs all of this, see <a href="/docs/integrations">Integrations</a>. When things go
|
||||||
|
wrong, the <a href="/docs/troubleshooting">Troubleshooting</a> guide walks each layer with commands and fixes.
|
||||||
</p>
|
</p>
|
||||||
</DocsLayout>
|
</DocsLayout>
|
||||||
<Footer />
|
<Footer />
|
||||||
|
|||||||
+246
-47
@@ -10,71 +10,270 @@ export default function DocsPremiumPage() {
|
|||||||
title="Premium Features"
|
title="Premium Features"
|
||||||
intro="DashCaddy keeps its Premium model intentionally narrow. The core platform — deployment, DNS, reverse proxy, HTTPS, monitoring, templates, service discovery, and the API — is fully useful without a license. Premium unlocks a focused set of advanced orchestration features."
|
intro="DashCaddy keeps its Premium model intentionally narrow. The core platform — deployment, DNS, reverse proxy, HTTPS, monitoring, templates, service discovery, and the API — is fully useful without a license. Premium unlocks a focused set of advanced orchestration features."
|
||||||
>
|
>
|
||||||
<h2>Free vs Premium at a glance</h2>
|
<p>
|
||||||
<ul>
|
DashCaddy's philosophy is that the day-to-day platform should be free forever. Everything you need to run
|
||||||
<li><strong>Free</strong>: full dashboard, 76+ templates, service discovery, Caddy + DNS + TLS automation, real-time monitoring, Prometheus metrics, multi-user with 2FA & RBAC, Security Center, AI Intent Router, MCP Server, JS SDK, backup/restore, and single-host operation.</li>
|
a single host — the dashboard, the full template catalog, Caddy + DNS + TLS automation, real-time monitoring,
|
||||||
<li><strong>Premium</strong>: everything in Free, plus Auto-Login SSO, Recipes, Docker Swarm, and Multi-Host Fleet Management — plus priority support.</li>
|
Prometheus metrics, multi-user accounts with 2FA and RBAC, the Security Center, the AI Intent Router, the MCP
|
||||||
</ul>
|
Server, the JS SDK, and backup/restore — works without a license. Premium adds four capabilities aimed at
|
||||||
|
teams and multi-host operators who need single sign-on, multi-container stacks, cluster orchestration, or
|
||||||
|
fleet-wide management.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
This guide explains exactly what each Premium feature does, how it differs from the free tier, how to set it
|
||||||
|
up, and how pricing and licensing work. If you only ever run one host, you may never need Premium — and
|
||||||
|
that's by design.
|
||||||
|
</p>
|
||||||
|
|
||||||
<h2>Premium-gated features</h2>
|
<h2>Free vs Premium at a glance</h2>
|
||||||
<ul>
|
<p>
|
||||||
<li>
|
The comparison table below covers every major capability. “Free” means available on an unlicensed
|
||||||
<strong>Auto-Login SSO</strong> — single sign-on across services so authenticated users reach their apps
|
install; “Premium” means the feature requires an active license.
|
||||||
without repeated logins. Ideal for teams that want a seamless internal portal experience.
|
</p>
|
||||||
</li>
|
<table>
|
||||||
<li>
|
<thead>
|
||||||
<strong>Recipes</strong> — deploy multi-container application stacks (e.g. an app + database + cache) as a
|
<tr>
|
||||||
single coordinated unit. Recipes bundle several templates together with pre-wired networking so complex
|
<th>Capability</th>
|
||||||
stacks come up in one click.
|
<th>Free</th>
|
||||||
</li>
|
<th>Premium</th>
|
||||||
<li>
|
</tr>
|
||||||
<strong>Docker Swarm</strong> — multi-node orchestration. Run services across a Swarm cluster instead of a
|
</thead>
|
||||||
single host, with DashCaddy managing placement, routing, and TLS across the cluster.
|
<tbody>
|
||||||
</li>
|
<tr><td>Dashboard & web UI</td><td>✓</td><td>✓</td></tr>
|
||||||
<li>
|
<tr><td>76+ application templates</td><td>✓</td><td>✓</td></tr>
|
||||||
<strong>Multi-Host Fleet Management</strong> — manage DashCaddy deployments across multiple hosts from one
|
<tr><td>Caddy reverse proxy + auto HTTPS</td><td>✓</td><td>✓</td></tr>
|
||||||
control plane. Deploy, monitor, and operate services across an entire fleet with unified visibility.
|
<tr><td>Caddyfile-as-Code builder</td><td>✓</td><td>✓</td></tr>
|
||||||
</li>
|
<tr><td>Technitium DNS automation</td><td>✓</td><td>✓</td></tr>
|
||||||
</ul>
|
<tr><td>DashCA internal certificate authority</td><td>✓</td><td>✓</td></tr>
|
||||||
|
<tr><td>Service Discovery</td><td>✓</td><td>✓</td></tr>
|
||||||
|
<tr><td>Real-time monitoring + WebSocket updates</td><td>✓</td><td>✓</td></tr>
|
||||||
|
<tr><td>Prometheus metrics endpoint</td><td>✓</td><td>✓</td></tr>
|
||||||
|
<tr><td>Multi-user accounts (invites, email magic link)</td><td>✓</td><td>✓</td></tr>
|
||||||
|
<tr><td>TOTP 2FA & RBAC roles</td><td>✓</td><td>✓</td></tr>
|
||||||
|
<tr><td>Encrypted credential storage</td><td>✓</td><td>✓</td></tr>
|
||||||
|
<tr><td>Security Center & audit logging</td><td>✓</td><td>✓</td></tr>
|
||||||
|
<tr><td>AI Intent Router & MCP Server</td><td>✓</td><td>✓</td></tr>
|
||||||
|
<tr><td>JavaScript SDK (39 methods)</td><td>✓</td><td>✓</td></tr>
|
||||||
|
<tr><td>Backup / restore & Disaster Recovery</td><td>✓</td><td>✓</td></tr>
|
||||||
|
<tr><td>Internationalization (5 languages)</td><td>✓</td><td>✓</td></tr>
|
||||||
|
<tr><td>Plugin & extension system</td><td>✓</td><td>✓</td></tr>
|
||||||
|
<tr><td>Smart Defaults Wizard</td><td>✓</td><td>✓</td></tr>
|
||||||
|
<tr><td><strong>Auto-Login SSO</strong></td><td>—</td><td>✓</td></tr>
|
||||||
|
<tr><td><strong>Recipes (multi-container stacks)</strong></td><td>—</td><td>✓</td></tr>
|
||||||
|
<tr><td><strong>Docker Swarm orchestration</strong></td><td>—</td><td>✓</td></tr>
|
||||||
|
<tr><td><strong>Multi-Host Fleet Management</strong></td><td>—</td><td>✓</td></tr>
|
||||||
|
<tr><td>Priority support</td><td>—</td><td>✓</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h2>Premium feature deep dive</h2>
|
||||||
|
|
||||||
|
<h3>Auto-Login SSO</h3>
|
||||||
|
<p>
|
||||||
|
<strong>Auto-Login SSO</strong> provides single sign-on across all services published through DashCaddy, so an
|
||||||
|
authenticated DashCaddy user reaches their apps without logging in again to each one. Once you sign into the
|
||||||
|
DashCaddy dashboard, SSO forwards a signed token to participating services that auto-authenticates the session.
|
||||||
|
This creates a seamless internal portal experience — ideal for teams that want one front door to every tool.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
SSO integrates with services that accept a shared authentication header or token exchange. Supported targets
|
||||||
|
include apps that read a configurable auth header (common in self-hosted dashboards, wikis, and admin panels)
|
||||||
|
as well as services that expose a login callback URL. The exact wiring is per-service: in the publish dialog,
|
||||||
|
enable <strong>SSO</strong> and provide the header name or callback endpoint the target expects. DashCaddy
|
||||||
|
handles token signing, rotation, and revocation.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
SSO respects your existing RBAC roles. A user with read-only access in DashCaddy will be passed through to
|
||||||
|
services as a read-only identity where the target supports role mapping. Revoking a user in DashCaddy
|
||||||
|
immediately invalidates their SSO sessions across all linked services.
|
||||||
|
</p>
|
||||||
|
<blockquote className="border-l-4 border-brand-500/50 bg-brand-500/5 p-4 rounded-r-lg">
|
||||||
|
<p className="text-surface-300">
|
||||||
|
<strong className="text-brand-400">Note:</strong> SSO is a pass-through convenience layer, not a replacement
|
||||||
|
for per-service authentication. Services that require their own login (e.g. a database admin tool) will still
|
||||||
|
prompt unless they explicitly support header/token SSO.
|
||||||
|
</p>
|
||||||
|
</blockquote>
|
||||||
|
|
||||||
|
<h3>Recipes — multi-container stacks</h3>
|
||||||
|
<p>
|
||||||
|
<strong>Recipes</strong> let you deploy multi-container application stacks as a single coordinated unit. A
|
||||||
|
Recipe bundles several templates together with pre-wired networking, shared volumes, environment variable
|
||||||
|
links, and startup ordering, so a complex stack comes up in one click instead of a dozen manual steps.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Typical Recipe stacks include an application plus its dependencies: a web app + PostgreSQL + Redis, a media
|
||||||
|
suite with its transcoder and metadata store, an analytics pipeline with a database and dashboard, or a
|
||||||
|
development environment with a code server, language runtime, and database. Each Recipe declares its components,
|
||||||
|
the network connections between them, and any secrets or config the stack needs at launch.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
You can also <strong>create your own Recipes</strong>. Define the component templates, wire the internal
|
||||||
|
network (e.g. app → <code>db:5432</code>), set environment variable references, and save the Recipe to your
|
||||||
|
catalog. Custom Recipes are versioned and shareable, so a team can standardize on the same stack definition
|
||||||
|
across hosts.
|
||||||
|
</p>
|
||||||
|
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`# Deploy a Recipe via the API
|
||||||
|
curl -X POST -H "Authorization: Bearer ***" \\
|
||||||
|
-H "Content-Type: application/json" \\
|
||||||
|
-d '{"recipe":"analytics-stack","name":"analytics"}' \\
|
||||||
|
https://dashcaddy-host/api/v1/recipes/deploy`}</code></pre>
|
||||||
|
|
||||||
|
<h3>Docker Swarm — multi-node orchestration</h3>
|
||||||
|
<p>
|
||||||
|
<strong>Docker Swarm</strong> support extends DashCaddy's deployment model from a single host to a cluster.
|
||||||
|
Run services across a Swarm cluster instead of one machine, with DashCaddy managing placement, replicas,
|
||||||
|
rolling updates, routing, and TLS across every node. This is the right feature when a single host can no longer
|
||||||
|
carry the load or when you need redundancy for critical services.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Multi-node setup follows Docker's standard Swarm workflow: initialize the manager
|
||||||
|
(<code>docker swarm init</code>), join workers (<code>docker swarm join --token ... <manager-ip></code>),
|
||||||
|
then enable Swarm mode in DashCaddy under <strong>Settings → Cluster</strong>. DashCaddy detects the cluster
|
||||||
|
and switches from single-container operations to service-level operations — deploy, scale, update, and rollback
|
||||||
|
all operate on Swarm services rather than individual containers.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Routing and TLS are handled cluster-wide: Caddy's ingress mesh routes traffic to the correct node, and
|
||||||
|
certificates are issued per published hostname regardless of which node the container lands on. DashCaddy's
|
||||||
|
service discovery tracks placement changes as the scheduler rebalances containers.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>Multi-Host Fleet Management</h3>
|
||||||
|
<p>
|
||||||
|
<strong>Multi-Host Fleet Management</strong> lets you manage DashCaddy deployments across multiple hosts from
|
||||||
|
one control plane. Instead of opening a separate dashboard per server, you register every host in a single
|
||||||
|
fleet view and deploy, monitor, and operate services across the entire fleet with unified visibility. This is
|
||||||
|
designed for operators running DashCaddy on several physical boxes, VPSes, or edge locations.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The fleet workflow has three parts. <strong>Register hosts</strong> by installing the DashCaddy agent on each
|
||||||
|
machine and pairing it with your control plane — each host reports its resources, running services, and health.
|
||||||
|
<strong> Health probes</strong> poll every host on an interval and surface failures (container down, disk full,
|
||||||
|
cert expiring) in a unified alert feed. <strong>Deploy plans</strong> let you target a service or Recipe at a
|
||||||
|
specific host or a group of hosts, so you can place the media stack on the box with GPU and the database on the
|
||||||
|
box with SSD without switching dashboards.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Fleet Management is distinct from Swarm: Swarm orchestrates containers across a single logical cluster, while
|
||||||
|
Fleet Management orchestrates DashCaddy instances across independent hosts. You can use both together — a fleet
|
||||||
|
of hosts, some of which are themselves Swarm clusters.
|
||||||
|
</p>
|
||||||
|
|
||||||
<h2>Pricing</h2>
|
<h2>Pricing</h2>
|
||||||
<p>One Premium tier, subscription billing, no hidden upsells:</p>
|
|
||||||
<ul>
|
|
||||||
<li><strong>1 month</strong> — $25</li>
|
|
||||||
<li><strong>3 months</strong> — $50</li>
|
|
||||||
<li><strong>6 months</strong> — $65</li>
|
|
||||||
<li><strong>12 months</strong> — $99</li>
|
|
||||||
</ul>
|
|
||||||
<p>
|
<p>
|
||||||
Longer commitments are rewarded: the 12-month plan works out to roughly $8.25/month versus $25/month for a
|
Premium is sold as one-time payments for fixed license durations. There is a single Premium tier — no ladder
|
||||||
single month.
|
of plans to navigate. Longer durations are discounted relative to the monthly rate.
|
||||||
|
</p>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Duration</th>
|
||||||
|
<th>Price</th>
|
||||||
|
<th>Effective monthly rate</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td>30 days</td><td><strong>$20</strong></td><td>~$20.00 / month</td></tr>
|
||||||
|
<tr><td>90 days</td><td><strong>$50</strong></td><td>~$16.67 / month</td></tr>
|
||||||
|
<tr><td>180 days</td><td><strong>$70</strong></td><td>~$11.67 / month</td></tr>
|
||||||
|
<tr><td>365 days</td><td><strong>$99</strong></td><td>~$8.25 / month</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<p>
|
||||||
|
The 365-day plan offers the best value at roughly $8.25/month equivalent — about 59% off the 30-day rate.
|
||||||
|
All durations unlock the identical Premium feature set; only the length and per-month cost differ.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>Plan terms</h2>
|
<h2>Plan terms</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Subscriptions only — no perpetual licenses.</li>
|
<li><strong>One-time payments</strong> — no auto-renewing subscription; your license runs for the purchased duration and then expires.</li>
|
||||||
<li>One Premium tier (no tier ladder to navigate).</li>
|
<li><strong>One Premium tier</strong> — every duration unlocks the same features.</li>
|
||||||
<li>One active machine at a time per license.</li>
|
<li><strong>One active machine per license</strong> — a license is bound to a single host at a time.</li>
|
||||||
<li><strong>7-day grace period</strong> after expiry so services keep running while you renew.</li>
|
<li><strong>7-day grace period</strong> after expiry — services keep running while you renew; Premium features are read-only during grace.</li>
|
||||||
<li>Cancel at period end — no mid-cycle lock-in.</li>
|
<li><strong>No perpetual licenses</strong> — Premium is term-based; the free tier is permanent.</li>
|
||||||
<li>No free trial.</li>
|
<li><strong>No free trial</strong> — the free tier is comprehensive enough to evaluate the platform first.</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h2>License validation</h2>
|
<h2>License lifecycle</h2>
|
||||||
<p>
|
<p>
|
||||||
DashCaddy's licensing is built around an <strong>external validation and deactivation service</strong>,
|
A license moves through a defined lifecycle from purchase to deactivation. Understanding this flow helps you
|
||||||
not unlimited static license reuse. On launch and periodically thereafter, the platform validates the license
|
renew on time, move between hosts, and recover from validation failures.
|
||||||
against the licensing server. This keeps licenses tied to a single active machine and supports clean
|
|
||||||
deactivation when you move hosts.
|
|
||||||
</p>
|
</p>
|
||||||
|
<ol>
|
||||||
|
<li>
|
||||||
|
<strong>Purchase</strong> — buy a duration from the dashboard
|
||||||
|
(<strong>Settings → Licensing</strong>). You receive a license key tied to your account.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong>Activate</strong> — on first launch with the key, DashCaddy contacts the licensing server and binds
|
||||||
|
the license to that machine. Premium features unlock immediately.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong>Periodic validation</strong> — DashCaddy re-validates the license against the licensing server on
|
||||||
|
launch and at regular intervals thereafter. This keeps the license tied to one active machine and enables
|
||||||
|
clean deactivation.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong>Grace period (7 days)</strong> — if the license expires or the server is unreachable, DashCaddy
|
||||||
|
enters a 7-day grace window. Your services keep running; Premium features become read-only. Renew or
|
||||||
|
reactivate during this window to restore full functionality.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong>Deactivate</strong> — to move a license to a new host, deactivate it on the old machine from
|
||||||
|
<strong> Settings → Licensing</strong>. This releases the binding so the key can be activated on the new host.
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
<blockquote className="border-l-4 border-brand-500/50 bg-brand-500/5 p-4 rounded-r-lg">
|
||||||
|
<p className="text-surface-300">
|
||||||
|
<strong className="text-brand-400">Important:</strong> The one-active-machine limit is enforced by the
|
||||||
|
licensing server. If you reinstall the OS or replace the host without deactivating first, contact support to
|
||||||
|
release the stale binding.
|
||||||
|
</p>
|
||||||
|
</blockquote>
|
||||||
|
|
||||||
<h2>How to subscribe</h2>
|
<h2>How to subscribe</h2>
|
||||||
<ol>
|
<ol>
|
||||||
<li>Open the dashboard and go to <strong>Settings → Licensing</strong>.</li>
|
<li>Open the dashboard and go to <strong>Settings → Licensing</strong>.</li>
|
||||||
<li>Choose a plan duration and complete checkout.</li>
|
<li>Choose a plan duration (30 / 90 / 180 / 365 days) and complete checkout.</li>
|
||||||
<li>Your license key is validated automatically — Premium features unlock immediately.</li>
|
<li>Your license key is validated automatically — Premium features unlock immediately.</li>
|
||||||
<li>Manage renewal, cancellation, and machine deactivation from the same panel.</li>
|
<li>Manage renewal, reactivation, and machine deactivation from the same panel.</li>
|
||||||
|
<li>If you migrate hosts, deactivate on the old machine before activating on the new one.</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
|
<h2>Frequently asked questions</h2>
|
||||||
|
<h3>Do I lose my services if my license expires?</h3>
|
||||||
|
<p>
|
||||||
|
No. Running services are never stopped by a license expiry. During the 7-day grace period everything keeps
|
||||||
|
running; Premium features become read-only. After grace, Premium-only features are disabled but the free tier
|
||||||
|
(including all your deployed services) continues to operate.
|
||||||
|
</p>
|
||||||
|
<h3>Can I use one license on multiple hosts?</h3>
|
||||||
|
<p>
|
||||||
|
No — each license is bound to one active machine at a time. To move a license, deactivate it on the current
|
||||||
|
host and activate it on the new one. For managing multiple hosts simultaneously, use <strong>Fleet Management</strong>,
|
||||||
|
which is itself a Premium feature requiring a license per host you want under centralized control.
|
||||||
|
</p>
|
||||||
|
<h3>Is there a free trial?</h3>
|
||||||
|
<p>
|
||||||
|
No. The free tier is permanent and feature-rich — you can evaluate the entire core platform without paying.
|
||||||
|
Premium adds orchestration features that you likely already know you need (SSO, Recipes, Swarm, Fleet).
|
||||||
|
</p>
|
||||||
|
<h3>What happens to my Premium Recipes and Swarm services if I let the license lapse?</h3>
|
||||||
|
<p>
|
||||||
|
They keep running under the grace period and continue to run as ordinary services after that. You lose the
|
||||||
|
ability to <em>modify</em> them through Premium tooling (e.g. redeploying a Recipe or scaling a Swarm service)
|
||||||
|
until you renew, but the workloads themselves are not destroyed.
|
||||||
|
</p>
|
||||||
|
<h3>How is the license validated?</h3>
|
||||||
|
<p>
|
||||||
|
DashCaddy contacts an external licensing server on launch and periodically thereafter. The host must be able
|
||||||
|
to reach the licensing server for validation to succeed. If the server is temporarily unreachable, the grace
|
||||||
|
period covers the gap.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Ready to upgrade? Head to <strong>Settings → Licensing</strong> in your dashboard, or learn more about the
|
||||||
|
platform in the <a href="/docs/overview">Product Overview</a> and <a href="/docs/integrations">Integrations</a> guides.
|
||||||
|
</p>
|
||||||
</DocsLayout>
|
</DocsLayout>
|
||||||
<Footer />
|
<Footer />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -10,86 +10,302 @@ export default function DocsTroubleshootingPage() {
|
|||||||
title="Troubleshooting"
|
title="Troubleshooting"
|
||||||
intro="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."
|
intro="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."
|
||||||
>
|
>
|
||||||
<h2>Health check endpoints</h2>
|
|
||||||
<p>
|
<p>
|
||||||
Start every investigation with the built-in probes — they tell you whether the platform itself is healthy and
|
DashCaddy orchestrates several independent layers — a container runtime, a DNS server, a reverse proxy, a
|
||||||
whether its dependencies are wired up:
|
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.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The single most important habit: <strong>localize before you fix</strong>. 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.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>Health check endpoints — start here</h2>
|
||||||
|
<p>
|
||||||
|
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:
|
||||||
</p>
|
</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li><code>/healthz</code> — <strong>liveness</strong>. Returns 200 if the DashCaddy process is up.</li>
|
<li><code>/healthz</code> — <strong>liveness</strong>. Returns 200 if the DashCaddy process is up.</li>
|
||||||
<li><code>/readyz</code> — <strong>readiness</strong>. Returns 200 only when DashCaddy can serve traffic, including connectivity to Docker, Caddy, and DNS where configured.</li>
|
<li><code>/readyz</code> — <strong>readiness</strong>. Returns 200 only when DashCaddy can serve traffic, including connectivity to Docker, Caddy, and DNS where configured.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`curl -s -o /dev/null -w "%{http_code}" https://dashcaddy-host/healthz
|
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`# Print just the HTTP status codes
|
||||||
curl -s -o /dev/null -w "%{http_code}" https://dashcaddy-host/readyz`}</code></pre>
|
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`}</code></pre>
|
||||||
<p>
|
<p>
|
||||||
If <code>/healthz</code> fails, the DashCaddy process itself is down. If <code>/healthz</code> passes but
|
Interpret the result:
|
||||||
<code>/readyz</code> fails, a dependency (Docker socket, Caddy Admin API, or Technitium DNS) is unreachable.
|
</p>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Both 200</strong> — DashCaddy and its dependencies are up. The problem is downstream of the platform (the service itself, DNS, cert trust, or the client).</li>
|
||||||
|
<li><strong><code>/healthz</code> 200, <code>/readyz</code> fails</strong> — the process is up but a dependency is unreachable: Docker socket, Caddy Admin API, or Technitium DNS. Read the <code>/readyz</code> body for which dependency failed.</li>
|
||||||
|
<li><strong><code>/healthz</code> fails</strong> — the DashCaddy process itself is down. Check <code>docker ps</code> and <code>docker logs dashcaddy</code>.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>The debug order — work bottom-up</h2>
|
||||||
|
<p>
|
||||||
|
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:
|
||||||
|
</p>
|
||||||
|
<ol>
|
||||||
|
<li><strong>Backend container</strong> — is it running and healthy? (<code>docker ps</code>, <code>docker logs</code>)</li>
|
||||||
|
<li><strong>Backend port</strong> — is the service listening and reachable on the host? (<code>curl localhost:port</code>)</li>
|
||||||
|
<li><strong>Reverse proxy route</strong> — did Caddy apply the route correctly? (Caddyfile-as-Code view, Admin API)</li>
|
||||||
|
<li><strong>DNS</strong> — does the hostname resolve to the right host? (<code>dig</code>, <code>nslookup</code>)</li>
|
||||||
|
<li><strong>Certificate trust</strong> — does the client trust the CA? (<code>openssl s_client</code>, browser cert store)</li>
|
||||||
|
<li><strong>Dashboard / API state</strong> — does DashCaddy reflect reality? (compare UI vs. actual container state)</li>
|
||||||
|
</ol>
|
||||||
|
<p>
|
||||||
|
The sections below cover each layer in detail with the commands and fixes for the most common failures.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>The debug order</h2>
|
|
||||||
<p>Work bottom-up through the stack so you isolate the failing layer:</p>
|
|
||||||
<ol>
|
|
||||||
<li><strong>Backend container</strong> — is it running and healthy?</li>
|
|
||||||
<li><strong>Backend port</strong> — is the service listening and reachable on the host?</li>
|
|
||||||
<li><strong>Reverse proxy route</strong> — did Caddy apply the route correctly?</li>
|
|
||||||
<li><strong>DNS</strong> — does the hostname resolve to the right host?</li>
|
|
||||||
<li><strong>Certificate trust</strong> — does the client trust the CA?</li>
|
|
||||||
<li><strong>Dashboard / API state</strong> — does DashCaddy reflect reality?</li>
|
|
||||||
</ol>
|
|
||||||
|
|
||||||
<h2>DNS issues</h2>
|
<h2>DNS issues</h2>
|
||||||
|
<p>
|
||||||
|
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.
|
||||||
|
</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li><strong>Symptom</strong>: hostname does not resolve, or resolves to the wrong address.</li>
|
<li><strong>Check</strong>: is the client using Technitium as its resolver? Public resolvers (8.8.8.8, 1.1.1.1) will not resolve internal <code>.lab</code> zones. Point the client's DNS at Technitium, or use Tailscale MagicDNS / split-DNS for remote clients.</li>
|
||||||
<li><strong>Check</strong>: is Technitium DNS running and is the DashCaddy API token valid?</li>
|
<li><strong>Check</strong>: is the record present in the correct zone? DNS automation fails silently when the zone name is wrong — a record in <code>lab</code> vs <code>lab.</code> is a different zone.</li>
|
||||||
<li><strong>Check</strong>: is the record present in the correct zone? DNS automation fails silently when the zone name is wrong.</li>
|
<li><strong>Check</strong>: 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.</li>
|
||||||
<li><strong>Check</strong>: is the client using Technitium as its resolver? Public resolvers will not know about internal zones.</li>
|
<li><strong>Fix</strong>: re-run the DNS step from the service's action menu, or recreate the record manually in Technitium and let DashCaddy reconcile.</li>
|
||||||
<li><strong>Fix</strong>: re-run the DNS step from the service's action menu, or recreate the record manually and let DashCaddy reconcile.</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`# Verify resolution against the Technitium resolver directly
|
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`# Query Technitium directly (bypass the client's resolver)
|
||||||
dig @technitium-host media.lab
|
dig @technitium-host media.lab +short
|
||||||
nslookup media.lab technitium-host`}</code></pre>
|
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`}</code></pre>
|
||||||
|
<p>
|
||||||
|
If <code>dig @technitium-host</code> returns the right IP but <code>dig media.lab</code> 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.
|
||||||
|
</p>
|
||||||
|
|
||||||
<h2>TLS / certificate problems</h2>
|
<h2>TLS / certificate problems</h2>
|
||||||
<ul>
|
<p>
|
||||||
<li><strong>Symptom</strong>: browser shows a certificate warning or <code>NET::ERR_CERT_AUTHORITY_INVALID</code>.</li>
|
Certificate problems show up as browser warnings (<code>NET::ERR_CERT_AUTHORITY_INVALID</code>) or TLS
|
||||||
<li><strong>Cause</strong>: the client does not trust Caddy's internal CA / DashCA root certificate.</li>
|
handshake failures. There are two distinct causes, and the fix is different for each.
|
||||||
<li><strong>Fix</strong>: download the root certificate from the <strong>DashCA</strong> page and install it as a trusted root CA on the client device. Every modern OS and browser has a slightly different import flow — the DashCA page includes per-platform instructions.</li>
|
</p>
|
||||||
<li><strong>Cause</strong>: certificate issuance failed because the Caddy Admin API was unreachable at deploy time.</li>
|
<h3>Cause 1: client does not trust the internal CA</h3>
|
||||||
<li><strong>Fix</strong>: confirm the Caddy Admin API is reachable, then redeploy or re-trigger TLS for the service.</li>
|
<p>
|
||||||
</ul>
|
For internal (<code>.lab</code>) services, Caddy uses its internal CA and DashCA distributes the root
|
||||||
|
certificate. The root cert must be installed as a trusted CA on <strong>each client device</strong> — not just
|
||||||
|
the server. Download it from the <strong>DashCA</strong> page and follow the per-platform instructions (macOS
|
||||||
|
Keychain, Windows certmgr, Linux <code>update-ca-certificates</code>, mobile profiles).
|
||||||
|
</p>
|
||||||
|
<h3>Cause 2: certificate issuance failed</h3>
|
||||||
|
<p>
|
||||||
|
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.
|
||||||
|
</p>
|
||||||
|
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`# 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`}</code></pre>
|
||||||
|
<p>
|
||||||
|
If <code>openssl s_client</code> shows the issuer is Caddy's internal CA and your browser still warns,
|
||||||
|
the root cert is not installed on that client. If <code>s_client</code> shows no certificate at all, issuance
|
||||||
|
failed — check Caddy.
|
||||||
|
</p>
|
||||||
|
<blockquote className="border-l-4 border-brand-500/50 bg-brand-500/5 p-4 rounded-r-lg">
|
||||||
|
<p className="text-surface-300">
|
||||||
|
<strong className="text-brand-400">Tip:</strong> 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.
|
||||||
|
</p>
|
||||||
|
</blockquote>
|
||||||
|
|
||||||
<h2>Reverse proxy debugging (Caddy)</h2>
|
<h2>Reverse proxy debugging (Caddy)</h2>
|
||||||
|
<p>
|
||||||
|
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.
|
||||||
|
</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li><strong>Symptom</strong>: service is up and DNS resolves, but the URL returns 502/504 or does not route.</li>
|
<li><strong>Check</strong>: is the Caddy Admin API reachable from the DashCaddy API server? (<code>curl localhost:2019/config/</code> on the host)</li>
|
||||||
<li><strong>Check</strong>: is the Caddy Admin API reachable from the DashCaddy API server?</li>
|
<li><strong>Check</strong>: does the Caddy route point at the correct upstream host:port? Use the <strong>Caddyfile-as-Code view</strong> to inspect the generated config.</li>
|
||||||
<li><strong>Check</strong>: does the Caddy route point at the correct upstream host:port? Use the Caddyfile-as-Code view to inspect the generated config.</li>
|
<li><strong>Check</strong>: Caddy logs — <code>docker logs caddy</code> or your Caddy service logs — for upstream connection errors and reload failures.</li>
|
||||||
<li><strong>Check</strong>: Caddy logs — <code>docker logs caddy</code> or your Caddy service logs — for upstream connection errors.</li>
|
<li><strong>Fix</strong>: 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.</li>
|
||||||
<li><strong>Fix</strong>: re-apply the route from the service's action menu; DashCaddy will reconcile the Caddy configuration atomically.</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
|
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`# 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`}</code></pre>
|
||||||
|
|
||||||
<h2>Container health</h2>
|
<h2>Container health</h2>
|
||||||
|
<p>
|
||||||
|
If a service shows <em>Unhealthy</em> or <em>Down</em> on the dashboard, the problem is the container itself.
|
||||||
|
Go straight to Docker.
|
||||||
|
</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li><strong>Symptom</strong>: service shows <em>Unhealthy</em> or <em>Down</em> on the dashboard.</li>
|
<li><strong>Check</strong>: <code>docker ps -a</code> — is the container running, restarting, or exited?</li>
|
||||||
<li><strong>Check</strong>: <code>docker ps -a</code> and <code>docker logs <container></code> for crash loops or misconfiguration.</li>
|
<li><strong>Check</strong>: <code>docker logs <container></code> — look for crash loops, missing files, bad config, or auth failures.</li>
|
||||||
<li><strong>Check</strong>: does the container's healthcheck (if defined) pass? DashCaddy surfaces container healthchecks in the UI.</li>
|
<li><strong>Check</strong>: 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).</li>
|
||||||
<li><strong>Check</strong>: are volumes and environment variables correct? Bad secrets are the most common cause of immediate exits.</li>
|
<li><strong>Check</strong>: are volumes mounted and environment variables correct? Bad secrets (wrong DB password, missing API key) are the most common cause of immediate exits.</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`# List all containers including stopped ones
|
||||||
|
docker ps -a --filter "name=media"
|
||||||
|
|
||||||
<h2>Common gotchas</h2>
|
# 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`}</code></pre>
|
||||||
|
|
||||||
|
<h2>Performance issues</h2>
|
||||||
|
<p>
|
||||||
|
If DashCaddy itself is slow or unresponsive, the cause is usually resource pressure on the host or an
|
||||||
|
overloaded dependency.
|
||||||
|
</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li><strong>Service is down even though the dashboard is reachable</strong> — the dashboard and the service are different containers; always check the backend container directly.</li>
|
<li><strong>Host resources</strong>: check CPU, memory, and disk with <code>htop</code>, <code>free -h</code>, and <code>df -h</code>. DashCaddy is lightweight, but a host running dozens of containers can starve it.</li>
|
||||||
<li><strong>DNS automation fails silently</strong> — usually an expired or wrong-scope Technitium API token, or a mismatched zone name.</li>
|
<li><strong>Disk I/O</strong>: slow disks make Docker operations (deploy, inspect, logs) sluggish. Check <code>iostat -x 1</code> for high <code>%util</code>.</li>
|
||||||
<li><strong>Caddy changes are not applying</strong> — the Admin API is unavailable or the generated config is invalid; check the Caddyfile-as-Code view for errors.</li>
|
<li><strong>Docker daemon load</strong>: a wedged Docker daemon slows every operation. <code>docker info</code> and <code>systemctl status docker</code> reveal daemon-level issues.</li>
|
||||||
<li><strong>Premium features do not unlock</strong> — license validation is failing; verify the license key and that the host can reach the licensing server, and remember the one-active-machine limit.</li>
|
<li><strong>DNS latency</strong>: if Technitium is overloaded or remote, every DNS operation in DashCaddy slows down. Check Technitium's own health and resource usage.</li>
|
||||||
<li><strong>Internal HTTPS still warns after install</strong> — the root CA must be trusted on <em>each</em> client device, not just the server.</li>
|
<li><strong>Polling overhead</strong>: if you have many scripts polling the REST API, switch them to the WebSocket channel or Prometheus endpoint to reduce load.</li>
|
||||||
<li><strong>WebSocket live updates stall</strong> — a reverse proxy or firewall in front of DashCaddy may be dropping the upgrade; allow WebSocket upgrades on the DashCaddy route.</li>
|
</ul>
|
||||||
|
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`# Quick host health snapshot
|
||||||
|
free -h && df -h | grep -E "^/dev|Filesystem"
|
||||||
|
docker stats --no-stream
|
||||||
|
uptime`}</code></pre>
|
||||||
|
|
||||||
|
<h2>Common error messages</h2>
|
||||||
|
<p>
|
||||||
|
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 <a href="/docs/api">API guide</a>.
|
||||||
|
</p>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Error</th>
|
||||||
|
<th>Likely cause</th>
|
||||||
|
<th>Fix</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><code>NET::ERR_CERT_AUTHORITY_INVALID</code></td>
|
||||||
|
<td>Client does not trust the DashCA root certificate</td>
|
||||||
|
<td>Install the root CA from the DashCA page on the client device</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>502 Bad Gateway</code></td>
|
||||||
|
<td>Caddy route points at a wrong/unreachable upstream port</td>
|
||||||
|
<td>Check the Caddyfile-as-Code view; fix the upstream host:port; re-apply</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>504 Gateway Timeout</code></td>
|
||||||
|
<td>Upstream is up but too slow to respond within the proxy timeout</td>
|
||||||
|
<td>Inspect container logs; increase Caddy proxy timeout if the app legitimately needs more time</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Hostname does not resolve</td>
|
||||||
|
<td>Client is not using Technitium as its resolver, or the record was not created</td>
|
||||||
|
<td>Point client DNS at Technitium; verify the record exists; re-run DNS step</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>DNS_TOKEN_INVALID</code></td>
|
||||||
|
<td>Technitium API token expired or revoked</td>
|
||||||
|
<td>Regenerate the token in Technitium; update it under Settings → DNS</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>PROXY_CADDY_UNREACHABLE</code></td>
|
||||||
|
<td>Caddy Admin API (localhost:2019) is down or firewalled</td>
|
||||||
|
<td>Restart Caddy; confirm the Admin API port is open to DashCaddy</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>DEPLOY_PORT_CONFLICT</code></td>
|
||||||
|
<td>Another container already holds the requested host port</td>
|
||||||
|
<td>Stop the conflicting container or choose a different port</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>LICENSE_EXPIRED</code></td>
|
||||||
|
<td>Premium license expired past the 7-day grace period</td>
|
||||||
|
<td>Renew from Settings → Licensing; free-tier features remain available</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>LICENSE_MACHINE_LIMIT</code></td>
|
||||||
|
<td>License already bound to another machine</td>
|
||||||
|
<td>Deactivate on the old host before activating on the new one</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>AUTH_PERMISSION_DENIED</code></td>
|
||||||
|
<td>User/API key lacks the RBAC role for the action</td>
|
||||||
|
<td>Assign the needed role in Settings → Users</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>WebSocket updates stall</td>
|
||||||
|
<td>A reverse proxy or firewall is dropping the WS upgrade</td>
|
||||||
|
<td>Allow WebSocket upgrades on the DashCaddy route in Caddy/firewall</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>429 Too Many Requests</code></td>
|
||||||
|
<td>API client exceeded the per-token rate limit</td>
|
||||||
|
<td>Back off and retry after <code>Retry-After</code>; switch polling to WS/Prometheus</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h2>Debug mode</h2>
|
||||||
|
<p>
|
||||||
|
When the standard checks do not reveal the problem, enable debug logging for verbose output from every layer.
|
||||||
|
Set the <code>LOG_LEVEL</code> environment variable to <code>debug</code> and restart DashCaddy:
|
||||||
|
</p>
|
||||||
|
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`# 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`}</code></pre>
|
||||||
|
<p>
|
||||||
|
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.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>Support resources</h2>
|
||||||
|
<p>
|
||||||
|
If you have worked through the layers above and are still stuck, the following resources can help:
|
||||||
|
</p>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Integrations guide</strong> — <a href="/docs/integrations">Infrastructure Integrations</a> explains what each layer expects and how to configure it.</li>
|
||||||
|
<li><strong>API error reference</strong> — the <a href="/docs/api">API and Automation</a> guide lists all 80 structured error codes across 12 modules.</li>
|
||||||
|
<li><strong>Installation</strong> — <a href="/docs/installation">Installation Guide</a> covers first-run setup and the Smart Defaults Wizard.</li>
|
||||||
|
<li><strong>Premium / licensing</strong> — <a href="/docs/premium">Premium Features</a> covers license validation, grace periods, and machine binding.</li>
|
||||||
|
<li><strong>Priority support</strong> — Premium license holders get priority support. Open a ticket from Settings → Support in the dashboard.</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h2>Mindset</h2>
|
<h2>Mindset</h2>
|
||||||
<p>
|
<p>
|
||||||
Most DashCaddy problems are really one dependency layer failing while the others are healthy. Use the health
|
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. Fixing the right layer first is always faster than
|
endpoints to localize, then walk the debug order from the container outward. Fixing the right layer first is
|
||||||
reloading the whole stack.
|
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.
|
||||||
</p>
|
</p>
|
||||||
</DocsLayout>
|
</DocsLayout>
|
||||||
<Footer />
|
<Footer />
|
||||||
|
|||||||
@@ -16,10 +16,10 @@ import Footer from '@/components/Footer';
|
|||||||
// pro-365d → $99, 365-day license (one-time payment)
|
// pro-365d → $99, 365-day license (one-time payment)
|
||||||
// ─────────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────────
|
||||||
const STRIPE_LINKS: Record<string, string> = {
|
const STRIPE_LINKS: Record<string, string> = {
|
||||||
'30d': 'https://buy.stripe.com/REPLACE_30D_LINK',
|
'30d': 'https://buy.stripe.com/7sY9AVgJm35P6Uo5j904800',
|
||||||
'90d': 'https://buy.stripe.com/REPLACE_90D_LINK',
|
'90d': 'https://buy.stripe.com/bJe6oJ0Ko7m5emQ4f504801',
|
||||||
'180d': 'https://buy.stripe.com/REPLACE_180D_LINK',
|
'180d': 'https://buy.stripe.com/4gMeVfct635P2E826X04802',
|
||||||
'365d': 'https://buy.stripe.com/REPLACE_365D_LINK',
|
'365d': 'https://buy.stripe.com/8x228tct65dXdiM9zp04803',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function PricingPage() {
|
export default function PricingPage() {
|
||||||
|
|||||||
Reference in New Issue
Block a user