Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
03b8242467 | ||
|
|
ac2feb5ab7 | ||
|
|
e58130756e | ||
|
|
e2b4ce4447 | ||
|
|
414c962d3c | ||
|
|
dcb8eeda4e | ||
|
|
81493a9076 | ||
|
|
fbb6db24d2 | ||
|
|
e51419a302 | ||
|
|
5905176a74 | ||
|
|
8c4aeb6065 | ||
|
|
843d85519f | ||
|
|
7b910aa913 | ||
|
|
8ba4c1b479 | ||
|
|
09113f1c19 | ||
|
|
b16671ccee | ||
|
|
8998c585d0 | ||
|
|
be8953fe3b | ||
|
|
6165b8e464 | ||
|
|
8579a29185 | ||
|
|
15eabc4dc0 |
@@ -28,3 +28,4 @@ STRIPE_PAYMENT_LINK_ANNUAL=https://buy.stripe.com/REPLACE_ANNUAL_LINK
|
||||
|
||||
# App URL
|
||||
NEXT_PUBLIC_APP_URL=https://dashcaddy.net
|
||||
NEXT_PUBLIC_LOOKUP_URL=https://licenses.dashcaddy.net/api/checkout/session
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
const nextConfig = {
|
||||
output: "export",
|
||||
trailingSlash: true,
|
||||
images: { unoptimized: true }
|
||||
};
|
||||
export default nextConfig;
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
# Enable directory index resolution (serves index.html from subdirectories)
|
||||
DirectoryIndex index.html index.html.var
|
||||
|
||||
# Redirect old doc paths to new ones — using RewriteRule for broader compatibility
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine On
|
||||
|
||||
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 25 KiB |
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
# Regenerate the /docs/catalog tree from the current
|
||||
# /opt/dashcaddy/dashcaddy-api/src/docker/app-templates.js.
|
||||
#
|
||||
# Run this whenever templates are added/changed. Outputs 78 static pages
|
||||
# under src/app/docs/catalog/ (1 index + 77 per-template).
|
||||
#
|
||||
# Idempotent — overwrites in place.
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
node /tmp/generate-template-docs.js
|
||||
echo "Verifying build..."
|
||||
npx --no-install next build 2>&1 | tail -3
|
||||
echo "Done. Review with: git diff --stat src/app/docs/catalog/"
|
||||
@@ -1,204 +1,204 @@
|
||||
import Navbar from "@/components/Navbar";
|
||||
import Footer from "@/components/Footer";
|
||||
import Link from "next/link";
|
||||
|
||||
export default function AboutPage() {
|
||||
return (
|
||||
<>
|
||||
<Navbar />
|
||||
<main className="flex-1">
|
||||
{/* Hero */}
|
||||
<section className="relative pt-32 pb-20 px-4">
|
||||
<div className="absolute inset-0 hero-glow pointer-events-none" />
|
||||
<div className="max-w-4xl mx-auto text-center relative z-10">
|
||||
<h1 className="text-4xl md:text-5xl font-bold text-white mb-6">
|
||||
Built for the{" "}
|
||||
<span className="gradient-text">Self-Hosting Community</span>
|
||||
</h1>
|
||||
<p className="text-xl text-surface-300 max-w-2xl mx-auto">
|
||||
DashCaddy was born from the frustration of managing dozens of
|
||||
Docker containers, SSL certificates, and DNS records by hand. We
|
||||
built the tool we wished existed — and then made it AI-powered.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Story */}
|
||||
<section className="py-20 px-4">
|
||||
<div className="max-w-3xl mx-auto">
|
||||
<h2 className="text-2xl font-bold text-white mb-6">Our Story</h2>
|
||||
<div className="space-y-4 text-surface-300 leading-relaxed">
|
||||
<p>
|
||||
Self-hosting is powerful. You own your data, you control your
|
||||
infrastructure, and you're not at the mercy of SaaS
|
||||
providers who can change their terms, raise prices, or shut down
|
||||
overnight. But let's be honest — it can also be a
|
||||
pain.
|
||||
</p>
|
||||
<p>
|
||||
Every new service means editing Caddyfiles, creating DNS
|
||||
records, configuring SSL certificates, writing Docker Compose
|
||||
files, and hoping everything plays nicely together. Multiply that
|
||||
by 20, 30, or 50 services, and you've got a full-time
|
||||
operations job on your hands.
|
||||
</p>
|
||||
<p>
|
||||
DashCaddy was built to solve this. One click to deploy an app.
|
||||
SSL, DNS, and reverse proxy configuration happen automatically.
|
||||
A beautiful dashboard with real-time WebSocket updates to monitor
|
||||
everything. Natural language AI commands so you can just say
|
||||
“deploy Plex” and it happens. And when something
|
||||
goes wrong, you know about it immediately — not when a
|
||||
family member texts you that Plex is down.
|
||||
</p>
|
||||
<p>
|
||||
We believe self-hosting should be accessible to everyone, not
|
||||
just people who enjoy writing YAML at 2 AM. DashCaddy makes it
|
||||
beautiful, effortless, and intelligent.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Values */}
|
||||
<section className="py-20 px-4 border-t border-surface-800">
|
||||
<div className="max-w-5xl mx-auto">
|
||||
<h2 className="text-2xl font-bold text-white mb-12 text-center">
|
||||
What We Believe In
|
||||
</h2>
|
||||
<div className="grid md:grid-cols-3 gap-8">
|
||||
{[
|
||||
{
|
||||
icon: "🔒",
|
||||
title: "Proprietary & Polished",
|
||||
description:
|
||||
"DashCaddy is proprietary software built with care. The core platform is free to use, with Premium features for advanced orchestration.",
|
||||
},
|
||||
{
|
||||
icon: "🏠",
|
||||
title: "Your Data, Your Server",
|
||||
description:
|
||||
"DashCaddy runs entirely on your hardware. No cloud dependency, no telemetry, no phoning home. Your data never leaves your network.",
|
||||
},
|
||||
{
|
||||
icon: "🤖",
|
||||
title: "AI-Native",
|
||||
description:
|
||||
"From the AI Intent Router to MCP server integration, DashCaddy is built for the AI era. Control your homelab with natural language or let AI assistants manage it for you.",
|
||||
},
|
||||
].map((value) => (
|
||||
<div
|
||||
key={value.title}
|
||||
className="glass-card rounded-xl p-8 text-center"
|
||||
>
|
||||
<div className="text-4xl mb-4">{value.icon}</div>
|
||||
<h3 className="text-lg font-semibold text-white mb-3">
|
||||
{value.title}
|
||||
</h3>
|
||||
<p className="text-surface-400">{value.description}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Tech Stack */}
|
||||
<section className="py-20 px-4 border-t border-surface-800">
|
||||
<div className="max-w-5xl mx-auto">
|
||||
<h2 className="text-2xl font-bold text-white mb-12 text-center">
|
||||
Built With
|
||||
</h2>
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-6">
|
||||
{[
|
||||
{ name: "Caddy", role: "Reverse Proxy & SSL", icon: "🔒" },
|
||||
{ name: "Docker", role: "Container Runtime", icon: "🐳" },
|
||||
{ name: "Node.js", role: "API Backend", icon: "🟢" },
|
||||
{ name: "Technitium", role: "DNS Server", icon: "🌐" },
|
||||
{ name: "Express", role: "REST API + WebSocket", icon: "⚡" },
|
||||
{ name: "MCP", role: "AI Protocol Layer", icon: "🤖" },
|
||||
{ name: "Stripe", role: "Payment Processing", icon: "💳" },
|
||||
{ name: "Jest", role: "Testing (775+ tests)", icon: "✅" },
|
||||
].map((tech) => (
|
||||
<div
|
||||
key={tech.name}
|
||||
className="glass-card rounded-xl p-6 text-center hover:border-brand-500/30 transition-colors"
|
||||
>
|
||||
<div className="text-3xl mb-3">{tech.icon}</div>
|
||||
<div className="font-semibold text-white">{tech.name}</div>
|
||||
<div className="text-sm text-surface-400">{tech.role}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Stats */}
|
||||
<section className="py-20 px-4 border-t border-surface-800">
|
||||
<div className="max-w-4xl mx-auto">
|
||||
<h2 className="text-2xl font-bold text-white mb-12 text-center">
|
||||
By the Numbers
|
||||
</h2>
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-8">
|
||||
{[
|
||||
{ value: "92+", label: "App Templates" },
|
||||
{ value: "775+", label: "Test Cases" },
|
||||
{ value: "39", label: "SDK Methods" },
|
||||
{ value: "5", label: "Languages" },
|
||||
].map((stat) => (
|
||||
<div key={stat.label} className="text-center">
|
||||
<div className="text-4xl font-bold text-brand-400 mb-2">
|
||||
{stat.value}
|
||||
</div>
|
||||
<p className="text-sm text-surface-400">{stat.label}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Contact / Support */}
|
||||
<section className="py-20 px-4 border-t border-surface-800">
|
||||
<div className="max-w-3xl mx-auto text-center">
|
||||
<h2 className="text-2xl font-bold text-white mb-6">Get In Touch</h2>
|
||||
<p className="text-surface-300 mb-8">
|
||||
Have questions or feedback? We'd love to hear from you.
|
||||
</p>
|
||||
<div className="flex flex-col sm:flex-row gap-4 justify-center">
|
||||
<a
|
||||
href="mailto:support@dashcaddy.net"
|
||||
className="px-6 py-3 rounded-lg bg-brand-600 hover:bg-brand-500 text-white font-medium transition-colors"
|
||||
>
|
||||
Email Us
|
||||
</a>
|
||||
<Link
|
||||
href="/docs"
|
||||
className="px-6 py-3 rounded-lg border border-surface-700 hover:border-surface-500 text-surface-300 font-medium transition-colors"
|
||||
>
|
||||
Read the Docs
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* CTA */}
|
||||
<section className="py-20 px-4 border-t border-surface-800">
|
||||
<div className="max-w-3xl mx-auto text-center">
|
||||
<h2 className="text-3xl font-bold text-white mb-4">
|
||||
Ready to simplify your homelab?
|
||||
</h2>
|
||||
<p className="text-surface-300 mb-8">
|
||||
Install DashCaddy and start deploying services today. Free forever.
|
||||
</p>
|
||||
<Link
|
||||
href="/pricing"
|
||||
className="inline-block px-8 py-4 rounded-lg bg-brand-600 hover:bg-brand-500 text-white font-semibold text-lg transition-colors"
|
||||
>
|
||||
View Pricing
|
||||
</Link>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<Footer />
|
||||
</>
|
||||
);
|
||||
}
|
||||
import Navbar from "@/components/Navbar";
|
||||
import Footer from "@/components/Footer";
|
||||
import Link from "next/link";
|
||||
|
||||
export default function AboutPage() {
|
||||
return (
|
||||
<>
|
||||
<Navbar />
|
||||
<main className="flex-1">
|
||||
{/* Hero */}
|
||||
<section className="relative pt-32 pb-20 px-4">
|
||||
<div className="absolute inset-0 hero-glow pointer-events-none" />
|
||||
<div className="max-w-4xl mx-auto text-center relative z-10">
|
||||
<h1 className="text-4xl md:text-5xl font-bold text-white mb-6">
|
||||
Built for the{" "}
|
||||
<span className="gradient-text">Self-Hosting Community</span>
|
||||
</h1>
|
||||
<p className="text-xl text-surface-300 max-w-2xl mx-auto">
|
||||
DashCaddy was born from the frustration of managing dozens of
|
||||
Docker containers, SSL certificates, and DNS records by hand. We
|
||||
built the tool we wished existed — and then made it AI-powered.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Story */}
|
||||
<section className="py-20 px-4">
|
||||
<div className="max-w-3xl mx-auto">
|
||||
<h2 className="text-2xl font-bold text-white mb-6">Our Story</h2>
|
||||
<div className="space-y-4 text-surface-300 leading-relaxed">
|
||||
<p>
|
||||
Self-hosting is powerful. You own your data, you control your
|
||||
infrastructure, and you're not at the mercy of SaaS
|
||||
providers who can change their terms, raise prices, or shut down
|
||||
overnight. But let's be honest — it can also be a
|
||||
pain.
|
||||
</p>
|
||||
<p>
|
||||
Every new service means editing Caddyfiles, creating DNS
|
||||
records, configuring SSL certificates, writing Docker Compose
|
||||
files, and hoping everything plays nicely together. Multiply that
|
||||
by 20, 30, or 50 services, and you've got a full-time
|
||||
operations job on your hands.
|
||||
</p>
|
||||
<p>
|
||||
DashCaddy was built to solve this. One click to deploy an app.
|
||||
SSL, DNS, and reverse proxy configuration happen automatically.
|
||||
A beautiful dashboard with real-time WebSocket updates to monitor
|
||||
everything. Natural language AI commands so you can just say
|
||||
“deploy Plex” and it happens. And when something
|
||||
goes wrong, you know about it immediately — not when a
|
||||
family member texts you that Plex is down.
|
||||
</p>
|
||||
<p>
|
||||
We believe self-hosting should be accessible to everyone, not
|
||||
just people who enjoy writing YAML at 2 AM. DashCaddy makes it
|
||||
beautiful, effortless, and intelligent.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Values */}
|
||||
<section className="py-20 px-4 border-t border-surface-800">
|
||||
<div className="max-w-5xl mx-auto">
|
||||
<h2 className="text-2xl font-bold text-white mb-12 text-center">
|
||||
What We Believe In
|
||||
</h2>
|
||||
<div className="grid md:grid-cols-3 gap-8">
|
||||
{[
|
||||
{
|
||||
icon: "🔒",
|
||||
title: "Proprietary & Polished",
|
||||
description:
|
||||
"DashCaddy is proprietary software built with care. The core platform is free to use, with Premium features for advanced orchestration.",
|
||||
},
|
||||
{
|
||||
icon: "🏠",
|
||||
title: "Your Data, Your Server",
|
||||
description:
|
||||
"DashCaddy runs entirely on your hardware. No cloud dependency, no telemetry, no phoning home. Your data never leaves your network.",
|
||||
},
|
||||
{
|
||||
icon: "🤖",
|
||||
title: "AI-Native",
|
||||
description:
|
||||
"From the AI Intent Router to MCP server integration, DashCaddy is built for the AI era. Control your homelab with natural language or let AI assistants manage it for you.",
|
||||
},
|
||||
].map((value) => (
|
||||
<div
|
||||
key={value.title}
|
||||
className="glass-card rounded-xl p-8 text-center"
|
||||
>
|
||||
<div className="text-4xl mb-4">{value.icon}</div>
|
||||
<h3 className="text-lg font-semibold text-white mb-3">
|
||||
{value.title}
|
||||
</h3>
|
||||
<p className="text-surface-400">{value.description}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Tech Stack */}
|
||||
<section className="py-20 px-4 border-t border-surface-800">
|
||||
<div className="max-w-5xl mx-auto">
|
||||
<h2 className="text-2xl font-bold text-white mb-12 text-center">
|
||||
Built With
|
||||
</h2>
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-6">
|
||||
{[
|
||||
{ name: "Caddy", role: "Reverse Proxy & SSL", icon: "🔒" },
|
||||
{ name: "Docker", role: "Container Runtime", icon: "🐳" },
|
||||
{ name: "Node.js", role: "API Backend", icon: "🟢" },
|
||||
{ name: "Technitium", role: "DNS Server", icon: "🌐" },
|
||||
{ name: "Express", role: "REST API + WebSocket", icon: "⚡" },
|
||||
{ name: "MCP", role: "AI Protocol Layer", icon: "🤖" },
|
||||
{ name: "Stripe", role: "Payment Processing", icon: "💳" },
|
||||
{ name: "Jest", role: "Testing (2,500+ tests)", icon: "✅" },
|
||||
].map((tech) => (
|
||||
<div
|
||||
key={tech.name}
|
||||
className="glass-card rounded-xl p-6 text-center hover:border-brand-500/30 transition-colors"
|
||||
>
|
||||
<div className="text-3xl mb-3">{tech.icon}</div>
|
||||
<div className="font-semibold text-white">{tech.name}</div>
|
||||
<div className="text-sm text-surface-400">{tech.role}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Stats */}
|
||||
<section className="py-20 px-4 border-t border-surface-800">
|
||||
<div className="max-w-4xl mx-auto">
|
||||
<h2 className="text-2xl font-bold text-white mb-12 text-center">
|
||||
By the Numbers
|
||||
</h2>
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-8">
|
||||
{[
|
||||
{ value: "77", label: "App Templates" },
|
||||
{ value: "2,500+", label: "Test Cases" },
|
||||
{ value: "350+", label: "API Endpoints" },
|
||||
{ value: "31", label: "Languages" },
|
||||
].map((stat) => (
|
||||
<div key={stat.label} className="text-center">
|
||||
<div className="text-4xl font-bold text-brand-400 mb-2">
|
||||
{stat.value}
|
||||
</div>
|
||||
<p className="text-sm text-surface-400">{stat.label}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Contact / Support */}
|
||||
<section className="py-20 px-4 border-t border-surface-800">
|
||||
<div className="max-w-3xl mx-auto text-center">
|
||||
<h2 className="text-2xl font-bold text-white mb-6">Get In Touch</h2>
|
||||
<p className="text-surface-300 mb-8">
|
||||
Have questions or feedback? We'd love to hear from you.
|
||||
</p>
|
||||
<div className="flex flex-col sm:flex-row gap-4 justify-center">
|
||||
<a
|
||||
href="mailto:support@dashcaddy.net"
|
||||
className="px-6 py-3 rounded-lg bg-brand-600 hover:bg-brand-500 text-white font-medium transition-colors"
|
||||
>
|
||||
Email Us
|
||||
</a>
|
||||
<Link
|
||||
href="/docs"
|
||||
className="px-6 py-3 rounded-lg border border-surface-700 hover:border-surface-500 text-surface-300 font-medium transition-colors"
|
||||
>
|
||||
Read the Docs
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* CTA */}
|
||||
<section className="py-20 px-4 border-t border-surface-800">
|
||||
<div className="max-w-3xl mx-auto text-center">
|
||||
<h2 className="text-3xl font-bold text-white mb-4">
|
||||
Ready to simplify your homelab?
|
||||
</h2>
|
||||
<p className="text-surface-300 mb-8">
|
||||
Install DashCaddy and start deploying services today. Free forever.
|
||||
</p>
|
||||
<Link
|
||||
href="/pricing"
|
||||
className="inline-block px-8 py-4 rounded-lg bg-brand-600 hover:bg-brand-500 text-white font-semibold text-lg transition-colors"
|
||||
>
|
||||
View Pricing
|
||||
</Link>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<Footer />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ export default function DocsApiPage() {
|
||||
>
|
||||
<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
|
||||
API, a JavaScript automation layer, 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>
|
||||
@@ -60,7 +60,7 @@ curl -X POST -H "Authorization: Bearer ***" \\
|
||||
</p>
|
||||
<h3>API key (Bearer token)</h3>
|
||||
<p>
|
||||
For server-to-server automation, scripts, and the SDK, use an API key. Generate keys from
|
||||
For server-to-server automation, scripts, and integrations, 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>
|
||||
@@ -86,46 +86,59 @@ curl -X POST -H "Authorization: Bearer ***" \\
|
||||
frequent reads and do not count against the REST rate limit.
|
||||
</p>
|
||||
|
||||
<h2>JavaScript SDK</h2>
|
||||
<h2>JavaScript automation</h2>
|
||||
<p>
|
||||
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
|
||||
error handling for you. Install it from npm and use it in Node.js, Deno, Bun, or the browser.
|
||||
For programmatic automation, use the REST API directly with <code>fetch</code> or any HTTP client. The API is
|
||||
JSON-based, uses Bearer token authentication, and returns structured error codes. Here is a minimal helper
|
||||
you can drop into any Node.js, Bun, or browser project:
|
||||
</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';
|
||||
|
||||
const dc = new DashCaddy({
|
||||
baseUrl: 'https://dashcaddy-host',
|
||||
token: process.env.DC_TOKEN,
|
||||
});
|
||||
|
||||
// List services
|
||||
const services = await dc.services.list();
|
||||
|
||||
// Deploy a template
|
||||
const svc = await dc.services.deploy({
|
||||
template: 'jellyfin',
|
||||
name: 'media',
|
||||
hostname: 'media.lab',
|
||||
});
|
||||
|
||||
// Adopt a discovered container
|
||||
await dc.services.adopt({ containerId: 'abc123', hostname: 'wiki.lab' });
|
||||
|
||||
// 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>
|
||||
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{['class DashCaddy {', '',
|
||||
' constructor(opts) {', '',
|
||||
' this.baseUrl = opts.baseUrl;', '',
|
||||
' this.token = opts.token;', '',
|
||||
' }', '',
|
||||
'', '',
|
||||
' async request(path, options) {', '',
|
||||
' options = options || {};', '',
|
||||
' var url = this.baseUrl + "/api/v1" + path;', '',
|
||||
' var res = await fetch(url, {', '',
|
||||
' method: options.method || "GET",', '',
|
||||
' body: options.body,', '',
|
||||
' headers: {', '',
|
||||
' "Content-Type": "application/json",', '',
|
||||
' "Authorization": "Bearer " + this.token', '',
|
||||
' }', '',
|
||||
' });', '',
|
||||
' var body = await res.json();', '',
|
||||
' if (!res.ok) throw { code: body.error, status: res.status };', '',
|
||||
' return body;', '',
|
||||
' }', '',
|
||||
'', '',
|
||||
' // List services', '',
|
||||
' services() { return this.request("/services"); }', '',
|
||||
'', '',
|
||||
' // Deploy from template', '',
|
||||
' deploy(template, name, hostname) {', '',
|
||||
' return this.request("/services", {', '',
|
||||
' method: "POST",', '',
|
||||
' body: JSON.stringify({ template, name, hostname })', '',
|
||||
' });', '',
|
||||
' }', '',
|
||||
'', '',
|
||||
' // Restart a service', '',
|
||||
' restart(id) {', '',
|
||||
' return this.request("/services/" + id + "/restart", { method: "POST" });', '',
|
||||
' }', '',
|
||||
'}'].join('\n')}</code></pre>
|
||||
<p>
|
||||
Every SDK method returns a typed result or throws a structured <code>DashCaddyError</code> carrying the error
|
||||
Every request returns a structured JSON response or throws an error object carrying the error
|
||||
code, HTTP status, and message — so your automation can branch on specific failure conditions.
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h2>AI Intent Router</h2>
|
||||
<p>
|
||||
The <strong>AI Intent Router</strong> accepts natural-language commands and translates them into real
|
||||
@@ -271,7 +284,7 @@ GET /readyz`}</code></pre>
|
||||
|
||||
<h2>Structured error codes</h2>
|
||||
<p>
|
||||
The API and SDK return <strong>80 structured error codes</strong> across <strong>12 modules</strong> rather
|
||||
The API returns <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.
|
||||
@@ -316,9 +329,9 @@ GET /readyz`}</code></pre>
|
||||
<h2>Why automation matters</h2>
|
||||
<p>
|
||||
DashCaddy can execute the full infrastructure chain around a service, not just report its state after the fact.
|
||||
Between the REST API, the JS SDK, the AI Intent Router, MCP, WebSockets, Prometheus, and the plugin system, you
|
||||
Between the REST API, the AI Intent Router, MCP, WebSockets, Prometheus, and the plugin system, you
|
||||
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.
|
||||
simple <code>curl</code> call, 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
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Actual Budget — DashCaddy Docs',
|
||||
description: 'Install and configure Actual Budget via DashCaddy. Privacy-focused budgeting app with envelope budgeting',
|
||||
};
|
||||
|
||||
export default function actualBudgetDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Actual Budget"
|
||||
intro="Privacy-focused budgeting app with envelope budgeting"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Productivity</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">actualbudget/actual-server:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Actual Budget?</h2>
|
||||
<p>Privacy-focused budgeting app with envelope budgeting</p>
|
||||
<p>Actual Budget ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Actual Budget, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Actual Budget</strong> from the Productivity category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>budget</code>), host port (default: <code>5006</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "actual-budget",
|
||||
"config": {
|
||||
"subdomain": "budget",
|
||||
"port": 5006
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Actual Budget on my home host and expose it at budget.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull actualbudget/actual-server:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Create your first budget in the web interface</li>
|
||||
<li>Import transactions from your bank (OFX, QFX, CSV)</li>
|
||||
<li>Set up envelope categories for spending control</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/actual-budget/data:/data</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<p>None. The container runs with its upstream defaults.</p><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull actualbudget/actual-server:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Actual Budget:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>actual-budget</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Adminer — DashCaddy Docs',
|
||||
description: 'Install and configure Adminer via DashCaddy. Lightweight database management in single PHP file',
|
||||
};
|
||||
|
||||
export default function adminerDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Adminer"
|
||||
intro="Lightweight database management in single PHP file"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Database</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">adminer:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Adminer?</h2>
|
||||
<p>Lightweight database management in single PHP file</p>
|
||||
<p>Adminer ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Adminer, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Adminer</strong> from the Database category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>adminer</code>), host port (default: <code>8087</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "adminer",
|
||||
"config": {
|
||||
"subdomain": "adminer",
|
||||
"port": 8087
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Adminer on my home host and expose it at adminer.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull adminer:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Connect to your database servers</li>
|
||||
<li>Supports MySQL, PostgreSQL, SQLite, etc.</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/adminer:/var/www/html</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>ADMINER_DEFAULT_SERVER</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull adminer:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Adminer:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>adminer</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Airsonic Advanced — DashCaddy Docs',
|
||||
description: 'Install and configure Airsonic Advanced via DashCaddy. Free web-based media streamer',
|
||||
};
|
||||
|
||||
export default function airsonicDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Airsonic Advanced"
|
||||
intro="Free web-based media streamer"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Media</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">linuxserver/airsonic-advanced:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Airsonic Advanced?</h2>
|
||||
<p>Free web-based media streamer</p>
|
||||
<p>Airsonic Advanced ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Airsonic Advanced, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Airsonic Advanced</strong> from the Media category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>airsonic</code>), host port (default: <code>4040</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "airsonic",
|
||||
"config": {
|
||||
"subdomain": "airsonic",
|
||||
"port": 4040
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Airsonic Advanced on my home host and expose it at airsonic.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull linuxserver/airsonic-advanced:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Default login: admin/admin</li>
|
||||
<li>Configure media folders</li>
|
||||
<li>Set up transcoding</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/airsonic/config:/config</code></li>
|
||||
<li><code>/music:/music</code></li>
|
||||
<li><code>/podcasts:/podcasts</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>PUID</code></li>
|
||||
<li><code>PGID</code></li>
|
||||
<li><code>TZ</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull linuxserver/airsonic-advanced:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Airsonic Advanced:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>airsonic</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Audiobookshelf — DashCaddy Docs',
|
||||
description: 'Install and configure Audiobookshelf via DashCaddy. Self-hosted audiobook and podcast server',
|
||||
};
|
||||
|
||||
export default function audiobookshelfDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Audiobookshelf"
|
||||
intro="Self-hosted audiobook and podcast server"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Media</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">ghcr.io/advplyr/audiobookshelf:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Audiobookshelf?</h2>
|
||||
<p>Self-hosted audiobook and podcast server</p>
|
||||
<p>Audiobookshelf ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Audiobookshelf, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>A host path containing your media. Default suggestion: <code>/media/audiobooks</code>. The deploy form / API payload <code>config.mediaPath</code> must be readable by the container UID (usually <code>1000</code>).</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Audiobookshelf</strong> from the Media category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>audiobooks</code>), host port (default: <code>13378</code>), and the media library path.</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "audiobookshelf",
|
||||
"config": {
|
||||
"subdomain": "audiobooks",
|
||||
"port": 13378,
|
||||
"mediaPath": "/media/audiobooks"
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Audiobookshelf on my home host and expose it at audiobooks.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull ghcr.io/advplyr/audiobookshelf:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Create your account on first access</li>
|
||||
<li>Add your audiobook library folders</li>
|
||||
<li>Download the mobile app for offline listening</li>
|
||||
</ol>
|
||||
<h2>Media library path notes</h2>
|
||||
<p>The media mount path you pass as <code>mediaPath</code> in the deploy payload is mounted as <code>/audiobooks</code> inside the container. Bind a host directory containing your media library (movies, TV shows, music, etc.).</p>
|
||||
<ul>
|
||||
<li><strong>UID/GID:</strong> Audiobookshelf runs as a non-root user. If you see permission errors in the dashboard Logs tab, run <code>chown -R 1000:1000 /media/audiobooks</code> on the host.</li>
|
||||
<li><strong>Multi-library:</strong> bind the parent folder and let Audiobookshelf discover subfolders.</li>
|
||||
</ul>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/audiobookshelf/config:/config</code></li>
|
||||
<li><code>/opt/audiobookshelf/metadata:/metadata</code></li>
|
||||
<li><code>MEDIA_PATH:/audiobooks</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<p>None. The container runs with its upstream defaults.</p><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull ghcr.io/advplyr/audiobookshelf:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Audiobookshelf:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>Library shows empty:</strong> confirm <code>mediaPath</code> is readable by the container UID and that the directory contains the file extensions Audiobookshelf indexes.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>audiobookshelf</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Authentik — DashCaddy Docs',
|
||||
description: 'Install and configure Authentik via DashCaddy. Identity provider and single sign-on platform',
|
||||
};
|
||||
|
||||
export default function authentikDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Authentik"
|
||||
intro="Identity provider and single sign-on platform"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Security</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#f5a62322', color: '#f5a623' }}>Difficulty: Advanced</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">ghcr.io/goauthentik/server:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Authentik?</h2>
|
||||
<p>Identity provider and single sign-on platform</p>
|
||||
<p>Authentik ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Authentik, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Authentik</strong> from the Security category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>auth</code>), host port (default: <code>9010</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/-/health/live/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "authentik",
|
||||
"config": {
|
||||
"subdomain": "auth",
|
||||
"port": 9010
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Authentik on my home host and expose it at auth.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull ghcr.io/goauthentik/server:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Requires a PostgreSQL database and Redis instance</li>
|
||||
<li>Consider deploying via the Dev Environment recipe for full stack</li>
|
||||
<li>Set up flows for authentication, enrollment, and recovery</li>
|
||||
<li>Configure OAuth2/OIDC providers for SSO with other apps</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/authentik/media:/media</code></li>
|
||||
<li><code>/opt/authentik/templates:/templates</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>AUTHENTIK_SECRET_KEY</code></li>
|
||||
<li><code>AUTHENTIK_ERROR_REPORTING__ENABLED</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull ghcr.io/goauthentik/server:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Authentik:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/-/health/live/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>authentik</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Bazarr — DashCaddy Docs',
|
||||
description: 'Install and configure Bazarr via DashCaddy. Automatic subtitle downloader for Sonarr and Radarr',
|
||||
};
|
||||
|
||||
export default function bazarrDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Bazarr"
|
||||
intro="Automatic subtitle downloader for Sonarr and Radarr"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Media Management</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">linuxserver/bazarr:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Bazarr?</h2>
|
||||
<p>Automatic subtitle downloader for Sonarr and Radarr</p>
|
||||
<p>Bazarr ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Bazarr, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Bazarr</strong> from the Media Management category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>bazarr</code>), host port (default: <code>6767</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "bazarr",
|
||||
"config": {
|
||||
"subdomain": "bazarr",
|
||||
"port": 6767
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Bazarr on my home host and expose it at bazarr.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull linuxserver/bazarr:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Connect to Sonarr and Radarr</li>
|
||||
<li>Configure subtitle providers</li>
|
||||
<li>Set language preferences</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/bazarr/config:/config</code></li>
|
||||
<li><code>/movies:/movies</code></li>
|
||||
<li><code>/tv:/tv</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>PUID</code></li>
|
||||
<li><code>PGID</code></li>
|
||||
<li><code>TZ</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull linuxserver/bazarr:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Bazarr:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>bazarr</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install BIND9 DNS Server — DashCaddy Docs',
|
||||
description: 'Install and configure BIND9 DNS Server via DashCaddy. Industry-standard DNS server - powerful and flexible',
|
||||
};
|
||||
|
||||
export default function bind9DocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install BIND9 DNS Server"
|
||||
intro="Industry-standard DNS server - powerful and flexible"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: DNS</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#f5a62322', color: '#f5a623' }}>Difficulty: Advanced</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">ubuntu/bind9:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is BIND9 DNS Server?</h2>
|
||||
<p>Industry-standard DNS server - powerful and flexible</p>
|
||||
<p>BIND9 DNS Server ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using BIND9 DNS Server, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>BIND9 DNS Server</strong> from the DNS category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>dns2</code>), host port (default: <code>953</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>tcp://localhost:53</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "bind9",
|
||||
"config": {
|
||||
"subdomain": "dns2",
|
||||
"port": 953
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy BIND9 DNS Server on my home host and expose it at dns2.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull ubuntu/bind9:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Configure zone files in /opt/bind9/config/</li>
|
||||
<li>Create named.conf.local for your .sami zone</li>
|
||||
<li>Add zone file: /opt/bind9/records/db.sami</li>
|
||||
<li>Restart container to apply changes</li>
|
||||
<li>Test with: dig @localhost sami</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/bind9/config:/etc/bind</code></li>
|
||||
<li><code>/opt/bind9/cache:/var/cache/bind</code></li>
|
||||
<li><code>/opt/bind9/records:/var/lib/bind</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>BIND9_USER</code></li>
|
||||
<li><code>TZ</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull ubuntu/bind9:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with BIND9 DNS Server:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>tcp://localhost:53</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>bind9</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install BookStack — DashCaddy Docs',
|
||||
description: 'Install and configure BookStack via DashCaddy. Simple wiki and documentation platform',
|
||||
};
|
||||
|
||||
export default function bookstackDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install BookStack"
|
||||
intro="Simple wiki and documentation platform"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Productivity</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Difficulty: Intermediate</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">linuxserver/bookstack:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is BookStack?</h2>
|
||||
<p>Simple wiki and documentation platform</p>
|
||||
<p>BookStack ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using BookStack, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>BookStack</strong> from the Productivity category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>wiki</code>), host port (default: <code>8091</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "bookstack",
|
||||
"config": {
|
||||
"subdomain": "wiki",
|
||||
"port": 8091
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy BookStack on my home host and expose it at wiki.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull linuxserver/bookstack:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Requires MariaDB/MySQL database</li>
|
||||
<li>Default login: admin@admin.com / password</li>
|
||||
<li>Change default credentials</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/bookstack/config:/config</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>PUID</code></li>
|
||||
<li><code>PGID</code></li>
|
||||
<li><code>APP_URL</code></li>
|
||||
<li><code>DB_HOST</code></li>
|
||||
<li><code>DB_DATABASE</code></li>
|
||||
<li><code>DB_USERNAME</code></li>
|
||||
<li><code>DB_PASSWORD</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull linuxserver/bookstack:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with BookStack:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>bookstack</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Calibre-Web — DashCaddy Docs',
|
||||
description: 'Install and configure Calibre-Web via DashCaddy. Web-based ebook manager and reader',
|
||||
};
|
||||
|
||||
export default function calibreWebDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Calibre-Web"
|
||||
intro="Web-based ebook manager and reader"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Media</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Difficulty: Intermediate</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">lscr.io/linuxserver/calibre-web:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Calibre-Web?</h2>
|
||||
<p>Web-based ebook manager and reader</p>
|
||||
<p>Calibre-Web ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Calibre-Web, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>A host path containing your media. Default suggestion: <code>/media/books</code>. The deploy form / API payload <code>config.mediaPath</code> must be readable by the container UID (usually <code>1000</code>).</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Calibre-Web</strong> from the Media category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>books</code>), host port (default: <code>8083</code>), and the media library path.</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "calibre-web",
|
||||
"config": {
|
||||
"subdomain": "books",
|
||||
"port": 8083,
|
||||
"mediaPath": "/media/books"
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Calibre-Web on my home host and expose it at books.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull lscr.io/linuxserver/calibre-web:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Default login: admin / admin123</li>
|
||||
<li>Point to your Calibre database location on first setup</li>
|
||||
<li>Supports EPUB, PDF, MOBI, and more formats</li>
|
||||
</ol>
|
||||
<h2>Media library path notes</h2>
|
||||
<p>The media mount path you pass as <code>mediaPath</code> in the deploy payload is mounted as <code>/books</code> inside the container. Bind a host directory containing your media library (movies, TV shows, music, etc.).</p>
|
||||
<ul>
|
||||
<li><strong>UID/GID:</strong> Calibre-Web runs as a non-root user. If you see permission errors in the dashboard Logs tab, run <code>chown -R 1000:1000 /media/books</code> on the host.</li>
|
||||
<li><strong>Multi-library:</strong> bind the parent folder and let Calibre-Web discover subfolders.</li>
|
||||
</ul>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/calibre-web/config:/config</code></li>
|
||||
<li><code>MEDIA_PATH:/books</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>PUID</code></li>
|
||||
<li><code>PGID</code></li>
|
||||
<li><code>TZ</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull lscr.io/linuxserver/calibre-web:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Calibre-Web:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>Library shows empty:</strong> confirm <code>mediaPath</code> is readable by the container UID and that the directory contains the file extensions Calibre-Web indexes.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>calibre-web</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Change Detection — DashCaddy Docs',
|
||||
description: 'Install and configure Change Detection via DashCaddy. Monitor websites for changes',
|
||||
};
|
||||
|
||||
export default function changedetectionDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Change Detection"
|
||||
intro="Monitor websites for changes"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Utilities</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">ghcr.io/dgtlmoon/changedetection.io:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Change Detection?</h2>
|
||||
<p>Monitor websites for changes</p>
|
||||
<p>Change Detection ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Change Detection, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Change Detection</strong> from the Utilities category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>watch</code>), host port (default: <code>5001</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "changedetection",
|
||||
"config": {
|
||||
"subdomain": "watch",
|
||||
"port": 5001
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Change Detection on my home host and expose it at watch.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull ghcr.io/dgtlmoon/changedetection.io:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Add URLs to monitor</li>
|
||||
<li>Configure check frequency</li>
|
||||
<li>Set up notifications</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/changedetection/data:/datastore</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<p>None. The container runs with its upstream defaults.</p><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull ghcr.io/dgtlmoon/changedetection.io:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Change Detection:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>changedetection</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install CoreDNS — DashCaddy Docs',
|
||||
description: 'Install and configure CoreDNS via DashCaddy. Cloud-native DNS server - lightweight and flexible',
|
||||
};
|
||||
|
||||
export default function corednsDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install CoreDNS"
|
||||
intro="Cloud-native DNS server - lightweight and flexible"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: DNS</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Difficulty: Intermediate</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">coredns/coredns:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is CoreDNS?</h2>
|
||||
<p>Cloud-native DNS server - lightweight and flexible</p>
|
||||
<p>CoreDNS ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using CoreDNS, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>CoreDNS</strong> from the DNS category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>dns4</code>), host port (default: <code>53</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>tcp://localhost:53</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "coredns",
|
||||
"config": {
|
||||
"subdomain": "dns4",
|
||||
"port": 53
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy CoreDNS on my home host and expose it at dns4.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull coredns/coredns:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Create Corefile in /opt/coredns/config/</li>
|
||||
<li>Define .sami zone with file plugin</li>
|
||||
<li>Create zone file with your records</li>
|
||||
<li>Restart container to load config</li>
|
||||
<li>Test with: dig @localhost test.sami</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/coredns/config:/etc/coredns</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<p>None. The container runs with its upstream defaults.</p><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull coredns/coredns:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with CoreDNS:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>tcp://localhost:53</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>coredns</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install CrowdSec — DashCaddy Docs',
|
||||
description: 'Install and configure CrowdSec via DashCaddy. Collaborative intrusion prevention system',
|
||||
};
|
||||
|
||||
export default function crowdsecDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install CrowdSec"
|
||||
intro="Collaborative intrusion prevention system"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Security</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Difficulty: Intermediate</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">crowdsecurity/crowdsec:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is CrowdSec?</h2>
|
||||
<p>Collaborative intrusion prevention system</p>
|
||||
<p>CrowdSec ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using CrowdSec, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>CrowdSec</strong> from the Security category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>crowdsec</code>), host port (default: <code>8091</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/health</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "crowdsec",
|
||||
"config": {
|
||||
"subdomain": "crowdsec",
|
||||
"port": 8091
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy CrowdSec on my home host and expose it at crowdsec.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull crowdsecurity/crowdsec:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Register at app.crowdsec.net for community threat intelligence</li>
|
||||
<li>Install bouncers on your reverse proxy for active blocking</li>
|
||||
<li>CrowdSec analyzes logs and shares threat data with the community</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/crowdsec/config:/etc/crowdsec</code></li>
|
||||
<li><code>/opt/crowdsec/data:/var/lib/crowdsec/data</code></li>
|
||||
<li><code>/var/log:/var/log:ro</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<p>None. The container runs with its upstream defaults.</p><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull crowdsecurity/crowdsec:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with CrowdSec:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/health</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>crowdsec</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install DashCA — DashCaddy Docs',
|
||||
description: 'Install and configure DashCA via DashCaddy. One-click root CA certificate installer for your network',
|
||||
};
|
||||
|
||||
export default function dashcaDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install DashCA"
|
||||
intro="One-click root CA certificate installer for your network"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Security</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">N/A</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is DashCA?</h2>
|
||||
<p>One-click root CA certificate installer for your network</p>
|
||||
<p>DashCA ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using DashCA, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>DashCA</strong> from the Security category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>ca</code>), host port (default: <code>32400</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/healthz</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "dashca",
|
||||
"config": {
|
||||
"subdomain": "ca",
|
||||
"port": 32400
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy DashCA on my home host and expose it at ca.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull N/A</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>New devices: visit http://ca.sami (HTTP, no certificate needed)</li>
|
||||
<li>Click the 'Install Certificate' button for your platform</li>
|
||||
<li>Follow platform-specific instructions</li>
|
||||
<li>Verify all *.sami domains now show secure connections</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<p>None. The container runs with its upstream defaults.</p><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull N/A</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with DashCA:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/healthz</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>dashca</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Digital Clock — DashCaddy Docs',
|
||||
description: 'Install and configure Digital Clock via DashCaddy. Live digital clock with time, date, and day of week',
|
||||
};
|
||||
|
||||
export default function digitalClockDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Digital Clock"
|
||||
intro="Live digital clock with time, date, and day of week"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Utilities</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">N/A</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Digital Clock?</h2>
|
||||
<p>Live digital clock with time, date, and day of week</p>
|
||||
<p>Digital Clock ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Digital Clock, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Digital Clock</strong> from the Utilities category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>digital-clock</code>), host port (default: <code>32400</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/healthz</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "digital-clock",
|
||||
"config": {
|
||||
"subdomain": "digital-clock",
|
||||
"port": 32400
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Digital Clock on my home host and expose it at digital-clock.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull N/A</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Clock appears in the top bar to the right of the weather widget</li>
|
||||
<li>No configuration needed — runs automatically</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<p>None. The container runs with its upstream defaults.</p><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull N/A</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Digital Clock:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/healthz</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>digital-clock</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Dozzle — DashCaddy Docs',
|
||||
description: 'Install and configure Dozzle via DashCaddy. Real-time Docker container log viewer',
|
||||
};
|
||||
|
||||
export default function dozzleDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Dozzle"
|
||||
intro="Real-time Docker container log viewer"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Monitoring</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">amir20/dozzle:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Dozzle?</h2>
|
||||
<p>Real-time Docker container log viewer</p>
|
||||
<p>Dozzle ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Dozzle, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Dozzle</strong> from the Monitoring category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>logs</code>), host port (default: <code>8088</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "dozzle",
|
||||
"config": {
|
||||
"subdomain": "logs",
|
||||
"port": 8088
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Dozzle on my home host and expose it at logs.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull amir20/dozzle:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>View real-time logs from all running containers</li>
|
||||
<li>Filter and search across container logs</li>
|
||||
<li>No configuration needed - auto-discovers containers</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/var/run/docker.sock:/var/run/docker.sock:ro</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<p>None. The container runs with its upstream defaults.</p><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull amir20/dozzle:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Dozzle:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>dozzle</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Drone CI — DashCaddy Docs',
|
||||
description: 'Install and configure Drone CI via DashCaddy. Container-native continuous delivery platform',
|
||||
};
|
||||
|
||||
export default function droneDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Drone CI"
|
||||
intro="Container-native continuous delivery platform"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Development</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Difficulty: Intermediate</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">drone/drone:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Drone CI?</h2>
|
||||
<p>Container-native continuous delivery platform</p>
|
||||
<p>Drone CI ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Drone CI, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Drone CI</strong> from the Development category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>drone</code>), host port (default: <code>8090</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "drone",
|
||||
"config": {
|
||||
"subdomain": "drone",
|
||||
"port": 8090
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Drone CI on my home host and expose it at drone.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull drone/drone:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Configure Git provider integration</li>
|
||||
<li>Set up shared secret</li>
|
||||
<li>Deploy Drone runners</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/drone/data:/data</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>DRONE_GITEA_SERVER</code></li>
|
||||
<li><code>DRONE_RPC_SECRET</code></li>
|
||||
<li><code>DRONE_SERVER_HOST</code></li>
|
||||
<li><code>DRONE_SERVER_PROTO</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull drone/drone:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Drone CI:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>drone</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Emby — DashCaddy Docs',
|
||||
description: 'Install and configure Emby via DashCaddy. Personal media server with apps for all devices',
|
||||
};
|
||||
|
||||
export default function embyDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Emby"
|
||||
intro="Personal media server with apps for all devices"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Media</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">emby/embyserver:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Emby?</h2>
|
||||
<p>Personal media server with apps for all devices</p>
|
||||
<p>Emby ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Emby, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>A host path containing your media. Default suggestion: <code>/media</code>. The deploy form / API payload <code>config.mediaPath</code> must be readable by the container UID (usually <code>1000</code>).</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Emby</strong> from the Media category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>emby</code>), host port (default: <code>8096</code>), and the media library path.</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/emby/web/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "emby",
|
||||
"config": {
|
||||
"subdomain": "emby",
|
||||
"port": 8096,
|
||||
"mediaPath": "/media"
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Emby on my home host and expose it at emby.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull emby/embyserver:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Complete the initial setup wizard at the web interface</li>
|
||||
<li>Add your media libraries (Movies, TV Shows, Music)</li>
|
||||
<li>Configure user accounts and permissions</li>
|
||||
<li>Install Emby apps on your devices for remote access</li>
|
||||
</ol>
|
||||
<h2>Media library path notes</h2>
|
||||
<p>The media mount path you pass as <code>mediaPath</code> in the deploy payload is mounted as <code>/media</code> inside the container. Bind a host directory containing your media library (movies, TV shows, music, etc.).</p>
|
||||
<ul>
|
||||
<li><strong>UID/GID:</strong> Emby runs as a non-root user. If you see permission errors in the dashboard Logs tab, run <code>chown -R 1000:1000 /media</code> on the host.</li>
|
||||
<li><strong>Multi-library:</strong> bind the parent folder and let Emby discover subfolders.</li>
|
||||
</ul>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/emby/config:/config</code></li>
|
||||
<li><code>/opt/emby/cache:/cache</code></li>
|
||||
<li><code>MEDIA_PATH:/media</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>UID</code></li>
|
||||
<li><code>GID</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull emby/embyserver:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Emby:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>Library shows empty:</strong> confirm <code>mediaPath</code> is readable by the container UID and that the directory contains the file extensions Emby indexes.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/emby/web/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>emby</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Excalidraw — DashCaddy Docs',
|
||||
description: 'Install and configure Excalidraw via DashCaddy. Collaborative virtual whiteboard for sketching and diagrams',
|
||||
};
|
||||
|
||||
export default function excalidrawDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Excalidraw"
|
||||
intro="Collaborative virtual whiteboard for sketching and diagrams"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Productivity</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">excalidraw/excalidraw:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Excalidraw?</h2>
|
||||
<p>Collaborative virtual whiteboard for sketching and diagrams</p>
|
||||
<p>Excalidraw ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Excalidraw, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Excalidraw</strong> from the Productivity category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>draw</code>), host port (default: <code>8086</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "excalidraw",
|
||||
"config": {
|
||||
"subdomain": "draw",
|
||||
"port": 8086
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Excalidraw on my home host and expose it at draw.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull excalidraw/excalidraw:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Start drawing immediately - no account needed</li>
|
||||
<li>Share drawings via link for real-time collaboration</li>
|
||||
<li>Export as PNG, SVG, or Excalidraw file</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/excalidraw/data:/var/lib/excalidraw</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<p>None. The container runs with its upstream defaults.</p><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull excalidraw/excalidraw:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Excalidraw:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>excalidraw</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install FileBrowser — DashCaddy Docs',
|
||||
description: 'Install and configure FileBrowser via DashCaddy. Web-based file manager with sharing capabilities',
|
||||
};
|
||||
|
||||
export default function filebrowserDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install FileBrowser"
|
||||
intro="Web-based file manager with sharing capabilities"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Files</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">filebrowser/filebrowser:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is FileBrowser?</h2>
|
||||
<p>Web-based file manager with sharing capabilities</p>
|
||||
<p>FileBrowser ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using FileBrowser, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>FileBrowser</strong> from the Files category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>files</code>), host port (default: <code>8085</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "filebrowser",
|
||||
"config": {
|
||||
"subdomain": "files",
|
||||
"port": 8085
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy FileBrowser on my home host and expose it at files.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull filebrowser/filebrowser:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Default login: admin/admin</li>
|
||||
<li>Change default password immediately</li>
|
||||
<li>Configure user permissions and shares</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/filebrowser/data:/srv</code></li>
|
||||
<li><code>/opt/filebrowser/database:/database</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<p>None. The container runs with its upstream defaults.</p><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull filebrowser/filebrowser:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with FileBrowser:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>filebrowser</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Gitea — DashCaddy Docs',
|
||||
description: 'Install and configure Gitea via DashCaddy. Lightweight self-hosted Git service',
|
||||
};
|
||||
|
||||
export default function giteaDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Gitea"
|
||||
intro="Lightweight self-hosted Git service"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Development</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">gitea/gitea:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Gitea?</h2>
|
||||
<p>Lightweight self-hosted Git service</p>
|
||||
<p>Gitea ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Gitea, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Gitea</strong> from the Development category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>gitea</code>), host port (default: <code>3005</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "gitea",
|
||||
"config": {
|
||||
"subdomain": "gitea",
|
||||
"port": 3005
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Gitea on my home host and expose it at gitea.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull gitea/gitea:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Complete initial setup wizard</li>
|
||||
<li>Create admin account</li>
|
||||
<li>Configure SSH access</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/gitea/data:/data</code></li>
|
||||
<li><code>/etc/timezone:/etc/timezone:ro</code></li>
|
||||
<li><code>/etc/localtime:/etc/localtime:ro</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>USER_UID</code></li>
|
||||
<li><code>USER_GID</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull gitea/gitea:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Gitea:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>gitea</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Grafana — DashCaddy Docs',
|
||||
description: 'Install and configure Grafana via DashCaddy. Analytics and interactive visualization platform',
|
||||
};
|
||||
|
||||
export default function grafanaDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Grafana"
|
||||
intro="Analytics and interactive visualization platform"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Monitoring</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#f5a62322', color: '#f5a623' }}>Difficulty: Advanced</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">grafana/grafana:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Grafana?</h2>
|
||||
<p>Analytics and interactive visualization platform</p>
|
||||
<p>Grafana ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Grafana, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Grafana</strong> from the Monitoring category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>grafana</code>), host port (default: <code>3000</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/api/health</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "grafana",
|
||||
"config": {
|
||||
"subdomain": "grafana",
|
||||
"port": 3000
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Grafana on my home host and expose it at grafana.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull grafana/grafana:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Open the deployed URL (returned in the response as <code>url</code>, or visible in the dashboard).</li>
|
||||
<li>Complete the upstream Grafana setup wizard (admin account, library paths, EULA).</li>
|
||||
<li>Restore from a backup if one exists: <code>POST /api/v1/apps/{appId}/restore</code> with the backup ID from <code>GET /api/v1/backups/history</code>.</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/grafana/data:/var/lib/grafana</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>GF_SECURITY_ADMIN_PASSWORD</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull grafana/grafana:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Grafana:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/api/health</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>grafana</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Homarr — DashCaddy Docs',
|
||||
description: 'Install and configure Homarr via DashCaddy. Sleek dashboard for all your services',
|
||||
};
|
||||
|
||||
export default function homarrDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Homarr"
|
||||
intro="Sleek dashboard for all your services"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Utilities</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">ghcr.io/ajnart/homarr:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Homarr?</h2>
|
||||
<p>Sleek dashboard for all your services</p>
|
||||
<p>Homarr ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Homarr, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Homarr</strong> from the Utilities category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>homarr</code>), host port (default: <code>7575</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "homarr",
|
||||
"config": {
|
||||
"subdomain": "homarr",
|
||||
"port": 7575
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Homarr on my home host and expose it at homarr.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull ghcr.io/ajnart/homarr:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Add your services via UI</li>
|
||||
<li>Configure integrations</li>
|
||||
<li>Customize layout and appearance</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/homarr/configs:/app/data/configs</code></li>
|
||||
<li><code>/opt/homarr/icons:/app/public/icons</code></li>
|
||||
<li><code>/var/run/docker.sock:/var/run/docker.sock:ro</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<p>None. The container runs with its upstream defaults.</p><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull ghcr.io/ajnart/homarr:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Homarr:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>homarr</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Home Assistant — DashCaddy Docs',
|
||||
description: 'Install and configure Home Assistant via DashCaddy. Open source home automation platform',
|
||||
};
|
||||
|
||||
export default function homeassistantDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Home Assistant"
|
||||
intro="Open source home automation platform"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Home Automation</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Difficulty: Intermediate</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">homeassistant/home-assistant:stable</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Home Assistant?</h2>
|
||||
<p>Open source home automation platform</p>
|
||||
<p>Home Assistant ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Home Assistant, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Home Assistant</strong> from the Home Automation category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>home</code>), host port (default: <code>8123</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/api/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "homeassistant",
|
||||
"config": {
|
||||
"subdomain": "home",
|
||||
"port": 8123
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Home Assistant on my home host and expose it at home.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull homeassistant/home-assistant:stable</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Complete onboarding wizard</li>
|
||||
<li>Add integrations for your smart devices</li>
|
||||
<li>Create automations and dashboards</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/homeassistant/config:/config</code></li>
|
||||
<li><code>/etc/localtime:/etc/localtime:ro</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>TZ</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull homeassistant/home-assistant:stable</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Home Assistant:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/api/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>homeassistant</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Homepage — DashCaddy Docs',
|
||||
description: 'Install and configure Homepage via DashCaddy. Highly customizable application dashboard',
|
||||
};
|
||||
|
||||
export default function homepageDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Homepage"
|
||||
intro="Highly customizable application dashboard"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Utilities</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">ghcr.io/gethomepage/homepage:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Homepage?</h2>
|
||||
<p>Highly customizable application dashboard</p>
|
||||
<p>Homepage ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Homepage, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Homepage</strong> from the Utilities category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>dashboard</code>), host port (default: <code>3008</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "homepage",
|
||||
"config": {
|
||||
"subdomain": "dashboard",
|
||||
"port": 3008
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Homepage on my home host and expose it at dashboard.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull ghcr.io/gethomepage/homepage:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Edit config files to add services</li>
|
||||
<li>Configure widgets</li>
|
||||
<li>Customize appearance</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/homepage/config:/app/config</code></li>
|
||||
<li><code>/var/run/docker.sock:/var/run/docker.sock:ro</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<p>None. The container runs with its upstream defaults.</p><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull ghcr.io/gethomepage/homepage:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Homepage:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>homepage</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Immich — DashCaddy Docs',
|
||||
description: 'Install and configure Immich via DashCaddy. Self-hosted Google Photos alternative',
|
||||
};
|
||||
|
||||
export default function immichDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Immich"
|
||||
intro="Self-hosted Google Photos alternative"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Photos</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Difficulty: Intermediate</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">ghcr.io/immich-app/immich-server:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Immich?</h2>
|
||||
<p>Self-hosted Google Photos alternative</p>
|
||||
<p>Immich ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Immich, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Immich</strong> from the Photos category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>photos</code>), host port (default: <code>2283</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/api/server-info/ping</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "immich",
|
||||
"config": {
|
||||
"subdomain": "photos",
|
||||
"port": 2283
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Immich on my home host and expose it at photos.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull ghcr.io/immich-app/immich-server:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Requires PostgreSQL and Redis</li>
|
||||
<li>Install mobile apps for backup</li>
|
||||
<li>Configure machine learning for face detection</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/immich/upload:/usr/src/app/upload</code></li>
|
||||
<li><code>/opt/immich/library:/usr/src/app/library</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>DB_HOSTNAME</code></li>
|
||||
<li><code>DB_USERNAME</code></li>
|
||||
<li><code>DB_PASSWORD</code></li>
|
||||
<li><code>DB_DATABASE_NAME</code></li>
|
||||
<li><code>REDIS_HOSTNAME</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull ghcr.io/immich-app/immich-server:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Immich:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/api/server-info/ping</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>immich</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install IT Tools — DashCaddy Docs',
|
||||
description: 'Install and configure IT Tools via DashCaddy. Collection of handy developer and IT tools in one place',
|
||||
};
|
||||
|
||||
export default function itToolsDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install IT Tools"
|
||||
intro="Collection of handy developer and IT tools in one place"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Utilities</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">corentinth/it-tools:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is IT Tools?</h2>
|
||||
<p>Collection of handy developer and IT tools in one place</p>
|
||||
<p>IT Tools ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using IT Tools, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>IT Tools</strong> from the Utilities category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>tools</code>), host port (default: <code>8087</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "it-tools",
|
||||
"config": {
|
||||
"subdomain": "tools",
|
||||
"port": 8087
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy IT Tools on my home host and expose it at tools.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull corentinth/it-tools:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Access the web interface for instant tools access</li>
|
||||
<li>Includes: hash generators, UUID, JWT decoder, base64, regex tester, and 70+ more</li>
|
||||
<li>No configuration needed</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/it-tools/config:/config</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<p>None. The container runs with its upstream defaults.</p><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull corentinth/it-tools:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with IT Tools:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>it-tools</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install JDownloader 2 — DashCaddy Docs',
|
||||
description: 'Install and configure JDownloader 2 via DashCaddy. Download manager for file hosting sites',
|
||||
};
|
||||
|
||||
export default function jdownloaderDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install JDownloader 2"
|
||||
intro="Download manager for file hosting sites"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Downloads</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">jlesage/jdownloader-2:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is JDownloader 2?</h2>
|
||||
<p>Download manager for file hosting sites</p>
|
||||
<p>JDownloader 2 ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using JDownloader 2, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>JDownloader 2</strong> from the Downloads category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>jdownloader</code>), host port (default: <code>5800</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "jdownloader",
|
||||
"config": {
|
||||
"subdomain": "jdownloader",
|
||||
"port": 5800
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy JDownloader 2 on my home host and expose it at jdownloader.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull jlesage/jdownloader-2:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Access web interface to configure</li>
|
||||
<li>Link to MyJDownloader account</li>
|
||||
<li>Configure download paths</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/jdownloader/config:/config</code></li>
|
||||
<li><code>/downloads:/output</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<p>None. The container runs with its upstream defaults.</p><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull jlesage/jdownloader-2:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with JDownloader 2:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>jdownloader</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Jellyfin — DashCaddy Docs',
|
||||
description: 'Install and configure Jellyfin via DashCaddy. Free software media system - alternative to Plex',
|
||||
};
|
||||
|
||||
export default function jellyfinDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Jellyfin"
|
||||
intro="Free software media system - alternative to Plex"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Media</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">jellyfin/jellyfin:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Jellyfin?</h2>
|
||||
<p>Free software media system - alternative to Plex</p>
|
||||
<p>Jellyfin ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Jellyfin, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>A host path containing your media. Default suggestion: <code>/media</code>. The deploy form / API payload <code>config.mediaPath</code> must be readable by the container UID (usually <code>1000</code>).</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Jellyfin</strong> from the Media category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>jellyfin</code>), host port (default: <code>8096</code>), and the media library path.</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/health</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "jellyfin",
|
||||
"config": {
|
||||
"subdomain": "jellyfin",
|
||||
"port": 8096,
|
||||
"mediaPath": "/media"
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Jellyfin on my home host and expose it at jellyfin.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull jellyfin/jellyfin:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Complete the initial setup wizard</li>
|
||||
<li>Add your media libraries</li>
|
||||
<li>Configure user accounts and permissions</li>
|
||||
</ol>
|
||||
<h2>Media library path notes</h2>
|
||||
<p>The media mount path you pass as <code>mediaPath</code> in the deploy payload is mounted as <code>/media</code> inside the container. Bind a host directory containing your media library (movies, TV shows, music, etc.).</p>
|
||||
<ul>
|
||||
<li><strong>UID/GID:</strong> Jellyfin runs as a non-root user. If you see permission errors in the dashboard Logs tab, run <code>chown -R 1000:1000 /media</code> on the host.</li>
|
||||
<li><strong>Multi-library:</strong> bind the parent folder and let Jellyfin discover subfolders.</li>
|
||||
</ul>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/jellyfin/config:/config</code></li>
|
||||
<li><code>/opt/jellyfin/cache:/cache</code></li>
|
||||
<li><code>MEDIA_PATH:/media</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>JELLYFIN_PublishedServerUrl</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull jellyfin/jellyfin:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Jellyfin:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>Library shows empty:</strong> confirm <code>mediaPath</code> is readable by the container UID and that the directory contains the file extensions Jellyfin indexes.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/health</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>jellyfin</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Jenkins — DashCaddy Docs',
|
||||
description: 'Install and configure Jenkins via DashCaddy. Automation server for CI/CD pipelines',
|
||||
};
|
||||
|
||||
export default function jenkinsDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Jenkins"
|
||||
intro="Automation server for CI/CD pipelines"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Development</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#f5a62322', color: '#f5a623' }}>Difficulty: Advanced</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">jenkins/jenkins:lts</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Jenkins?</h2>
|
||||
<p>Automation server for CI/CD pipelines</p>
|
||||
<p>Jenkins ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Jenkins, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Jenkins</strong> from the Development category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>jenkins</code>), host port (default: <code>8089</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/login</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "jenkins",
|
||||
"config": {
|
||||
"subdomain": "jenkins",
|
||||
"port": 8089
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Jenkins on my home host and expose it at jenkins.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull jenkins/jenkins:lts</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Get initial admin password from logs</li>
|
||||
<li>Install suggested plugins</li>
|
||||
<li>Create admin user</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/jenkins/data:/var/jenkins_home</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<p>None. The container runs with its upstream defaults.</p><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull jenkins/jenkins:lts</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Jenkins:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/login</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>jenkins</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Kavita — DashCaddy Docs',
|
||||
description: 'Install and configure Kavita via DashCaddy. Digital reading platform for manga, comics, and books',
|
||||
};
|
||||
|
||||
export default function kavitaDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Kavita"
|
||||
intro="Digital reading platform for manga, comics, and books"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Media</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">jvmilazz0/kavita:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Kavita?</h2>
|
||||
<p>Digital reading platform for manga, comics, and books</p>
|
||||
<p>Kavita ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Kavita, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>A host path containing your media. Default suggestion: <code>/media/reading</code>. The deploy form / API payload <code>config.mediaPath</code> must be readable by the container UID (usually <code>1000</code>).</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Kavita</strong> from the Media category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>kavita</code>), host port (default: <code>5004</code>), and the media library path.</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "kavita",
|
||||
"config": {
|
||||
"subdomain": "kavita",
|
||||
"port": 5004,
|
||||
"mediaPath": "/media/reading"
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Kavita on my home host and expose it at kavita.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull jvmilazz0/kavita:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Create admin account on first access</li>
|
||||
<li>Add library folders for manga, comics, or books</li>
|
||||
<li>Supports EPUB, PDF, CBZ, CBR formats</li>
|
||||
</ol>
|
||||
<h2>Media library path notes</h2>
|
||||
<p>The media mount path you pass as <code>mediaPath</code> in the deploy payload is mounted as <code>/data</code> inside the container. Bind a host directory containing your media library (movies, TV shows, music, etc.).</p>
|
||||
<ul>
|
||||
<li><strong>UID/GID:</strong> Kavita runs as a non-root user. If you see permission errors in the dashboard Logs tab, run <code>chown -R 1000:1000 /media/reading</code> on the host.</li>
|
||||
<li><strong>Multi-library:</strong> bind the parent folder and let Kavita discover subfolders.</li>
|
||||
</ul>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/kavita/config:/kavita/config</code></li>
|
||||
<li><code>MEDIA_PATH:/data</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<p>None. The container runs with its upstream defaults.</p><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull jvmilazz0/kavita:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Kavita:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>Library shows empty:</strong> confirm <code>mediaPath</code> is readable by the container UID and that the directory contains the file extensions Kavita indexes.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>kavita</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Komga — DashCaddy Docs',
|
||||
description: 'Install and configure Komga via DashCaddy. Comic and manga media server with web reader',
|
||||
};
|
||||
|
||||
export default function komgaDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Komga"
|
||||
intro="Comic and manga media server with web reader"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Media</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">gotson/komga:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Komga?</h2>
|
||||
<p>Comic and manga media server with web reader</p>
|
||||
<p>Komga ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Komga, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>A host path containing your media. Default suggestion: <code>/media/comics</code>. The deploy form / API payload <code>config.mediaPath</code> must be readable by the container UID (usually <code>1000</code>).</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Komga</strong> from the Media category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>komga</code>), host port (default: <code>25600</code>), and the media library path.</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "komga",
|
||||
"config": {
|
||||
"subdomain": "komga",
|
||||
"port": 25600,
|
||||
"mediaPath": "/media/comics"
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Komga on my home host and expose it at komga.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull gotson/komga:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Create admin account on first access</li>
|
||||
<li>Add your comic libraries (CBZ, CBR, PDF supported)</li>
|
||||
<li>Use OPDS for third-party reader apps</li>
|
||||
</ol>
|
||||
<h2>Media library path notes</h2>
|
||||
<p>The media mount path you pass as <code>mediaPath</code> in the deploy payload is mounted as <code>/data</code> inside the container. Bind a host directory containing your media library (movies, TV shows, music, etc.).</p>
|
||||
<ul>
|
||||
<li><strong>UID/GID:</strong> Komga runs as a non-root user. If you see permission errors in the dashboard Logs tab, run <code>chown -R 1000:1000 /media/comics</code> on the host.</li>
|
||||
<li><strong>Multi-library:</strong> bind the parent folder and let Komga discover subfolders.</li>
|
||||
</ul>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/komga/config:/config</code></li>
|
||||
<li><code>MEDIA_PATH:/data</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>TZ</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull gotson/komga:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Komga:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>Library shows empty:</strong> confirm <code>mediaPath</code> is readable by the container UID and that the directory contains the file extensions Komga indexes.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>komga</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Lidarr — DashCaddy Docs',
|
||||
description: 'Install and configure Lidarr via DashCaddy. Music collection manager for Usenet and BitTorrent',
|
||||
};
|
||||
|
||||
export default function lidarrDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Lidarr"
|
||||
intro="Music collection manager for Usenet and BitTorrent"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Media Management</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Difficulty: Intermediate</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">linuxserver/lidarr:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Lidarr?</h2>
|
||||
<p>Music collection manager for Usenet and BitTorrent</p>
|
||||
<p>Lidarr ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Lidarr, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Lidarr</strong> from the Media Management category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>lidarr</code>), host port (default: <code>8686</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/api/v1/system/status</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "lidarr",
|
||||
"config": {
|
||||
"subdomain": "lidarr",
|
||||
"port": 8686
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Lidarr on my home host and expose it at lidarr.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull linuxserver/lidarr:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Configure download clients</li>
|
||||
<li>Add indexers</li>
|
||||
<li>Set up root folders for music</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/lidarr/config:/config</code></li>
|
||||
<li><code>/downloads:/downloads</code></li>
|
||||
<li><code>/music:/music</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>PUID</code></li>
|
||||
<li><code>PGID</code></li>
|
||||
<li><code>TZ</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull linuxserver/lidarr:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Lidarr:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/api/v1/system/status</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>lidarr</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Docker Mailserver — DashCaddy Docs',
|
||||
description: 'Install and configure Docker Mailserver via DashCaddy. Full-featured email server with SMTP, IMAP, spam filtering',
|
||||
};
|
||||
|
||||
export default function mailserverDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Docker Mailserver"
|
||||
intro="Full-featured email server with SMTP, IMAP, spam filtering"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Communication</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#f5a62322', color: '#f5a623' }}>Difficulty: Advanced</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">mailserver/docker-mailserver:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Docker Mailserver?</h2>
|
||||
<p>Full-featured email server with SMTP, IMAP, spam filtering</p>
|
||||
<p>Docker Mailserver ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Docker Mailserver, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Docker Mailserver</strong> from the Communication category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>mail</code>), host port (default: <code>25</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "mailserver",
|
||||
"config": {
|
||||
"subdomain": "mail",
|
||||
"port": 25
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Docker Mailserver on my home host and expose it at mail.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull mailserver/docker-mailserver:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Configure DNS records (MX, SPF, DKIM, DMARC)</li>
|
||||
<li>Create email accounts using setup.sh</li>
|
||||
<li>Set up SSL certificates for secure connections</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/mailserver/data:/var/mail</code></li>
|
||||
<li><code>/opt/mailserver/state:/var/mail-state</code></li>
|
||||
<li><code>/opt/mailserver/logs:/var/log/mail</code></li>
|
||||
<li><code>/opt/mailserver/config:/tmp/docker-mailserver</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>ENABLE_SPAMASSASSIN</code></li>
|
||||
<li><code>ENABLE_CLAMAV</code></li>
|
||||
<li><code>ENABLE_FAIL2BAN</code></li>
|
||||
<li><code>ONE_DIR</code></li>
|
||||
<li><code>TZ</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull mailserver/docker-mailserver:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Docker Mailserver:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>mailserver</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Matrix Synapse — DashCaddy Docs',
|
||||
description: 'Install and configure Matrix Synapse via DashCaddy. Decentralized, secure messaging and collaboration',
|
||||
};
|
||||
|
||||
export default function matrixDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Matrix Synapse"
|
||||
intro="Decentralized, secure messaging and collaboration"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Communication</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#f5a62322', color: '#f5a623' }}>Difficulty: Advanced</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">matrixdotorg/synapse:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Matrix Synapse?</h2>
|
||||
<p>Decentralized, secure messaging and collaboration</p>
|
||||
<p>Matrix Synapse ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Matrix Synapse, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Matrix Synapse</strong> from the Communication category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>matrix</code>), host port (default: <code>8008</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/_matrix/client/versions</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "matrix",
|
||||
"config": {
|
||||
"subdomain": "matrix",
|
||||
"port": 8008
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Matrix Synapse on my home host and expose it at matrix.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull matrixdotorg/synapse:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Generate initial config with --generate</li>
|
||||
<li>Configure homeserver.yaml</li>
|
||||
<li>Set up federation if needed</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/matrix/data:/data</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>SYNAPSE_SERVER_NAME</code></li>
|
||||
<li><code>SYNAPSE_REPORT_STATS</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull matrixdotorg/synapse:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Matrix Synapse:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/_matrix/client/versions</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>matrix</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Mealie — DashCaddy Docs',
|
||||
description: 'Install and configure Mealie via DashCaddy. Recipe manager and meal planner with grocery lists',
|
||||
};
|
||||
|
||||
export default function mealieDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Mealie"
|
||||
intro="Recipe manager and meal planner with grocery lists"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Productivity</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">ghcr.io/mealie-recipes/mealie:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Mealie?</h2>
|
||||
<p>Recipe manager and meal planner with grocery lists</p>
|
||||
<p>Mealie ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Mealie, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Mealie</strong> from the Productivity category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>mealie</code>), host port (default: <code>9925</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "mealie",
|
||||
"config": {
|
||||
"subdomain": "mealie",
|
||||
"port": 9925
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Mealie on my home host and expose it at mealie.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull ghcr.io/mealie-recipes/mealie:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Default login: changeme@example.com / MyPassword</li>
|
||||
<li>Import recipes from URLs or add them manually</li>
|
||||
<li>Create meal plans and generate shopping lists</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/mealie/data:/app/data</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>ALLOW_SIGNUP</code></li>
|
||||
<li><code>MAX_WORKERS</code></li>
|
||||
<li><code>WEB_CONCURRENCY</code></li>
|
||||
<li><code>BASE_URL</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull ghcr.io/mealie-recipes/mealie:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Mealie:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>mealie</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Minecraft Server — DashCaddy Docs',
|
||||
description: 'Install and configure Minecraft Server via DashCaddy. Minecraft Java Edition dedicated server',
|
||||
};
|
||||
|
||||
export default function minecraftDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Minecraft Server"
|
||||
intro="Minecraft Java Edition dedicated server"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Gaming</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">itzg/minecraft-server:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Minecraft Server?</h2>
|
||||
<p>Minecraft Java Edition dedicated server</p>
|
||||
<p>Minecraft Server ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Minecraft Server, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Minecraft Server</strong> from the Gaming category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>mc</code>), host port (default: <code>25565</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>tcp://localhost:25565</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "minecraft",
|
||||
"config": {
|
||||
"subdomain": "mc",
|
||||
"port": 25565
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Minecraft Server on my home host and expose it at mc.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull itzg/minecraft-server:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Server accepts the Minecraft EULA automatically</li>
|
||||
<li>Connect with your Minecraft client to the server IP:port</li>
|
||||
<li>Configure server.properties in the data volume for customization</li>
|
||||
<li>Supports Vanilla, Paper, Forge, Fabric via TYPE environment variable</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/minecraft/data:/data</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>EULA</code></li>
|
||||
<li><code>TYPE</code></li>
|
||||
<li><code>VERSION</code></li>
|
||||
<li><code>MEMORY</code></li>
|
||||
<li><code>MAX_PLAYERS</code></li>
|
||||
<li><code>MOTD</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull itzg/minecraft-server:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Minecraft Server:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>tcp://localhost:25565</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>minecraft</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install MongoDB — DashCaddy Docs',
|
||||
description: 'Install and configure MongoDB via DashCaddy. Document-oriented NoSQL database',
|
||||
};
|
||||
|
||||
export default function mongodbDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install MongoDB"
|
||||
intro="Document-oriented NoSQL database"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Database</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Difficulty: Intermediate</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">mongo:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is MongoDB?</h2>
|
||||
<p>Document-oriented NoSQL database</p>
|
||||
<p>MongoDB ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using MongoDB, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>MongoDB</strong> from the Database category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>mongo</code>), host port (default: <code>27017</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "mongodb",
|
||||
"config": {
|
||||
"subdomain": "mongo",
|
||||
"port": 27017
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy MongoDB on my home host and expose it at mongo.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull mongo:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Change default admin password</li>
|
||||
<li>Create application databases and users</li>
|
||||
<li>Configure replica set if needed</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/mongodb/data:/data/db</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>MONGO_INITDB_ROOT_USERNAME</code></li>
|
||||
<li><code>MONGO_INITDB_ROOT_PASSWORD</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull mongo:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with MongoDB:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>mongodb</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Navidrome — DashCaddy Docs',
|
||||
description: 'Install and configure Navidrome via DashCaddy. Modern music server and streamer',
|
||||
};
|
||||
|
||||
export default function navidromeDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Navidrome"
|
||||
intro="Modern music server and streamer"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Media</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">deluan/navidrome:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Navidrome?</h2>
|
||||
<p>Modern music server and streamer</p>
|
||||
<p>Navidrome ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Navidrome, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Navidrome</strong> from the Media category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>music</code>), host port (default: <code>4533</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "navidrome",
|
||||
"config": {
|
||||
"subdomain": "music",
|
||||
"port": 4533
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Navidrome on my home host and expose it at music.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull deluan/navidrome:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Point to your music library</li>
|
||||
<li>Create user accounts</li>
|
||||
<li>Install Subsonic-compatible apps</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/navidrome/data:/data</code></li>
|
||||
<li><code>/music:/music:ro</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>ND_SCANSCHEDULE</code></li>
|
||||
<li><code>ND_LOGLEVEL</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull deluan/navidrome:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Navidrome:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>navidrome</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Nextcloud — DashCaddy Docs',
|
||||
description: 'Install and configure Nextcloud via DashCaddy. Self-hosted productivity platform and file sync',
|
||||
};
|
||||
|
||||
export default function nextcloudDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Nextcloud"
|
||||
intro="Self-hosted productivity platform and file sync"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Productivity</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Difficulty: Intermediate</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">nextcloud:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Nextcloud?</h2>
|
||||
<p>Self-hosted productivity platform and file sync</p>
|
||||
<p>Nextcloud ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Nextcloud, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Nextcloud</strong> from the Productivity category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>cloud</code>), host port (default: <code>8080</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/status.php</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "nextcloud",
|
||||
"config": {
|
||||
"subdomain": "cloud",
|
||||
"port": 8080
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Nextcloud on my home host and expose it at cloud.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull nextcloud:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Change the default admin password</li>
|
||||
<li>Configure trusted domains</li>
|
||||
<li>Install recommended apps</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/nextcloud/html:/var/www/html</code></li>
|
||||
<li><code>/opt/nextcloud/data:/var/www/html/data</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>NEXTCLOUD_ADMIN_USER</code></li>
|
||||
<li><code>NEXTCLOUD_ADMIN_PASSWORD</code></li>
|
||||
<li><code>NEXTCLOUD_TRUSTED_DOMAINS</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull nextcloud:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Nextcloud:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/status.php</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>nextcloud</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Node-RED — DashCaddy Docs',
|
||||
description: 'Install and configure Node-RED via DashCaddy. Flow-based programming for IoT and automation',
|
||||
};
|
||||
|
||||
export default function noderedDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Node-RED"
|
||||
intro="Flow-based programming for IoT and automation"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Home Automation</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Difficulty: Intermediate</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">nodered/node-red:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Node-RED?</h2>
|
||||
<p>Flow-based programming for IoT and automation</p>
|
||||
<p>Node-RED ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Node-RED, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Node-RED</strong> from the Home Automation category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>nodered</code>), host port (default: <code>1880</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "nodered",
|
||||
"config": {
|
||||
"subdomain": "nodered",
|
||||
"port": 1880
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Node-RED on my home host and expose it at nodered.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull nodered/node-red:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Install additional nodes from palette</li>
|
||||
<li>Create flows for automation</li>
|
||||
<li>Connect to Home Assistant or MQTT</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/nodered/data:/data</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>TZ</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull nodered/node-red:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Node-RED:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>nodered</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install NZBGet — DashCaddy Docs',
|
||||
description: 'Install and configure NZBGet via DashCaddy. Efficient Usenet downloader',
|
||||
};
|
||||
|
||||
export default function nzbgetDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install NZBGet"
|
||||
intro="Efficient Usenet downloader"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Downloads</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Difficulty: Intermediate</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">linuxserver/nzbget:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is NZBGet?</h2>
|
||||
<p>Efficient Usenet downloader</p>
|
||||
<p>NZBGet ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using NZBGet, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>NZBGet</strong> from the Downloads category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>nzbget</code>), host port (default: <code>6789</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "nzbget",
|
||||
"config": {
|
||||
"subdomain": "nzbget",
|
||||
"port": 6789
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy NZBGet on my home host and expose it at nzbget.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull linuxserver/nzbget:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Default login: nzbget/tegbzn6789</li>
|
||||
<li>Configure news servers</li>
|
||||
<li>Set up categories and paths</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/nzbget/config:/config</code></li>
|
||||
<li><code>/downloads:/downloads</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>PUID</code></li>
|
||||
<li><code>PGID</code></li>
|
||||
<li><code>TZ</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull linuxserver/nzbget:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with NZBGet:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>nzbget</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Outline — DashCaddy Docs',
|
||||
description: 'Install and configure Outline via DashCaddy. Modern team knowledge base and wiki',
|
||||
};
|
||||
|
||||
export default function outlineDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Outline"
|
||||
intro="Modern team knowledge base and wiki"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Productivity</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#f5a62322', color: '#f5a623' }}>Difficulty: Advanced</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">outlinewiki/outline:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Outline?</h2>
|
||||
<p>Modern team knowledge base and wiki</p>
|
||||
<p>Outline ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Outline, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Outline</strong> from the Productivity category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>outline</code>), host port (default: <code>3006</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "outline",
|
||||
"config": {
|
||||
"subdomain": "outline",
|
||||
"port": 3006
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Outline on my home host and expose it at outline.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull outlinewiki/outline:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Requires PostgreSQL and Redis</li>
|
||||
<li>Configure OAuth provider</li>
|
||||
<li>Set up S3-compatible storage</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/outline/data:/var/lib/outline/data</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>URL</code></li>
|
||||
<li><code>SECRET_KEY</code></li>
|
||||
<li><code>DATABASE_URL</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull outlinewiki/outline:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Outline:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>outline</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,638 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'App Catalog — DashCaddy Docs',
|
||||
description: 'Browse all 77 one-click installable apps supported by DashCaddy, organized by category with install instructions for each.',
|
||||
};
|
||||
|
||||
export default function DocsCatalogPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="App Catalog"
|
||||
intro="DashCaddy ships with 77 pre-configured application templates. Every template can be deployed from the dashboard, called via the REST API, or invoked through the MCP server. This page is the index — click any app for its dedicated install guide with prerequisites, the exact API payload shape, and post-install verification steps."
|
||||
>
|
||||
<div className="mb-8 rounded-xl border border-brand-500/30 bg-brand-500/5 p-5">
|
||||
<p className="text-sm text-surface-200">
|
||||
<strong className="text-brand-400">77 apps</strong> across <strong>17 categories</strong>. The canonical source is <code>dashcaddy-api/src/docker/app-templates.js</code>; regenerate these pages with <code>scripts/regenerate-catalog-docs.sh</code> after editing.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<h2>Media</h2>
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<a href="/docs/catalog/plex" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Plex</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Stream your personal media collection anywhere</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/jellyfin" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Jellyfin</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Free software media system - alternative to Plex</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/emby" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Emby</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Personal media server with apps for all devices</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/audiobookshelf" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Audiobookshelf</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Self-hosted audiobook and podcast server</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/navidrome" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Navidrome</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Modern music server and streamer</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/calibre-web" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Calibre-Web</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Intermediate</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Web-based ebook manager and reader</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/kavita" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Kavita</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Digital reading platform for manga, comics, and books</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/vintage-radio" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Vintage Stereo</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Glass-front console stereo that tunes curated real internet stations (SomaFM, KEXP, Radio Paradise, and more) through a beautiful analog UI</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/komga" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Komga</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Comic and manga media server with web reader</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/airsonic" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Airsonic Advanced</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Free web-based media streamer</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h2>Media Management</h2>
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<a href="/docs/catalog/seerr" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Seerr</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Media request and discovery manager for Plex, Jellyfin, and Emby</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/sonarr" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Sonarr</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Intermediate</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Smart PVR for newsgroup and bittorrent users</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/radarr" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Radarr</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Intermediate</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Movie collection manager for Usenet and BitTorrent</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/tautulli" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Tautulli</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Plex media server monitoring and statistics</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/prowlarr" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Prowlarr</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#f5a62322', color: '#f5a623' }}>Advanced</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Indexer manager/proxy for *arr applications</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/bazarr" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Bazarr</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Automatic subtitle downloader for Sonarr and Radarr</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/lidarr" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Lidarr</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Intermediate</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Music collection manager for Usenet and BitTorrent</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/readarr" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Readarr</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Intermediate</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Book and audiobook collection manager</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h2>Downloads</h2>
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<a href="/docs/catalog/qbittorrent" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">qBittorrent</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Lightweight BitTorrent client with web UI</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/transmission" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Transmission</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Lightweight BitTorrent client</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/sabnzbd" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">SABnzbd</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Intermediate</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Binary newsreader for Usenet downloads</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/jdownloader" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">JDownloader 2</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Download manager for file hosting sites</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/nzbget" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">NZBGet</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Intermediate</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Efficient Usenet downloader</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h2>Productivity</h2>
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<a href="/docs/catalog/nextcloud" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Nextcloud</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Intermediate</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Self-hosted productivity platform and file sync</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/paperless-ngx" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Paperless-ngx</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Intermediate</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Document management system - scan, organize, and search documents</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/bookstack" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">BookStack</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Intermediate</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Simple wiki and documentation platform</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/actual-budget" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Actual Budget</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Privacy-focused budgeting app with envelope budgeting</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/mealie" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Mealie</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Recipe manager and meal planner with grocery lists</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/outline" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Outline</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#f5a62322', color: '#f5a623' }}>Advanced</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Modern team knowledge base and wiki</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/trilium" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Trilium Notes</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Hierarchical knowledge base and note-taking app</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/excalidraw" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Excalidraw</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Collaborative virtual whiteboard for sketching and diagrams</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/standardnotes" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Standard Notes</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Intermediate</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">End-to-end encrypted notes app</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h2>Development</h2>
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<a href="/docs/catalog/gitea" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Gitea</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Lightweight self-hosted Git service</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/vscode-server" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">VS Code Server</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Visual Studio Code in your browser</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/jenkins" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Jenkins</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#f5a62322', color: '#f5a623' }}>Advanced</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Automation server for CI/CD pipelines</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/drone" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Drone CI</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Intermediate</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Container-native continuous delivery platform</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h2>Management</h2>
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<a href="/docs/catalog/portainer" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Portainer</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Docker container management UI</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/watchtower" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Watchtower</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Automatic Docker container image updates</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h2>Monitoring</h2>
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<a href="/docs/catalog/uptime-kuma" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Uptime Kuma</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Self-hosted monitoring tool like Uptime Robot</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/grafana" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Grafana</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#f5a62322', color: '#f5a623' }}>Advanced</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Analytics and interactive visualization platform</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/dozzle" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Dozzle</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Real-time Docker container log viewer</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/speedtest" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Speedtest Tracker</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Internet speed monitoring over time</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h2>Networking</h2>
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<a href="/docs/catalog/pihole" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Pi-hole</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Intermediate</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Network-wide ad blocker and DNS sinkhole</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/wireguard" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">WireGuard VPN</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#f5a62322', color: '#f5a623' }}>Advanced</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Fast, modern, secure VPN tunnel</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h2>DNS</h2>
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<a href="/docs/catalog/technitium" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Technitium DNS Server</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Modern DNS server with web UI for managing private zones</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/bind9" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">BIND9 DNS Server</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#f5a62322', color: '#f5a623' }}>Advanced</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Industry-standard DNS server - powerful and flexible</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/powerdns" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">PowerDNS</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Intermediate</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">High-performance DNS server with SQL backend</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/coredns" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">CoreDNS</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Intermediate</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Cloud-native DNS server - lightweight and flexible</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h2>Files</h2>
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<a href="/docs/catalog/filebrowser" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">FileBrowser</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Web-based file manager with sharing capabilities</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/syncthing" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Syncthing</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Continuous file synchronization between devices</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/sami-files" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Sami Files</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Intermediate</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Multi-server SSH file manager — browse, edit, upload, and exec across all your machines from one browser tab</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h2>Communication</h2>
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<a href="/docs/catalog/rocketchat" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Rocket.Chat</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Intermediate</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Team collaboration platform like Slack</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/matrix" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Matrix Synapse</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#f5a62322', color: '#f5a623' }}>Advanced</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Decentralized, secure messaging and collaboration</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/roundcube" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Roundcube</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Intermediate</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Modern webmail client with rich features</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/mailserver" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Docker Mailserver</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#f5a62322', color: '#f5a623' }}>Advanced</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Full-featured email server with SMTP, IMAP, spam filtering</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h2>Home Automation</h2>
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<a href="/docs/catalog/homeassistant" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Home Assistant</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Intermediate</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Open source home automation platform</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/nodered" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Node-RED</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Intermediate</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Flow-based programming for IoT and automation</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h2>Database</h2>
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<a href="/docs/catalog/postgres" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">PostgreSQL</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Intermediate</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Advanced open-source relational database</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/redis" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Redis</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">In-memory data structure store and cache</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/mongodb" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">MongoDB</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Intermediate</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Document-oriented NoSQL database</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/adminer" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Adminer</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Lightweight database management in single PHP file</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h2>Security</h2>
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<a href="/docs/catalog/dashca" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">DashCA</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">One-click root CA certificate installer for your network</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/vaultwarden" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Vaultwarden</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Lightweight Bitwarden-compatible password manager</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/authentik" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Authentik</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#f5a62322', color: '#f5a623' }}>Advanced</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Identity provider and single sign-on platform</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/crowdsec" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">CrowdSec</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Intermediate</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Collaborative intrusion prevention system</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h2>Photos</h2>
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<a href="/docs/catalog/immich" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Immich</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Intermediate</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Self-hosted Google Photos alternative</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/photoprism" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">PhotoPrism</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Intermediate</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">AI-powered photo management</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h2>Utilities</h2>
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<a href="/docs/catalog/homepage" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Homepage</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Highly customizable application dashboard</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/stirling-pdf" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Stirling PDF</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Self-hosted PDF manipulation tool - merge, split, convert, and more</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/weather" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Weather</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Live weather widget with temperature, conditions, and wind</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/homarr" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Homarr</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Sleek dashboard for all your services</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/digital-clock" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Digital Clock</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Live digital clock with time, date, and day of week</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/it-tools" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">IT Tools</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Collection of handy developer and IT tools in one place</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/changedetection" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Change Detection</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Monitor websites for changes</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/whoami" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Whoami</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Simple HTTP request debugging service</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h2>Gaming</h2>
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<a href="/docs/catalog/minecraft" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Minecraft Server</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Minecraft Java Edition dedicated server</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/valheim" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Valheim Server</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Valheim dedicated server for multiplayer Viking adventures</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<hr className="my-10 border-surface-700" />
|
||||
<h2>Adding your own template</h2>
|
||||
<p>Add an entry to <code>APP_TEMPLATES</code> in <code>dashcaddy-api/src/docker/app-templates.js</code> with the required fields (name, description, category, docker.image, ports, volumes), then re-run <code>scripts/regenerate-catalog-docs.sh</code>. The template will appear in the dashboard App Selector automatically.</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Paperless-ngx — DashCaddy Docs',
|
||||
description: 'Install and configure Paperless-ngx via DashCaddy. Document management system - scan, organize, and search documents',
|
||||
};
|
||||
|
||||
export default function paperlessNgxDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Paperless-ngx"
|
||||
intro="Document management system - scan, organize, and search documents"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Productivity</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Difficulty: Intermediate</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">ghcr.io/paperless-ngx/paperless-ngx:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Paperless-ngx?</h2>
|
||||
<p>Document management system - scan, organize, and search documents</p>
|
||||
<p>Paperless-ngx ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Paperless-ngx, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Paperless-ngx</strong> from the Productivity category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>paperless</code>), host port (default: <code>8095</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "paperless-ngx",
|
||||
"config": {
|
||||
"subdomain": "paperless",
|
||||
"port": 8095
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Paperless-ngx on my home host and expose it at paperless.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull ghcr.io/paperless-ngx/paperless-ngx:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Create admin account via: docker exec -it <container> python3 manage.py createsuperuser</li>
|
||||
<li>Drop documents into the consume folder for automatic import</li>
|
||||
<li>Configure tags and correspondents for organization</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/paperless/data:/usr/src/paperless/data</code></li>
|
||||
<li><code>/opt/paperless/media:/usr/src/paperless/media</code></li>
|
||||
<li><code>/opt/paperless/consume:/usr/src/paperless/consume</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>PAPERLESS_URL</code></li>
|
||||
<li><code>USERMAP_UID</code></li>
|
||||
<li><code>USERMAP_GID</code></li>
|
||||
<li><code>PAPERLESS_TIME_ZONE</code></li>
|
||||
<li><code>PAPERLESS_OCR_LANGUAGE</code></li>
|
||||
<li><code>PAPERLESS_SECRET_KEY</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull ghcr.io/paperless-ngx/paperless-ngx:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Paperless-ngx:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>paperless-ngx</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install PhotoPrism — DashCaddy Docs',
|
||||
description: 'Install and configure PhotoPrism via DashCaddy. AI-powered photo management',
|
||||
};
|
||||
|
||||
export default function photoprismDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install PhotoPrism"
|
||||
intro="AI-powered photo management"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Photos</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Difficulty: Intermediate</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">photoprism/photoprism:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is PhotoPrism?</h2>
|
||||
<p>AI-powered photo management</p>
|
||||
<p>PhotoPrism ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using PhotoPrism, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>PhotoPrism</strong> from the Photos category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>gallery</code>), host port (default: <code>2342</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/api/v1/status</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "photoprism",
|
||||
"config": {
|
||||
"subdomain": "gallery",
|
||||
"port": 2342
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy PhotoPrism on my home host and expose it at gallery.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull photoprism/photoprism:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Change admin password</li>
|
||||
<li>Import your photos</li>
|
||||
<li>Run indexing for AI features</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/photoprism/storage:/photoprism/storage</code></li>
|
||||
<li><code>/opt/photoprism/originals:/photoprism/originals</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>PHOTOPRISM_ADMIN_PASSWORD</code></li>
|
||||
<li><code>PHOTOPRISM_SITE_URL</code></li>
|
||||
<li><code>PHOTOPRISM_DATABASE_DRIVER</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull photoprism/photoprism:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with PhotoPrism:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/api/v1/status</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>photoprism</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Pi-hole — DashCaddy Docs',
|
||||
description: 'Install and configure Pi-hole via DashCaddy. Network-wide ad blocker and DNS sinkhole',
|
||||
};
|
||||
|
||||
export default function piholeDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Pi-hole"
|
||||
intro="Network-wide ad blocker and DNS sinkhole"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Networking</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Difficulty: Intermediate</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">pihole/pihole:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Pi-hole?</h2>
|
||||
<p>Network-wide ad blocker and DNS sinkhole</p>
|
||||
<p>Pi-hole ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Pi-hole, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Pi-hole</strong> from the Networking category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>pihole</code>), host port (default: <code>80</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/admin/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "pihole",
|
||||
"config": {
|
||||
"subdomain": "pihole",
|
||||
"port": 80
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Pi-hole on my home host and expose it at pihole.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull pihole/pihole:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Open the deployed URL (returned in the response as <code>url</code>, or visible in the dashboard).</li>
|
||||
<li>Complete the upstream Pi-hole setup wizard (admin account, library paths, EULA).</li>
|
||||
<li>Restore from a backup if one exists: <code>POST /api/v1/apps/{appId}/restore</code> with the backup ID from <code>GET /api/v1/backups/history</code>.</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/pihole/etc:/etc/pihole</code></li>
|
||||
<li><code>/opt/pihole/dnsmasq:/etc/dnsmasq.d</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>WEBPASSWORD</code></li>
|
||||
<li><code>TZ</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull pihole/pihole:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Pi-hole:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/admin/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>pihole</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Plex — DashCaddy Docs',
|
||||
description: 'Install and configure Plex via DashCaddy. Stream your personal media collection anywhere',
|
||||
};
|
||||
|
||||
export default function plexDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Plex"
|
||||
intro="Stream your personal media collection anywhere"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Media</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">plexinc/pms-docker:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Plex?</h2>
|
||||
<p>Stream your personal media collection anywhere</p>
|
||||
<p>Plex ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Plex, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>A host path containing your media. Default suggestion: <code>/media</code>. The deploy form / API payload <code>config.mediaPath</code> must be readable by the container UID (usually <code>1000</code>).</li>
|
||||
<li>A <strong>Plex Claim Token</strong> — get one from <a href="https://plex.tv/claim" className="text-brand-400 underline">https://plex.tv/claim</a> right before you click Deploy.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Plex</strong> from the Media category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>plex</code>), host port (default: <code>32400</code>), and the media library path, and the claim token.</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/web/index.html</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "plex",
|
||||
"config": {
|
||||
"subdomain": "plex",
|
||||
"port": 32400,
|
||||
"mediaPath": "/media",
|
||||
"plexClaimToken": "<get fresh token from https://plex.tv/claim>"
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Plex on my home host and expose it at plex.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull plexinc/pms-docker:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Get your claim token from https://plex.tv/claim</li>
|
||||
<li>Add your media libraries in the web interface</li>
|
||||
<li>Configure remote access settings</li>
|
||||
</ol>
|
||||
<h2>Media library path notes</h2>
|
||||
<p>The media mount path you pass as <code>mediaPath</code> in the deploy payload is mounted as <code>/data</code> inside the container. Bind a host directory containing your media library (movies, TV shows, music, etc.).</p>
|
||||
<ul>
|
||||
<li><strong>UID/GID:</strong> Plex runs as a non-root user. If you see permission errors in the dashboard Logs tab, run <code>chown -R 1000:1000 /media</code> on the host.</li>
|
||||
<li><strong>Multi-library:</strong> bind the parent folder and let Plex discover subfolders.</li>
|
||||
</ul>
|
||||
<h2>Plex Claim Token</h2>
|
||||
<p>Get from https://plex.tv/claim - expires in 4 minutes!</p>
|
||||
<p>Pass it as <code>plexClaimToken</code> inside the <code>config</code> object of the deploy payload (NOT as an environment variable).</p>
|
||||
<blockquote className="border-l-4 border-yellow-500/50 bg-yellow-500/5 p-4 rounded-r-lg">
|
||||
<p className="text-yellow-200"><strong>Heads up:</strong> Plex Claim Token expires within minutes. Get a fresh one from
|
||||
<a href="https://plex.tv/claim" className="underline"> https://plex.tv/claim</a>
|
||||
right before you click <em>Deploy</em>.</p>
|
||||
</blockquote>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/plex/config:/config</code></li>
|
||||
<li><code>/opt/plex/transcode:/transcode</code></li>
|
||||
<li><code>MEDIA_PATH:/data</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>PLEX_CLAIM</code></li>
|
||||
<li><code>ADVERTISE_IP</code></li>
|
||||
<li><code>PLEX_UID</code></li>
|
||||
<li><code>PLEX_GID</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull plexinc/pms-docker:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Plex:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>Library shows empty:</strong> confirm <code>mediaPath</code> is readable by the container UID and that the directory contains the file extensions Plex indexes.</li>
|
||||
<li><strong>Account linking fails:</strong> your claim token probably expired. Get a new one and redeploy.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/web/index.html</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>plex</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Portainer — DashCaddy Docs',
|
||||
description: 'Install and configure Portainer via DashCaddy. Docker container management UI',
|
||||
};
|
||||
|
||||
export default function portainerDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Portainer"
|
||||
intro="Docker container management UI"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Management</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">portainer/portainer-ce:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Portainer?</h2>
|
||||
<p>Docker container management UI</p>
|
||||
<p>Portainer ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Portainer, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Portainer</strong> from the Management category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>portainer</code>), host port (default: <code>9000</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/api/status</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "portainer",
|
||||
"config": {
|
||||
"subdomain": "portainer",
|
||||
"port": 9000
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Portainer on my home host and expose it at portainer.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull portainer/portainer-ce:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Open the deployed URL (returned in the response as <code>url</code>, or visible in the dashboard).</li>
|
||||
<li>Complete the upstream Portainer setup wizard (admin account, library paths, EULA).</li>
|
||||
<li>Restore from a backup if one exists: <code>POST /api/v1/apps/{appId}/restore</code> with the backup ID from <code>GET /api/v1/backups/history</code>.</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/var/run/docker.sock:/var/run/docker.sock</code></li>
|
||||
<li><code>/opt/portainer/data:/data</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<p>None. The container runs with its upstream defaults.</p><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull portainer/portainer-ce:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Portainer:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/api/status</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>portainer</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install PostgreSQL — DashCaddy Docs',
|
||||
description: 'Install and configure PostgreSQL via DashCaddy. Advanced open-source relational database',
|
||||
};
|
||||
|
||||
export default function postgresDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install PostgreSQL"
|
||||
intro="Advanced open-source relational database"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Database</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Difficulty: Intermediate</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">postgres:16-alpine</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is PostgreSQL?</h2>
|
||||
<p>Advanced open-source relational database</p>
|
||||
<p>PostgreSQL ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using PostgreSQL, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>PostgreSQL</strong> from the Database category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>postgres</code>), host port (default: <code>5432</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "postgres",
|
||||
"config": {
|
||||
"subdomain": "postgres",
|
||||
"port": 5432
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy PostgreSQL on my home host and expose it at postgres.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull postgres:16-alpine</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Change default password immediately</li>
|
||||
<li>Create databases and users as needed</li>
|
||||
<li>Configure pg_hba.conf for remote access</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/postgres/data:/var/lib/postgresql/data</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>POSTGRES_USER</code></li>
|
||||
<li><code>POSTGRES_PASSWORD</code></li>
|
||||
<li><code>POSTGRES_DB</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull postgres:16-alpine</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with PostgreSQL:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>postgres</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install PowerDNS — DashCaddy Docs',
|
||||
description: 'Install and configure PowerDNS via DashCaddy. High-performance DNS server with SQL backend',
|
||||
};
|
||||
|
||||
export default function powerdnsDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install PowerDNS"
|
||||
intro="High-performance DNS server with SQL backend"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: DNS</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Difficulty: Intermediate</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">pschiffe/pdns-mysql:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is PowerDNS?</h2>
|
||||
<p>High-performance DNS server with SQL backend</p>
|
||||
<p>PowerDNS ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using PowerDNS, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>PowerDNS</strong> from the DNS category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>dns3</code>), host port (default: <code>8081</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/api/v1/servers</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "powerdns",
|
||||
"config": {
|
||||
"subdomain": "dns3",
|
||||
"port": 8081
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy PowerDNS on my home host and expose it at dns3.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull pschiffe/pdns-mysql:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Access API at https://dns3.sami:8081</li>
|
||||
<li>Use API key for authentication</li>
|
||||
<li>Create zone via API or PowerDNS Admin</li>
|
||||
<li>Add records for your .sami domain</li>
|
||||
<li>Configure devices to use DNS server</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/powerdns/data:/var/lib/mysql</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>PDNS_api</code></li>
|
||||
<li><code>PDNS_api_key</code></li>
|
||||
<li><code>PDNS_webserver</code></li>
|
||||
<li><code>PDNS_webserver_address</code></li>
|
||||
<li><code>PDNS_webserver_allow_from</code></li>
|
||||
<li><code>MYSQL_ROOT_PASSWORD</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull pschiffe/pdns-mysql:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with PowerDNS:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/api/v1/servers</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>powerdns</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Prowlarr — DashCaddy Docs',
|
||||
description: 'Install and configure Prowlarr via DashCaddy. Indexer manager/proxy for *arr applications',
|
||||
};
|
||||
|
||||
export default function prowlarrDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Prowlarr"
|
||||
intro="Indexer manager/proxy for *arr applications"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Media Management</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#f5a62322', color: '#f5a623' }}>Difficulty: Advanced</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">linuxserver/prowlarr:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Prowlarr?</h2>
|
||||
<p>Indexer manager/proxy for *arr applications</p>
|
||||
<p>Prowlarr ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Prowlarr, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Prowlarr</strong> from the Media Management category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>prowlarr</code>), host port (default: <code>9696</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/api/v1/system/status</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "prowlarr",
|
||||
"config": {
|
||||
"subdomain": "prowlarr",
|
||||
"port": 9696
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Prowlarr on my home host and expose it at prowlarr.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull linuxserver/prowlarr:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Open the deployed URL (returned in the response as <code>url</code>, or visible in the dashboard).</li>
|
||||
<li>Complete the upstream Prowlarr setup wizard (admin account, library paths, EULA).</li>
|
||||
<li>Restore from a backup if one exists: <code>POST /api/v1/apps/{appId}/restore</code> with the backup ID from <code>GET /api/v1/backups/history</code>.</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/prowlarr/config:/config</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>PUID</code></li>
|
||||
<li><code>PGID</code></li>
|
||||
<li><code>TZ</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull linuxserver/prowlarr:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Prowlarr:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/api/v1/system/status</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>prowlarr</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install qBittorrent — DashCaddy Docs',
|
||||
description: 'Install and configure qBittorrent via DashCaddy. Lightweight BitTorrent client with web UI',
|
||||
};
|
||||
|
||||
export default function qbittorrentDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install qBittorrent"
|
||||
intro="Lightweight BitTorrent client with web UI"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Downloads</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">linuxserver/qbittorrent:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is qBittorrent?</h2>
|
||||
<p>Lightweight BitTorrent client with web UI</p>
|
||||
<p>qBittorrent ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using qBittorrent, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>qBittorrent</strong> from the Downloads category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>torrent</code>), host port (default: <code>8080</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "qbittorrent",
|
||||
"config": {
|
||||
"subdomain": "torrent",
|
||||
"port": 8080
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy qBittorrent on my home host and expose it at torrent.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull linuxserver/qbittorrent:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Default login: admin/adminadmin</li>
|
||||
<li>Change default password immediately</li>
|
||||
<li>Configure download paths</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/qbittorrent/config:/config</code></li>
|
||||
<li><code>/downloads:/downloads</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>PUID</code></li>
|
||||
<li><code>PGID</code></li>
|
||||
<li><code>TZ</code></li>
|
||||
<li><code>WEBUI_PORT</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull linuxserver/qbittorrent:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with qBittorrent:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>qbittorrent</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Radarr — DashCaddy Docs',
|
||||
description: 'Install and configure Radarr via DashCaddy. Movie collection manager for Usenet and BitTorrent',
|
||||
};
|
||||
|
||||
export default function radarrDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Radarr"
|
||||
intro="Movie collection manager for Usenet and BitTorrent"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Media Management</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Difficulty: Intermediate</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">linuxserver/radarr:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Radarr?</h2>
|
||||
<p>Movie collection manager for Usenet and BitTorrent</p>
|
||||
<p>Radarr ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Radarr, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Radarr</strong> from the Media Management category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>radarr</code>), host port (default: <code>7878</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/api/v3/system/status</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "radarr",
|
||||
"config": {
|
||||
"subdomain": "radarr",
|
||||
"port": 7878
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Radarr on my home host and expose it at radarr.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull linuxserver/radarr:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Open the deployed URL (returned in the response as <code>url</code>, or visible in the dashboard).</li>
|
||||
<li>Complete the upstream Radarr setup wizard (admin account, library paths, EULA).</li>
|
||||
<li>Restore from a backup if one exists: <code>POST /api/v1/apps/{appId}/restore</code> with the backup ID from <code>GET /api/v1/backups/history</code>.</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/radarr/config:/config</code></li>
|
||||
<li><code>/downloads:/downloads</code></li>
|
||||
<li><code>/movies:/movies</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>PUID</code></li>
|
||||
<li><code>PGID</code></li>
|
||||
<li><code>TZ</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull linuxserver/radarr:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Radarr:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/api/v3/system/status</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>radarr</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Readarr — DashCaddy Docs',
|
||||
description: 'Install and configure Readarr via DashCaddy. Book and audiobook collection manager',
|
||||
};
|
||||
|
||||
export default function readarrDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Readarr"
|
||||
intro="Book and audiobook collection manager"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Media Management</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Difficulty: Intermediate</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">linuxserver/readarr:develop</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Readarr?</h2>
|
||||
<p>Book and audiobook collection manager</p>
|
||||
<p>Readarr ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Readarr, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Readarr</strong> from the Media Management category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>readarr</code>), host port (default: <code>8787</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/api/v1/system/status</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "readarr",
|
||||
"config": {
|
||||
"subdomain": "readarr",
|
||||
"port": 8787
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Readarr on my home host and expose it at readarr.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull linuxserver/readarr:develop</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Configure download clients</li>
|
||||
<li>Add indexers for books</li>
|
||||
<li>Set up root folders</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/readarr/config:/config</code></li>
|
||||
<li><code>/downloads:/downloads</code></li>
|
||||
<li><code>/books:/books</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>PUID</code></li>
|
||||
<li><code>PGID</code></li>
|
||||
<li><code>TZ</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull linuxserver/readarr:develop</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Readarr:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/api/v1/system/status</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>readarr</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Redis — DashCaddy Docs',
|
||||
description: 'Install and configure Redis via DashCaddy. In-memory data structure store and cache',
|
||||
};
|
||||
|
||||
export default function redisDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Redis"
|
||||
intro="In-memory data structure store and cache"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Database</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">redis:alpine</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Redis?</h2>
|
||||
<p>In-memory data structure store and cache</p>
|
||||
<p>Redis ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Redis, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Redis</strong> from the Database category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>redis</code>), host port (default: <code>6379</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "redis",
|
||||
"config": {
|
||||
"subdomain": "redis",
|
||||
"port": 6379
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Redis on my home host and expose it at redis.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull redis:alpine</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Configure redis.conf for persistence</li>
|
||||
<li>Set up authentication if needed</li>
|
||||
<li>Configure maxmemory policy</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/redis/data:/data</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<p>None. The container runs with its upstream defaults.</p><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull redis:alpine</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Redis:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>redis</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Rocket.Chat — DashCaddy Docs',
|
||||
description: 'Install and configure Rocket.Chat via DashCaddy. Team collaboration platform like Slack',
|
||||
};
|
||||
|
||||
export default function rocketchatDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Rocket.Chat"
|
||||
intro="Team collaboration platform like Slack"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Communication</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Difficulty: Intermediate</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">rocket.chat:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Rocket.Chat?</h2>
|
||||
<p>Team collaboration platform like Slack</p>
|
||||
<p>Rocket.Chat ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Rocket.Chat, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Rocket.Chat</strong> from the Communication category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>chat</code>), host port (default: <code>3004</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/api/info</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "rocketchat",
|
||||
"config": {
|
||||
"subdomain": "chat",
|
||||
"port": 3004
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Rocket.Chat on my home host and expose it at chat.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull rocket.chat:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Requires MongoDB - deploy mongo container first</li>
|
||||
<li>Complete admin setup wizard</li>
|
||||
<li>Configure OAuth and integrations</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/rocketchat/uploads:/app/uploads</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>ROOT_URL</code></li>
|
||||
<li><code>MONGO_URL</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull rocket.chat:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Rocket.Chat:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/api/info</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>rocketchat</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Roundcube — DashCaddy Docs',
|
||||
description: 'Install and configure Roundcube via DashCaddy. Modern webmail client with rich features',
|
||||
};
|
||||
|
||||
export default function roundcubeDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Roundcube"
|
||||
intro="Modern webmail client with rich features"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Communication</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Difficulty: Intermediate</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">roundcube/roundcubemail:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Roundcube?</h2>
|
||||
<p>Modern webmail client with rich features</p>
|
||||
<p>Roundcube ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Roundcube, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Roundcube</strong> from the Communication category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>webmail</code>), host port (default: <code>8086</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "roundcube",
|
||||
"config": {
|
||||
"subdomain": "webmail",
|
||||
"port": 8086
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Roundcube on my home host and expose it at webmail.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull roundcube/roundcubemail:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Configure IMAP/SMTP server settings</li>
|
||||
<li>Set up database connection</li>
|
||||
<li>Customize appearance and plugins</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/roundcube/config:/var/roundcube/config</code></li>
|
||||
<li><code>/opt/roundcube/db:/var/roundcube/db</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>ROUNDCUBEMAIL_DEFAULT_HOST</code></li>
|
||||
<li><code>ROUNDCUBEMAIL_SMTP_SERVER</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull roundcube/roundcubemail:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Roundcube:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>roundcube</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install SABnzbd — DashCaddy Docs',
|
||||
description: 'Install and configure SABnzbd via DashCaddy. Binary newsreader for Usenet downloads',
|
||||
};
|
||||
|
||||
export default function sabnzbdDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install SABnzbd"
|
||||
intro="Binary newsreader for Usenet downloads"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Downloads</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Difficulty: Intermediate</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">linuxserver/sabnzbd:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is SABnzbd?</h2>
|
||||
<p>Binary newsreader for Usenet downloads</p>
|
||||
<p>SABnzbd ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using SABnzbd, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>SABnzbd</strong> from the Downloads category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>sabnzbd</code>), host port (default: <code>8092</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "sabnzbd",
|
||||
"config": {
|
||||
"subdomain": "sabnzbd",
|
||||
"port": 8092
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy SABnzbd on my home host and expose it at sabnzbd.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull linuxserver/sabnzbd:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Configure Usenet server credentials</li>
|
||||
<li>Set up download categories</li>
|
||||
<li>Configure post-processing scripts</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/sabnzbd/config:/config</code></li>
|
||||
<li><code>/downloads:/downloads</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>PUID</code></li>
|
||||
<li><code>PGID</code></li>
|
||||
<li><code>TZ</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull linuxserver/sabnzbd:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with SABnzbd:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>sabnzbd</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Sami Files — DashCaddy Docs',
|
||||
description: 'Install and configure Sami Files via DashCaddy. Multi-server SSH file manager — browse, edit, upload, and exec across all your machines from one browser tab',
|
||||
};
|
||||
|
||||
export default function samiFilesDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Sami Files"
|
||||
intro="Multi-server SSH file manager — browse, edit, upload, and exec across all your machines from one browser tab"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Files</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Difficulty: Intermediate</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">N/A</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Sami Files?</h2>
|
||||
<p>Multi-server SSH file manager — browse, edit, upload, and exec across all your machines from one browser tab</p>
|
||||
<p>Sami Files ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Sami Files, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Sami Files</strong> from the Files category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>files</code>), host port (default: <code>8765</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>http://127.0.0.1:8765/api/health</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "sami-files",
|
||||
"config": {
|
||||
"subdomain": "files",
|
||||
"port": 8765
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Sami Files on my home host and expose it at files.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull N/A</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Clone the repo: git clone http://100.81.59.99:3030/sami7777/sami-files.git /opt/sami-files</li>
|
||||
<li>Create venv and install deps: /usr/local/lib/hermes-agent/venv/bin/pip install fastapi uvicorn asyncssh pyyaml python-multipart</li>
|
||||
<li>Copy deploy/sami-files.service to /etc/systemd/system/ and `systemctl daemon-reload`</li>
|
||||
<li>Enable + start: systemctl enable --now sami-files.service</li>
|
||||
<li>Edit /opt/sami-files/config/servers.yaml to add your SSH targets</li>
|
||||
<li>Add the Caddy snippet (above) to your Caddyfile and reload Caddy</li>
|
||||
<li>Mount the log dir into DashCaddy: add `-v /opt/sami-files/logs:/opt/sami-files/logs:ro` to start.sh, then recreate the container</li>
|
||||
<li>Browse to https://files.sami — log in via DashCaddy SSO</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<p>None. The container runs with its upstream defaults.</p><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull N/A</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Sami Files:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>http://127.0.0.1:8765/api/health</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>sami-files</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Seerr — DashCaddy Docs',
|
||||
description: 'Install and configure Seerr via DashCaddy. Media request and discovery manager for Plex, Jellyfin, and Emby',
|
||||
};
|
||||
|
||||
export default function seerrDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Seerr"
|
||||
intro="Media request and discovery manager for Plex, Jellyfin, and Emby"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Media Management</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">ghcr.io/seerr-team/seerr:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Seerr?</h2>
|
||||
<p>Media request and discovery manager for Plex, Jellyfin, and Emby</p>
|
||||
<p>Seerr ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Seerr, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Seerr</strong> from the Media Management category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>requests</code>), host port (default: <code>5055</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/api/v1/status</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "seerr",
|
||||
"config": {
|
||||
"subdomain": "requests",
|
||||
"port": 5055
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Seerr on my home host and expose it at requests.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull ghcr.io/seerr-team/seerr:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Connect to Plex, Jellyfin, or Emby server</li>
|
||||
<li>Link Sonarr and Radarr</li>
|
||||
<li>Configure user permissions</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/seerr/config:/app/config</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>TZ</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull ghcr.io/seerr-team/seerr:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Seerr:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/api/v1/status</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>seerr</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Sonarr — DashCaddy Docs',
|
||||
description: 'Install and configure Sonarr via DashCaddy. Smart PVR for newsgroup and bittorrent users',
|
||||
};
|
||||
|
||||
export default function sonarrDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Sonarr"
|
||||
intro="Smart PVR for newsgroup and bittorrent users"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Media Management</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Difficulty: Intermediate</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">linuxserver/sonarr:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Sonarr?</h2>
|
||||
<p>Smart PVR for newsgroup and bittorrent users</p>
|
||||
<p>Sonarr ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Sonarr, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Sonarr</strong> from the Media Management category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>sonarr</code>), host port (default: <code>8989</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/api/v3/system/status</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "sonarr",
|
||||
"config": {
|
||||
"subdomain": "sonarr",
|
||||
"port": 8989
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Sonarr on my home host and expose it at sonarr.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull linuxserver/sonarr:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Configure download clients (qBittorrent, etc.)</li>
|
||||
<li>Add indexers for content discovery</li>
|
||||
<li>Set up root folders for TV shows</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/sonarr/config:/config</code></li>
|
||||
<li><code>/downloads:/downloads</code></li>
|
||||
<li><code>/tv:/tv</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>PUID</code></li>
|
||||
<li><code>PGID</code></li>
|
||||
<li><code>TZ</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull linuxserver/sonarr:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Sonarr:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/api/v3/system/status</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>sonarr</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Speedtest Tracker — DashCaddy Docs',
|
||||
description: 'Install and configure Speedtest Tracker via DashCaddy. Internet speed monitoring over time',
|
||||
};
|
||||
|
||||
export default function speedtestDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Speedtest Tracker"
|
||||
intro="Internet speed monitoring over time"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Monitoring</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">ghcr.io/alexjustesen/speedtest-tracker:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Speedtest Tracker?</h2>
|
||||
<p>Internet speed monitoring over time</p>
|
||||
<p>Speedtest Tracker ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Speedtest Tracker, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Speedtest Tracker</strong> from the Monitoring category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>speedtest</code>), host port (default: <code>8093</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "speedtest",
|
||||
"config": {
|
||||
"subdomain": "speedtest",
|
||||
"port": 8093
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Speedtest Tracker on my home host and expose it at speedtest.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull ghcr.io/alexjustesen/speedtest-tracker:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Configure test schedule</li>
|
||||
<li>View historical data</li>
|
||||
<li>Set up notifications for slow speeds</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/speedtest/config:/config</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>PUID</code></li>
|
||||
<li><code>PGID</code></li>
|
||||
<li><code>DB_CONNECTION</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull ghcr.io/alexjustesen/speedtest-tracker:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Speedtest Tracker:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>speedtest</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Standard Notes — DashCaddy Docs',
|
||||
description: 'Install and configure Standard Notes via DashCaddy. End-to-end encrypted notes app',
|
||||
};
|
||||
|
||||
export default function standardnotesDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Standard Notes"
|
||||
intro="End-to-end encrypted notes app"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Productivity</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Difficulty: Intermediate</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">standardnotes/server:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Standard Notes?</h2>
|
||||
<p>End-to-end encrypted notes app</p>
|
||||
<p>Standard Notes ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Standard Notes, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Standard Notes</strong> from the Productivity category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>notes</code>), host port (default: <code>3007</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "standardnotes",
|
||||
"config": {
|
||||
"subdomain": "notes",
|
||||
"port": 3007
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Standard Notes on my home host and expose it at notes.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull standardnotes/server:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Configure environment variables</li>
|
||||
<li>Set up database connection</li>
|
||||
<li>Install Standard Notes apps</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/standardnotes/data:/var/lib/server</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>RAILS_ENV</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull standardnotes/server:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Standard Notes:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>standardnotes</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Stirling PDF — DashCaddy Docs',
|
||||
description: 'Install and configure Stirling PDF via DashCaddy. Self-hosted PDF manipulation tool - merge, split, convert, and more',
|
||||
};
|
||||
|
||||
export default function stirlingPdfDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Stirling PDF"
|
||||
intro="Self-hosted PDF manipulation tool - merge, split, convert, and more"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Utilities</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">frooodle/s-pdf:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Stirling PDF?</h2>
|
||||
<p>Self-hosted PDF manipulation tool - merge, split, convert, and more</p>
|
||||
<p>Stirling PDF ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Stirling PDF, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Stirling PDF</strong> from the Utilities category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>pdf</code>), host port (default: <code>8084</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "stirling-pdf",
|
||||
"config": {
|
||||
"subdomain": "pdf",
|
||||
"port": 8084
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Stirling PDF on my home host and expose it at pdf.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull frooodle/s-pdf:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Access the web interface to start manipulating PDFs</li>
|
||||
<li>Supports merge, split, rotate, convert, compress, and more</li>
|
||||
<li>Optional OCR support via Tesseract</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/stirling-pdf/data:/usr/share/tessdata</code></li>
|
||||
<li><code>/opt/stirling-pdf/config:/configs</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>DOCKER_ENABLE_SECURITY</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull frooodle/s-pdf:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Stirling PDF:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>stirling-pdf</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Syncthing — DashCaddy Docs',
|
||||
description: 'Install and configure Syncthing via DashCaddy. Continuous file synchronization between devices',
|
||||
};
|
||||
|
||||
export default function syncthingDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Syncthing"
|
||||
intro="Continuous file synchronization between devices"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Files</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">linuxserver/syncthing:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Syncthing?</h2>
|
||||
<p>Continuous file synchronization between devices</p>
|
||||
<p>Syncthing ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Syncthing, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Syncthing</strong> from the Files category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>sync</code>), host port (default: <code>8384</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "syncthing",
|
||||
"config": {
|
||||
"subdomain": "sync",
|
||||
"port": 8384
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Syncthing on my home host and expose it at sync.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull linuxserver/syncthing:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Add devices using their Device IDs</li>
|
||||
<li>Configure shared folders</li>
|
||||
<li>Set up folder synchronization</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/syncthing/config:/config</code></li>
|
||||
<li><code>/opt/syncthing/data:/data</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>PUID</code></li>
|
||||
<li><code>PGID</code></li>
|
||||
<li><code>TZ</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull linuxserver/syncthing:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Syncthing:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>syncthing</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Tautulli — DashCaddy Docs',
|
||||
description: 'Install and configure Tautulli via DashCaddy. Plex media server monitoring and statistics',
|
||||
};
|
||||
|
||||
export default function tautulliDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Tautulli"
|
||||
intro="Plex media server monitoring and statistics"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Media Management</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">linuxserver/tautulli:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Tautulli?</h2>
|
||||
<p>Plex media server monitoring and statistics</p>
|
||||
<p>Tautulli ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Tautulli, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Tautulli</strong> from the Media Management category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>tautulli</code>), host port (default: <code>8181</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "tautulli",
|
||||
"config": {
|
||||
"subdomain": "tautulli",
|
||||
"port": 8181
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Tautulli on my home host and expose it at tautulli.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull linuxserver/tautulli:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Connect to Plex server</li>
|
||||
<li>Configure notifications</li>
|
||||
<li>Set up newsletters</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/tautulli/config:/config</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>PUID</code></li>
|
||||
<li><code>PGID</code></li>
|
||||
<li><code>TZ</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull linuxserver/tautulli:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Tautulli:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>tautulli</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Technitium DNS Server — DashCaddy Docs',
|
||||
description: 'Install and configure Technitium DNS Server via DashCaddy. Modern DNS server with web UI for managing private zones',
|
||||
};
|
||||
|
||||
export default function technitiumDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Technitium DNS Server"
|
||||
intro="Modern DNS server with web UI for managing private zones"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: DNS</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">technitium/dns-server:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Technitium DNS Server?</h2>
|
||||
<p>Modern DNS server with web UI for managing private zones</p>
|
||||
<p>Technitium DNS Server ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Technitium DNS Server, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Technitium DNS Server</strong> from the DNS category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>dns1</code>), host port (default: <code>5380</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "technitium",
|
||||
"config": {
|
||||
"subdomain": "dns1",
|
||||
"port": 5380
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Technitium DNS Server on my home host and expose it at dns1.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull technitium/dns-server:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Access web interface at https://dns1.sami</li>
|
||||
<li>Login with admin credentials</li>
|
||||
<li>Create a primary zone for 'sami' domain</li>
|
||||
<li>Add A records for your services (e.g., plex.sami -> 192.168.1.100)</li>
|
||||
<li>Configure your devices to use this DNS server</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/technitium/config:/etc/dns</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>DNS_SERVER_DOMAIN</code></li>
|
||||
<li><code>DNS_SERVER_ADMIN_PASSWORD</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull technitium/dns-server:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Technitium DNS Server:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>technitium</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Transmission — DashCaddy Docs',
|
||||
description: 'Install and configure Transmission via DashCaddy. Lightweight BitTorrent client',
|
||||
};
|
||||
|
||||
export default function transmissionDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Transmission"
|
||||
intro="Lightweight BitTorrent client"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Downloads</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">linuxserver/transmission:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Transmission?</h2>
|
||||
<p>Lightweight BitTorrent client</p>
|
||||
<p>Transmission ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Transmission, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Transmission</strong> from the Downloads category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>transmission</code>), host port (default: <code>9092</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/transmission/web/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "transmission",
|
||||
"config": {
|
||||
"subdomain": "transmission",
|
||||
"port": 9092
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Transmission on my home host and expose it at transmission.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull linuxserver/transmission:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Configure download paths</li>
|
||||
<li>Set bandwidth limits</li>
|
||||
<li>Configure blocklists if needed</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/transmission/config:/config</code></li>
|
||||
<li><code>/downloads:/downloads</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>PUID</code></li>
|
||||
<li><code>PGID</code></li>
|
||||
<li><code>TZ</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull linuxserver/transmission:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Transmission:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/transmission/web/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>transmission</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Trilium Notes — DashCaddy Docs',
|
||||
description: 'Install and configure Trilium Notes via DashCaddy. Hierarchical knowledge base and note-taking app',
|
||||
};
|
||||
|
||||
export default function triliumDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Trilium Notes"
|
||||
intro="Hierarchical knowledge base and note-taking app"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Productivity</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">zadam/trilium:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Trilium Notes?</h2>
|
||||
<p>Hierarchical knowledge base and note-taking app</p>
|
||||
<p>Trilium Notes ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Trilium Notes, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Trilium Notes</strong> from the Productivity category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>notes</code>), host port (default: <code>8085</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "trilium",
|
||||
"config": {
|
||||
"subdomain": "notes",
|
||||
"port": 8085
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Trilium Notes on my home host and expose it at notes.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull zadam/trilium:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Set your password on first access</li>
|
||||
<li>Organize notes in a tree hierarchy</li>
|
||||
<li>Supports rich text, code blocks, math equations, and diagrams</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/trilium/data:/home/node/trilium-data</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<p>None. The container runs with its upstream defaults.</p><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull zadam/trilium:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Trilium Notes:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>trilium</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Uptime Kuma — DashCaddy Docs',
|
||||
description: 'Install and configure Uptime Kuma via DashCaddy. Self-hosted monitoring tool like Uptime Robot',
|
||||
};
|
||||
|
||||
export default function uptimeKumaDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Uptime Kuma"
|
||||
intro="Self-hosted monitoring tool like Uptime Robot"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Monitoring</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">louislam/uptime-kuma:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Uptime Kuma?</h2>
|
||||
<p>Self-hosted monitoring tool like Uptime Robot</p>
|
||||
<p>Uptime Kuma ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Uptime Kuma, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Uptime Kuma</strong> from the Monitoring category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>uptime</code>), host port (default: <code>3002</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "uptime-kuma",
|
||||
"config": {
|
||||
"subdomain": "uptime",
|
||||
"port": 3002
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Uptime Kuma on my home host and expose it at uptime.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull louislam/uptime-kuma:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Open the deployed URL (returned in the response as <code>url</code>, or visible in the dashboard).</li>
|
||||
<li>Complete the upstream Uptime Kuma setup wizard (admin account, library paths, EULA).</li>
|
||||
<li>Restore from a backup if one exists: <code>POST /api/v1/apps/{appId}/restore</code> with the backup ID from <code>GET /api/v1/backups/history</code>.</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/uptime-kuma:/app/data</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<p>None. The container runs with its upstream defaults.</p><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull louislam/uptime-kuma:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Uptime Kuma:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>uptime-kuma</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Valheim Server — DashCaddy Docs',
|
||||
description: 'Install and configure Valheim Server via DashCaddy. Valheim dedicated server for multiplayer Viking adventures',
|
||||
};
|
||||
|
||||
export default function valheimDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Valheim Server"
|
||||
intro="Valheim dedicated server for multiplayer Viking adventures"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Gaming</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">lloesche/valheim-server:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Valheim Server?</h2>
|
||||
<p>Valheim dedicated server for multiplayer Viking adventures</p>
|
||||
<p>Valheim Server ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Valheim Server, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Valheim Server</strong> from the Gaming category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>valheim</code>), host port (default: <code>2456</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>tcp://localhost:2456</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "valheim",
|
||||
"config": {
|
||||
"subdomain": "valheim",
|
||||
"port": 2456
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Valheim Server on my home host and expose it at valheim.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull lloesche/valheim-server:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Connect via Steam: Add Server > IP:2456</li>
|
||||
<li>Default server password is auto-generated (check environment variables)</li>
|
||||
<li>World data is persisted in the data volume</li>
|
||||
<li>Requires at least 4GB RAM for smooth operation</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/valheim/config:/config</code></li>
|
||||
<li><code>/opt/valheim/data:/opt/valheim</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>SERVER_NAME</code></li>
|
||||
<li><code>WORLD_NAME</code></li>
|
||||
<li><code>SERVER_PASS</code></li>
|
||||
<li><code>SERVER_PUBLIC</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull lloesche/valheim-server:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Valheim Server:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>tcp://localhost:2456</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>valheim</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Vaultwarden — DashCaddy Docs',
|
||||
description: 'Install and configure Vaultwarden via DashCaddy. Lightweight Bitwarden-compatible password manager',
|
||||
};
|
||||
|
||||
export default function vaultwardenDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Vaultwarden"
|
||||
intro="Lightweight Bitwarden-compatible password manager"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Security</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">vaultwarden/server:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Vaultwarden?</h2>
|
||||
<p>Lightweight Bitwarden-compatible password manager</p>
|
||||
<p>Vaultwarden ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Vaultwarden, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Vaultwarden</strong> from the Security category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>vault</code>), host port (default: <code>8088</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "vaultwarden",
|
||||
"config": {
|
||||
"subdomain": "vault",
|
||||
"port": 8088
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Vaultwarden on my home host and expose it at vault.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull vaultwarden/server:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Change admin token immediately</li>
|
||||
<li>Create your account</li>
|
||||
<li>Install browser extensions and mobile apps</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/vaultwarden/data:/data</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>DOMAIN</code></li>
|
||||
<li><code>ADMIN_TOKEN</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull vaultwarden/server:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Vaultwarden:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>vaultwarden</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Vintage Stereo — DashCaddy Docs',
|
||||
description: 'Install and configure Vintage Stereo via DashCaddy. Glass-front console stereo that tunes curated real internet stations (SomaFM, KEXP, Radio Paradise, and more) through a beautiful analog UI',
|
||||
};
|
||||
|
||||
export default function vintageRadioDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Vintage Stereo"
|
||||
intro="Glass-front console stereo that tunes curated real internet stations (SomaFM, KEXP, Radio Paradise, and more) through a beautiful analog UI"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Media</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">nginx:alpine</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Vintage Stereo?</h2>
|
||||
<p>Glass-front console stereo that tunes curated real internet stations (SomaFM, KEXP, Radio Paradise, and more) through a beautiful analog UI</p>
|
||||
<p>Vintage Stereo ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Vintage Stereo, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Vintage Stereo</strong> from the Media category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>radio</code>), host port (default: <code>8090</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "vintage-radio",
|
||||
"config": {
|
||||
"subdomain": "radio",
|
||||
"port": 8090
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Vintage Stereo on my home host and expose it at radio.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull nginx:alpine</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Run `bash /usr/local/bin/vintage-radio-install.sh` once before starting the container — copies the bundled web assets (index.html, radio.css, radio.js, stations.json) from the DashCaddy repo (dashcaddy-api/static-sites/vintage-radio/web) into /opt/vintage-radio/web</li>
|
||||
<li>Open radio.sami (or your configured subdomain)</li>
|
||||
<li>Press the PWR knob, drag the dial or click a station card</li>
|
||||
<li>Cycle the MODE knob to filter by genre (ALL / AMBIENT / ROCK / MIXED)</li>
|
||||
<li>To add stations, edit /opt/vintage-radio/web/stations.json on the host and restart the container</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/vintage-radio/web:/usr/share/nginx/html:ro</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<p>None. The container runs with its upstream defaults.</p><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull nginx:alpine</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Vintage Stereo:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>vintage-radio</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install VS Code Server — DashCaddy Docs',
|
||||
description: 'Install and configure VS Code Server via DashCaddy. Visual Studio Code in your browser',
|
||||
};
|
||||
|
||||
export default function vscodeServerDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install VS Code Server"
|
||||
intro="Visual Studio Code in your browser"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Development</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">codercom/code-server:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is VS Code Server?</h2>
|
||||
<p>Visual Studio Code in your browser</p>
|
||||
<p>VS Code Server ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using VS Code Server, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>VS Code Server</strong> from the Development category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>code</code>), host port (default: <code>8443</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/healthz</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "vscode-server",
|
||||
"config": {
|
||||
"subdomain": "code",
|
||||
"port": 8443
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy VS Code Server on my home host and expose it at code.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull codercom/code-server:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Open the deployed URL (returned in the response as <code>url</code>, or visible in the dashboard).</li>
|
||||
<li>Complete the upstream VS Code Server setup wizard (admin account, library paths, EULA).</li>
|
||||
<li>Restore from a backup if one exists: <code>POST /api/v1/apps/{appId}/restore</code> with the backup ID from <code>GET /api/v1/backups/history</code>.</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/vscode/config:/home/coder/.config</code></li>
|
||||
<li><code>/opt/vscode/projects:/home/coder/projects</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>PASSWORD</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull codercom/code-server:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with VS Code Server:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/healthz</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>vscode-server</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Watchtower — DashCaddy Docs',
|
||||
description: 'Install and configure Watchtower via DashCaddy. Automatic Docker container image updates',
|
||||
};
|
||||
|
||||
export default function watchtowerDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Watchtower"
|
||||
intro="Automatic Docker container image updates"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Management</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">containrrr/watchtower:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Watchtower?</h2>
|
||||
<p>Automatic Docker container image updates</p>
|
||||
<p>Watchtower ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Watchtower, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Watchtower</strong> from the Management category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>watchtower</code>), host port (default: <code>8089</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/v1/update</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "watchtower",
|
||||
"config": {
|
||||
"subdomain": "watchtower",
|
||||
"port": 8089
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Watchtower on my home host and expose it at watchtower.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull containrrr/watchtower:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Watchtower checks for image updates daily at 4 AM by default</li>
|
||||
<li>Customize schedule via WATCHTOWER_SCHEDULE (cron format)</li>
|
||||
<li>Add labels to exclude specific containers from updates</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/var/run/docker.sock:/var/run/docker.sock</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>WATCHTOWER_CLEANUP</code></li>
|
||||
<li><code>WATCHTOWER_SCHEDULE</code></li>
|
||||
<li><code>WATCHTOWER_HTTP_API_METRICS</code></li>
|
||||
<li><code>WATCHTOWER_HTTP_API_TOKEN</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull containrrr/watchtower:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Watchtower:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/v1/update</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>watchtower</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Weather — DashCaddy Docs',
|
||||
description: 'Install and configure Weather via DashCaddy. Live weather widget with temperature, conditions, and wind',
|
||||
};
|
||||
|
||||
export default function weatherDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Weather"
|
||||
intro="Live weather widget with temperature, conditions, and wind"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Utilities</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">N/A</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Weather?</h2>
|
||||
<p>Live weather widget with temperature, conditions, and wind</p>
|
||||
<p>Weather ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Weather, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Weather</strong> from the Utilities category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>weather</code>), host port (default: <code>32400</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/healthz</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "weather",
|
||||
"config": {
|
||||
"subdomain": "weather",
|
||||
"port": 32400
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Weather on my home host and expose it at weather.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull N/A</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Click the gear icon on the widget to set your ZIP code</li>
|
||||
<li>Weather appears in the top bar next to the logo</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<p>None. The container runs with its upstream defaults.</p><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull N/A</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Weather:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/healthz</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>weather</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Whoami — DashCaddy Docs',
|
||||
description: 'Install and configure Whoami via DashCaddy. Simple HTTP request debugging service',
|
||||
};
|
||||
|
||||
export default function whoamiDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Whoami"
|
||||
intro="Simple HTTP request debugging service"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Utilities</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">traefik/whoami:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Whoami?</h2>
|
||||
<p>Simple HTTP request debugging service</p>
|
||||
<p>Whoami ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Whoami, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Whoami</strong> from the Utilities category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>whoami</code>), host port (default: <code>8094</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "whoami",
|
||||
"config": {
|
||||
"subdomain": "whoami",
|
||||
"port": 8094
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Whoami on my home host and expose it at whoami.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull traefik/whoami:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Useful for testing reverse proxy setup</li>
|
||||
<li>Shows request headers and info</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/whoami/config:/config</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<p>None. The container runs with its upstream defaults.</p><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull traefik/whoami:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Whoami:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>whoami</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install WireGuard VPN — DashCaddy Docs',
|
||||
description: 'Install and configure WireGuard VPN via DashCaddy. Fast, modern, secure VPN tunnel',
|
||||
};
|
||||
|
||||
export default function wireguardDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install WireGuard VPN"
|
||||
intro="Fast, modern, secure VPN tunnel"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Networking</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#f5a62322', color: '#f5a623' }}>Difficulty: Advanced</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">linuxserver/wireguard:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is WireGuard VPN?</h2>
|
||||
<p>Fast, modern, secure VPN tunnel</p>
|
||||
<p>WireGuard VPN ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using WireGuard VPN, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>WireGuard VPN</strong> from the Networking category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>vpn</code>), host port (default: <code>51820</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "wireguard",
|
||||
"config": {
|
||||
"subdomain": "vpn",
|
||||
"port": 51820
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy WireGuard VPN on my home host and expose it at vpn.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull linuxserver/wireguard:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Configure your external IP/domain</li>
|
||||
<li>Set up port forwarding on router</li>
|
||||
<li>Download client configs from /config/peer1/</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/wireguard/config:/config</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>PUID</code></li>
|
||||
<li><code>PGID</code></li>
|
||||
<li><code>TZ</code></li>
|
||||
<li><code>SERVERURL</code></li>
|
||||
<li><code>SERVERPORT</code></li>
|
||||
<li><code>PEERS</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull linuxserver/wireguard:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with WireGuard VPN:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>wireguard</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -34,7 +34,7 @@ export default function DocsFirstServicePage() {
|
||||
|
||||
<h2>Three ways to add a service</h2>
|
||||
|
||||
<h3>1. Pick from 92+ one-click templates</h3>
|
||||
<h3>1. Pick from 77 one-click templates</h3>
|
||||
<p>
|
||||
The template library covers the most popular self-hosted applications — media servers, dashboards,
|
||||
databases, note apps, automation tools, and more. Each template bundles sane defaults for ports,
|
||||
@@ -62,7 +62,7 @@ export default function DocsFirstServicePage() {
|
||||
<h3>Step 1: Open the template library</h3>
|
||||
<p>
|
||||
From the dashboard sidebar, click <strong>New Service → From Template</strong>. The template library
|
||||
opens with a searchable grid of 92+ applications.
|
||||
opens with a searchable grid of 77 applications.
|
||||
</p>
|
||||
|
||||
<h3>Step 2: Find Plex</h3>
|
||||
|
||||
@@ -8,381 +8,281 @@ export default function DocsInstallationPage() {
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Installation Guide"
|
||||
intro="DashCaddy ships as a Docker-based deployment with a guided installer that handles dependency checks, configuration generation, and first launch. This guide covers prerequisites, the installer flow, and what to expect on first run."
|
||||
intro="DashCaddy installs in three commands on any Linux box — a desktop, a NUC, a Raspberry Pi 5, or a VPS. The installer handles Docker, Caddy, data volumes, the setup wizard, and your first login. This guide walks you through picking a hostname, running the installer, what the wizard does, and how to harden the host right after."
|
||||
>
|
||||
<h2>System requirements</h2>
|
||||
<p>Before installing DashCaddy, verify that your host meets these minimum requirements:</p>
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-left text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-surface-700">
|
||||
<th className="py-3 pr-4 font-semibold text-surface-200">Component</th>
|
||||
<th className="py-3 pr-4 font-semibold text-surface-200">Minimum</th>
|
||||
<th className="py-3 font-semibold text-surface-200">Recommended</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="text-surface-300">
|
||||
<tr className="border-b border-surface-800">
|
||||
<td className="py-3 pr-4">Operating System</td>
|
||||
<td className="py-3 pr-4">Linux (Ubuntu 20.04+, Debian 11+, CentOS 8+)</td>
|
||||
<td className="py-3">Ubuntu 22.04 LTS or Debian 12</td>
|
||||
</tr>
|
||||
<tr className="border-b border-surface-800">
|
||||
<td className="py-3 pr-4">CPU</td>
|
||||
<td className="py-3 pr-4">2 cores</td>
|
||||
<td className="py-3">4+ cores</td>
|
||||
</tr>
|
||||
<tr className="border-b border-surface-800">
|
||||
<td className="py-3 pr-4">RAM</td>
|
||||
<td className="py-3 pr-4">2 GB</td>
|
||||
<td className="py-3">4+ GB (8 GB for 20+ services)</td>
|
||||
</tr>
|
||||
<tr className="border-b border-surface-800">
|
||||
<td className="py-3 pr-4">Disk</td>
|
||||
<td className="py-3 pr-4">10 GB free</td>
|
||||
<td className="py-3">50+ GB SSD</td>
|
||||
</tr>
|
||||
<tr className="border-b border-surface-800">
|
||||
<td className="py-3 pr-4">Docker</td>
|
||||
<td className="py-3 pr-4">20.10+</td>
|
||||
<td className="py-3">Latest stable</td>
|
||||
</tr>
|
||||
<tr className="border-b border-surface-800">
|
||||
<td className="py-3 pr-4">Docker Compose</td>
|
||||
<td className="py-3 pr-4">v2.0+</td>
|
||||
<td className="py-3">Latest stable</td>
|
||||
</tr>
|
||||
<tr className="border-b border-surface-800">
|
||||
<td className="py-3 pr-4">Node.js</td>
|
||||
<td className="py-3 pr-4">20.x LTS</td>
|
||||
<td className="py-3">20.x LTS or 22.x LTS</td>
|
||||
</tr>
|
||||
<tr className="border-b border-surface-800">
|
||||
<td className="py-3 pr-4">Caddy</td>
|
||||
<td className="py-3 pr-4">2.6+ with Admin API</td>
|
||||
<td className="py-3">Latest stable</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="py-3 pr-4">Technitium DNS</td>
|
||||
<td className="py-3 pr-4">Optional</td>
|
||||
<td className="py-3">Latest stable (for auto DNS)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<blockquote className="border-l-4 border-brand-500/50 bg-brand-500/5 p-4 rounded-r-lg mt-6">
|
||||
<p className="text-surface-300">
|
||||
<strong className="text-brand-400">Note:</strong> DashCaddy can run on Windows and macOS for development, but production deployments should target Linux. Docker Desktop works for testing but is not recommended for production workloads.
|
||||
</p>
|
||||
</blockquote>
|
||||
|
||||
<h2>Quick start with start.sh</h2>
|
||||
<h2>Before you start</h2>
|
||||
<p>
|
||||
The fastest path to a running DashCaddy is the bundled <code>start.sh</code> script. It performs environment
|
||||
checks, pulls the required containers, generates configuration, and brings the stack up in a single command.
|
||||
You only need three things to install DashCaddy:
|
||||
</p>
|
||||
|
||||
<h3>Step 1: Clone the repository</h3>
|
||||
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`git clone https://github.com/samiahmed7777/dashcaddy.git
|
||||
cd dashcaddy`}</code></pre>
|
||||
|
||||
<h3>Step 2: Make the launcher executable</h3>
|
||||
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`chmod +x start.sh`}</code></pre>
|
||||
|
||||
<h3>Step 3: Run the installer</h3>
|
||||
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`./start.sh`}</code></pre>
|
||||
|
||||
<p>
|
||||
The script is idempotent — re-running it will reconcile the stack rather than clobber an existing install.
|
||||
If DashCaddy is already running, <code>start.sh</code> detects this and offers to update configuration or
|
||||
restart services instead of reinstalling from scratch.
|
||||
</p>
|
||||
|
||||
<h3>What start.sh does</h3>
|
||||
<p>Under the hood, the script performs these steps:</p>
|
||||
<ol>
|
||||
<li><strong>Dependency validation</strong> — checks for Docker, Docker Compose, Node.js, and Caddy</li>
|
||||
<li><strong>Port availability check</strong> — verifies ports 80, 443, and 2019 (Caddy Admin API) are free</li>
|
||||
<li><strong>Configuration generation</strong> — creates <code>.env</code> file with sensible defaults</li>
|
||||
<li><strong>Docker image pull</strong> — fetches the DashCaddy API and dashboard images</li>
|
||||
<li><strong>Container startup</strong> — launches the stack via Docker Compose</li>
|
||||
<li><strong>Health check</strong> — waits for the API to respond on <code>/healthz</code></li>
|
||||
<li><strong>Dashboard URL display</strong> — prints the access URL and initial admin credentials</li>
|
||||
</ol>
|
||||
|
||||
<h2>Docker Compose configuration</h2>
|
||||
<p>
|
||||
If you prefer to inspect or customize the Docker Compose configuration before launching, here's the
|
||||
default <code>docker-compose.yml</code> that <code>start.sh</code> generates:
|
||||
</p>
|
||||
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`version: '3.8'
|
||||
|
||||
services:
|
||||
dashcaddy-api:
|
||||
image: samiahmed7777/dashcaddy-api:latest
|
||||
container_name: dashcaddy-api
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "3001:3001" # API port
|
||||
volumes:
|
||||
- ./data:/app/data
|
||||
- ./config:/app/config
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- DASHCADDY_PORT=3001
|
||||
- CADDY_ADMIN_URL=http://caddy:2019
|
||||
- TECHNITIUM_API_URL=http://technitium:5380
|
||||
- TECHNITIUM_API_TOKEN=\${TECHNITIUM_API_TOKEN}
|
||||
- JWT_SECRET=\${JWT_SECRET}
|
||||
- DASHCA_ENABLED=true
|
||||
depends_on:
|
||||
- caddy
|
||||
- technitium
|
||||
networks:
|
||||
- dashcaddy-net
|
||||
|
||||
dashcaddy-dashboard:
|
||||
image: samiahmed7777/dashcaddy-dashboard:latest
|
||||
container_name: dashcaddy-dashboard
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "3000:3000" # Dashboard port
|
||||
environment:
|
||||
- NEXT_PUBLIC_API_URL=http://localhost:3001
|
||||
depends_on:
|
||||
- dashcaddy-api
|
||||
networks:
|
||||
- dashcaddy-net
|
||||
|
||||
caddy:
|
||||
image: caddy:2-alpine
|
||||
container_name: caddy
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
- "2019:2019" # Admin API
|
||||
volumes:
|
||||
- ./caddy/Caddyfile:/etc/caddy/Caddyfile
|
||||
- ./caddy/data:/data
|
||||
- ./caddy/config:/config
|
||||
networks:
|
||||
- dashcaddy-net
|
||||
|
||||
technitium:
|
||||
image: technitium/dns-server:latest
|
||||
container_name: technitium
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "5380:5380" # Web console
|
||||
- "53:53/udp"
|
||||
- "53:53/tcp"
|
||||
volumes:
|
||||
- ./technitium:/etc/dns
|
||||
environment:
|
||||
- DNS_SERVER_DOMAIN=local
|
||||
networks:
|
||||
- dashcaddy-net
|
||||
|
||||
networks:
|
||||
dashcaddy-net:
|
||||
driver: bridge`}</code></pre>
|
||||
|
||||
<h2>Environment variables reference</h2>
|
||||
<p>
|
||||
DashCaddy's behavior is controlled through environment variables in the <code>.env</code> file. Here's
|
||||
a complete reference:
|
||||
</p>
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-left text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-surface-700">
|
||||
<th className="py-3 pr-4 font-semibold text-surface-200">Variable</th>
|
||||
<th className="py-3 pr-4 font-semibold text-surface-200">Default</th>
|
||||
<th className="py-3 font-semibold text-surface-200">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="text-surface-300">
|
||||
<tr className="border-b border-surface-800">
|
||||
<td className="py-3 pr-4 font-mono text-xs">NODE_ENV</td>
|
||||
<td className="py-3 pr-4">production</td>
|
||||
<td className="py-3">Runtime environment (development or production)</td>
|
||||
</tr>
|
||||
<tr className="border-b border-surface-800">
|
||||
<td className="py-3 pr-4 font-mono text-xs">DASHCADDY_PORT</td>
|
||||
<td className="py-3 pr-4">3001</td>
|
||||
<td className="py-3">Port for the DashCaddy API server</td>
|
||||
</tr>
|
||||
<tr className="border-b border-surface-800">
|
||||
<td className="py-3 pr-4 font-mono text-xs">CADDY_ADMIN_URL</td>
|
||||
<td className="py-3 pr-4">http://caddy:2019</td>
|
||||
<td className="py-3">Caddy Admin API endpoint</td>
|
||||
</tr>
|
||||
<tr className="border-b border-surface-800">
|
||||
<td className="py-3 pr-4 font-mono text-xs">TECHNITIUM_API_URL</td>
|
||||
<td className="py-3 pr-4">http://technitium:5380</td>
|
||||
<td className="py-3">Technitium DNS API endpoint</td>
|
||||
</tr>
|
||||
<tr className="border-b border-surface-800">
|
||||
<td className="py-3 pr-4 font-mono text-xs">TECHNITIUM_API_TOKEN</td>
|
||||
<td className="py-3 pr-4">(required)</td>
|
||||
<td className="py-3">API token for Technitium DNS authentication</td>
|
||||
</tr>
|
||||
<tr className="border-b border-surface-800">
|
||||
<td className="py-3 pr-4 font-mono text-xs">JWT_SECRET</td>
|
||||
<td className="py-3 pr-4">(auto-generated)</td>
|
||||
<td className="py-3">Secret key for JWT token signing</td>
|
||||
</tr>
|
||||
<tr className="border-b border-surface-800">
|
||||
<td className="py-3 pr-4 font-mono text-xs">DASHCA_ENABLED</td>
|
||||
<td className="py-3 pr-4">true</td>
|
||||
<td className="py-3">Enable internal certificate authority</td>
|
||||
</tr>
|
||||
<tr className="border-b border-surface-800">
|
||||
<td className="py-3 pr-4 font-mono text-xs">BASE_DOMAIN</td>
|
||||
<td className="py-3 pr-4">local</td>
|
||||
<td className="py-3">Base domain for service hostnames</td>
|
||||
</tr>
|
||||
<tr className="border-b border-surface-800">
|
||||
<td className="py-3 pr-4 font-mono text-xs">PROMETHEUS_ENABLED</td>
|
||||
<td className="py-3 pr-4">true</td>
|
||||
<td className="py-3">Expose Prometheus metrics at /metrics</td>
|
||||
</tr>
|
||||
<tr className="border-b border-surface-800">
|
||||
<td className="py-3 pr-4 font-mono text-xs">LOG_LEVEL</td>
|
||||
<td className="py-3 pr-4">info</td>
|
||||
<td className="py-3">Logging verbosity (debug, info, warn, error)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="py-3 pr-4 font-mono text-xs">DATA_DIR</td>
|
||||
<td className="py-3 pr-4">./data</td>
|
||||
<td className="py-3">Path for persistent state storage</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<h2>The Smart Defaults Wizard</h2>
|
||||
<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">Smart Defaults Wizard:</strong> On first launch, the dashboard opens to the Smart Defaults Wizard. It surveys your host and pre-fills sensible choices so you can go from install to a working deployment in minutes. Every default is editable — the wizard simply gives you a known-good starting point instead of a blank slate.
|
||||
</p>
|
||||
</blockquote>
|
||||
|
||||
<p>The wizard performs these tasks automatically:</p>
|
||||
<ul>
|
||||
<li><strong>Caddy detection</strong> — detects the Caddy Admin API endpoint and validates connectivity</li>
|
||||
<li><strong>Technitium detection</strong> — detects Technitium DNS (if installed) and configures the API token and zone</li>
|
||||
<li><strong>Domain suggestion</strong> — proposes a base domain and naming convention for new services</li>
|
||||
<li><strong>DashCA initialization</strong> — initializes the internal certificate authority and offers the root certificate for download</li>
|
||||
<li><strong>Admin account creation</strong> — creates the initial admin account and offers TOTP 2FA enrollment</li>
|
||||
<li><strong>Network scanning</strong> — scans for existing Docker containers and offers to adopt them via Service Discovery</li>
|
||||
<li><strong>A Linux host that stays on</strong> — Ubuntu 22.04 LTS, Debian 12, or any recent Linux distribution. 2 GB RAM and a few GB of disk are enough to start.</li>
|
||||
<li><strong>Root or sudo access</strong> — the installer writes to <code>/etc/dashcaddy</code>, <code>/opt/dockerdata</code>, and the system Docker daemon.</li>
|
||||
<li><strong>A hostname</strong> — a domain you own, a custom-TLD like <code>dashcaddy.home</code>, or just your machine's IP for local-only use.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Manual setup</h2>
|
||||
<h2>Pick your hostname</h2>
|
||||
<p>
|
||||
If you want direct control over paths, services, Caddy, and DNS integration, you can deploy manually
|
||||
instead of using <code>start.sh</code>:
|
||||
DashCaddy uses a single base hostname and creates subdomains under it for every service you add (for example
|
||||
<code> plex.dashcaddy.home</code>, <code>homeassistant.dashcaddy.home</code>). Choose the option that matches
|
||||
your situation:
|
||||
</p>
|
||||
|
||||
<h3>Option A — A real domain you own (recommended)</h3>
|
||||
<p>
|
||||
If you own <code>example.com</code>, register <code>dashcaddy.example.com</code> as a DNS A record pointing at
|
||||
your host's public IP. You can buy a domain for about $10/year from any registrar (Namecheap, Cloudflare,
|
||||
Porkbun, Google Domains). With a real domain, DashCaddy automatically gets a trusted Let's Encrypt
|
||||
certificate on first launch — no certificate warnings, no client setup.
|
||||
</p>
|
||||
|
||||
<h3>Option B — A custom TLD on your home network</h3>
|
||||
<p>
|
||||
Use a domain like <code>dashcaddy.home</code>, <code>dashcaddy.lan</code>, or <code>dashcaddy.local</code>.
|
||||
These will not resolve on the public internet, which is exactly what you want for a home server. Two ways to
|
||||
make them resolve:
|
||||
</p>
|
||||
<ul>
|
||||
<li><strong>Router DNS</strong> — most home routers let you add a local DNS entry. Point <code>*.dashcaddy.home</code> at your server's LAN IP. All devices on your network pick it up automatically.</li>
|
||||
<li><strong>Tailscale MagicDNS</strong> — install Tailscale on the host and your devices. Their MagicDNS gives every machine a stable <code>name.tailnet.ts.net</code> name, no DNS editing needed.</li>
|
||||
</ul>
|
||||
<p>
|
||||
Custom TLDs require installing DashCaddy's internal CA certificate (DashCA) on each device that connects.
|
||||
The setup wizard walks you through this on first run.
|
||||
</p>
|
||||
|
||||
<h3>Option C — A direct IP address</h3>
|
||||
<p>
|
||||
If you don't want to set up DNS at all, you can reach DashCaddy at <code>https://192.168.x.x:port</code>.
|
||||
This works for a single machine on a LAN, but the URL changes when the host gets a new IP and you lose
|
||||
subdomain support. Useful for a quick test, not a permanent setup.
|
||||
</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">No port forwarding?</strong> If your ISP puts you behind CGNAT or you
|
||||
don't want to open ports 80 and 443, DashCaddy works fine behind a Tailscale tunnel or Cloudflare
|
||||
Tunnel. Set up the tunnel first, point your hostname at the tunnel's origin, then run the installer
|
||||
with <code>--no-caddy</code> and let the tunnel handle HTTPS termination.
|
||||
</p>
|
||||
</blockquote>
|
||||
|
||||
<h2>Run the installer</h2>
|
||||
<p>
|
||||
Open a terminal on the host you want to install DashCaddy on and run one of these:
|
||||
</p>
|
||||
|
||||
<h3>Three-command install (most users)</h3>
|
||||
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`# 1. Download and run the installer
|
||||
curl -fsSL https://get.dashcaddy.net | bash
|
||||
|
||||
# 2. Answer the prompts (domain, install location, storage size)
|
||||
# Defaults are sensible — just press Enter to accept.
|
||||
|
||||
# 3. Open the URL the installer prints at the end`}</code></pre>
|
||||
|
||||
<h3>Two-keystroke install (no typing)</h3>
|
||||
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`# Install with a public domain — only thing you type is the domain
|
||||
curl -fsSL https://get.dashcaddy.net | bash -s -- --domain dashcaddy.example.com
|
||||
|
||||
# Or use a custom TLD (your router / Tailscale handles DNS)
|
||||
curl -fsSL https://get.dashcaddy.net | bash -s -- --domain dashcaddy.home
|
||||
|
||||
# Or skip DNS entirely and use a local address (good for a first test)
|
||||
curl -fsSL https://get.dashcaddy.net | bash -s -- quick`}</code></pre>
|
||||
|
||||
<h3>What the installer does</h3>
|
||||
<p>
|
||||
The installer is a single shell script that runs in seven steps, all idempotent — re-running it on an existing
|
||||
install reconciles the stack rather than clobbering your config:
|
||||
</p>
|
||||
<ol>
|
||||
<li>Clone the repository: <code>git clone https://github.com/samiahmed7777/dashcaddy.git</code></li>
|
||||
<li>Install the API dependencies: <code>cd dashcaddy && npm ci</code></li>
|
||||
<li>Prepare Caddy and confirm the Admin API is reachable on port 2019</li>
|
||||
<li>Prepare Technitium DNS if you want automatic DNS changes (optional)</li>
|
||||
<li>Copy <code>.env.example</code> to <code>.env</code> and configure environment variables</li>
|
||||
<li>Start the DashCaddy API: <code>npm run start</code> (or use your process manager like systemd or PM2)</li>
|
||||
<li>Serve the dashboard through Caddy by adding a reverse proxy rule to your Caddyfile</li>
|
||||
<li><strong>Detect your environment</strong> — operating system, package manager, free disk space, available memory, public and LAN IP addresses</li>
|
||||
<li><strong>Install prerequisites</strong> — Docker Engine and the Docker Compose plugin if missing</li>
|
||||
<li><strong>Write configuration</strong> — generates <code>/etc/dashcaddy/</code>, <code>/opt/dockerdata/</code>, and the DashCaddy config file with sensible defaults</li>
|
||||
<li><strong>Pull the DashCaddy container image</strong> — fetches the latest release from the public registry</li>
|
||||
<li><strong>Start the DashCaddy container</strong> — launches <code>dashcaddy-api</code> on port 3001, mounts persistent data volumes</li>
|
||||
<li><strong>Health check</strong> — waits for the API to respond on <code>/healthz</code> before continuing</li>
|
||||
<li><strong>Print the access URL</strong> — shows the dashboard address and any follow-up commands</li>
|
||||
</ol>
|
||||
|
||||
<h2>Common installation issues</h2>
|
||||
<p>If you encounter problems during installation, check this troubleshooting table:</p>
|
||||
<p>
|
||||
If the installer ever hits a problem, it stops at the step that failed and tells you exactly which command to
|
||||
re-run to retry. It does not silently leave a half-installed state behind.
|
||||
</p>
|
||||
|
||||
<h2>The first-run setup wizard</h2>
|
||||
<p>
|
||||
When you open the dashboard URL the installer printed, you land on the setup wizard. It runs once and writes
|
||||
your configuration to disk — every choice is editable later from the settings page.
|
||||
</p>
|
||||
|
||||
<p>The wizard walks through these screens:</p>
|
||||
<ol>
|
||||
<li><strong>Pick a configuration profile</strong> — <em>Professional Home Lab</em> (recommended for most users), <em>Simple</em> (minimal config), or <em>Public</em> (for VPS deployments exposed to the internet)</li>
|
||||
<li><strong>Configure your network</strong> — your domain, the LAN IP that'll be used for service routing, and whether you want to use Tailscale for remote access</li>
|
||||
<li><strong>Set storage limits</strong> — max disk usage for Docker data and how much headroom to keep free</li>
|
||||
<li><strong>Review your choices</strong> — a summary screen with the resolved values so you can sanity-check before saving</li>
|
||||
<li><strong>Disk-safety confirmation</strong> — confirms you understand that bound storage will be created and what happens on uninstall</li>
|
||||
</ol>
|
||||
|
||||
<p>
|
||||
After the wizard saves, you're asked to create the first admin account. Pick a strong password — this
|
||||
account controls every service DashCaddy manages.
|
||||
</p>
|
||||
|
||||
<h2>Set up two-factor authentication</h2>
|
||||
<p>
|
||||
Right after creating your admin account, the dashboard prompts you to enroll a TOTP authenticator
|
||||
(Google Authenticator, Authy, 1Password, or any RFC 6238 app). Scan the QR code with your phone, enter the
|
||||
6-digit code to confirm, and store the recovery codes somewhere safe — they are the only way back into your
|
||||
account if you lose your phone.
|
||||
</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">Don't skip 2FA.</strong> DashCaddy can manage DNS records, start and
|
||||
stop containers, and read credentials for the services it deploys. A leaked password without a second
|
||||
factor is a full compromise. A leaked password with 2FA is just a login screen.
|
||||
</p>
|
||||
</blockquote>
|
||||
|
||||
<h2>Install the DashCA root certificate (custom-TLD installs only)</h2>
|
||||
<p>
|
||||
If you chose Option B (a custom TLD like <code>dashcaddy.home</code>) or Option C (a direct IP), your browser
|
||||
will warn that the certificate is not trusted — because it isn't, to anyone outside your network. DashCaddy
|
||||
ships an internal certificate authority called <strong>DashCA</strong> so internal hostnames get a real TLS
|
||||
certificate instead of self-signed warnings.
|
||||
</p>
|
||||
<p>
|
||||
Visit the <strong>DashCA</strong> page in the dashboard and follow the per-platform instructions:
|
||||
</p>
|
||||
<ul>
|
||||
<li><strong>macOS</strong> — open the downloaded <code>.crt</code>, add it to the System keychain, set it to “Always Trust”</li>
|
||||
<li><strong>Windows</strong> — double-click the <code>.crt</code>, install to “Trusted Root Certification Authorities”</li>
|
||||
<li><strong>Linux</strong> — copy to <code>/usr/local/share/ca-certificates/</code> and run <code>sudo update-ca-certificates</code></li>
|
||||
<li><strong>iOS / Android</strong> — download the profile, install it via Settings, then enable full trust for the CA</li>
|
||||
</ul>
|
||||
<p>
|
||||
You need to install the root certificate on every device that connects to your DashCaddy services, not just
|
||||
the server. There's no way around this for custom-TLD installs — TLS is what keeps your traffic private.
|
||||
</p>
|
||||
|
||||
<h2>Harden the host right after install</h2>
|
||||
<p>
|
||||
DashCaddy protects the services it manages, but it runs on top of a normal Linux box that's still exposed
|
||||
on your network. Spend five minutes on these four steps:
|
||||
</p>
|
||||
|
||||
<h3>1. Enable the firewall</h3>
|
||||
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`# UFW (Ubuntu / Debian)
|
||||
sudo ufw default deny incoming
|
||||
sudo ufw allow ssh # if you SSH in
|
||||
sudo ufw allow 80/tcp # HTTP (Let's Encrypt + redirect)
|
||||
sudo ufw allow 443/tcp # HTTPS
|
||||
sudo ufw enable
|
||||
|
||||
# If you put DashCaddy behind a tunnel (Tailscale / Cloudflare), skip 80/443 above
|
||||
# and only allow the tunnel interface — see your tunnel provider's docs`}</code></pre>
|
||||
|
||||
<h3>2. Disable password SSH login</h3>
|
||||
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`# Edit /etc/ssh/sshd_config and set:
|
||||
PasswordAuthentication no
|
||||
PermitRootLogin prohibit-password # or "no" if you use a sudo user
|
||||
|
||||
sudo systemctl restart sshd`}</code></pre>
|
||||
|
||||
<h3>3. Turn on automatic security updates</h3>
|
||||
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`# Debian / Ubuntu
|
||||
sudo apt install -y unattended-upgrades
|
||||
sudo dpkg-reconfigure -plow unattended-upgrades # answer "Yes"`}</code></pre>
|
||||
|
||||
<h3>4. Set up backups</h3>
|
||||
<p>
|
||||
All your DashCaddy state — services, config, encrypted credentials, DashCA — lives in
|
||||
<code> /opt/dockerdata</code> and <code>/etc/dashcaddy</code>. Snapshot those two directories somewhere off
|
||||
the host. Any tool works: <code>rsync</code> to a NAS, <code>restic</code> to Backblaze B2, a systemd timer
|
||||
that tars them up nightly. The dashboard's Backup button exports a single JSON file you can store
|
||||
anywhere if you want a no-setup option.
|
||||
</p>
|
||||
|
||||
<h2>Verify the install</h2>
|
||||
<p>
|
||||
After the wizard finishes and you've logged in, run through this checklist to confirm everything is wired
|
||||
up correctly:
|
||||
</p>
|
||||
<ol>
|
||||
<li><strong>Container is running</strong> — <code>docker ps</code> should show <code>dashcaddy-api</code> with status <code>Up</code></li>
|
||||
<li><strong>API is healthy</strong> — from the host, <code>curl http://localhost:3001/healthz</code> returns <code>{`{"status":"alive"}`}</code> and <code>/readyz</code> returns 200 with all checks passing</li>
|
||||
<li><strong>Dashboard responds</strong> — opening the URL the installer printed shows the dashboard, not a browser error</li>
|
||||
<li><strong>TLS works</strong> — the address bar shows a padlock with no warnings (a real cert if you used Option A; trusted if you used Option B with DashCA installed)</li>
|
||||
<li><strong>2FA works</strong> — log out and back in with your password + TOTP code</li>
|
||||
<li><strong>Updates work</strong> — the dashboard shows the current version in the footer and the update button is enabled</li>
|
||||
</ol>
|
||||
|
||||
<h2>Troubleshooting a fresh install</h2>
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-left text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-surface-700">
|
||||
<th className="py-3 pr-4 font-semibold text-surface-200">Symptom</th>
|
||||
<th className="py-3 pr-4 font-semibold text-surface-200">Cause</th>
|
||||
<th className="py-3 font-semibold text-surface-200">Solution</th>
|
||||
<th className="py-3 pr-4 font-semibold text-surface-200">Likely cause</th>
|
||||
<th className="py-3 font-semibold text-surface-200">Fix</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="text-surface-300">
|
||||
<tr className="border-b border-surface-800">
|
||||
<td className="py-3 pr-4">Port 80 or 443 already in use</td>
|
||||
<td className="py-3 pr-4">Another web server (nginx, Apache) is running</td>
|
||||
<td className="py-3">Stop the conflicting service or change Caddy's ports in docker-compose.yml</td>
|
||||
<td className="py-3 pr-4">Installer says “Docker not found”</td>
|
||||
<td className="py-3 pr-4">Docker Engine isn't installed or you're not in the docker group</td>
|
||||
<td className="py-3">Run <code>sudo usermod -aG docker $USER</code>, log out, log back in. Re-run the installer.</td>
|
||||
</tr>
|
||||
<tr className="border-b border-surface-800">
|
||||
<td className="py-3 pr-4">Docker permission denied</td>
|
||||
<td className="py-3 pr-4">Current user not in docker group</td>
|
||||
<td className="py-3">Run <code>sudo usermod -aG docker $USER</code> and log out/in</td>
|
||||
<td className="py-3 pr-4">Installer fails on “port 80/443 already in use”</td>
|
||||
<td className="py-3 pr-4">Another web server (nginx, Apache, another Caddy) is bound to those ports</td>
|
||||
<td className="py-3">Stop the conflicting service, or move it to different ports and re-run the installer</td>
|
||||
</tr>
|
||||
<tr className="border-b border-surface-800">
|
||||
<td className="py-3 pr-4">Caddy Admin API unreachable</td>
|
||||
<td className="py-3 pr-4">Caddy not running or Admin API disabled</td>
|
||||
<td className="py-3">Ensure Caddy is running with <code>admin :2019</code> in its config</td>
|
||||
<td className="py-3 pr-4">Dashboard URL gives a connection refused</td>
|
||||
<td className="py-3 pr-4">Container failed to start, or the host firewall is blocking the port</td>
|
||||
<td className="py-3"><code>docker ps</code> — if the container exited, <code>docker logs dashcaddy-api</code> shows why. If running, check the firewall.</td>
|
||||
</tr>
|
||||
<tr className="border-b border-surface-800">
|
||||
<td className="py-3 pr-4">Technitium API token invalid</td>
|
||||
<td className="py-3 pr-4">Token not set or expired</td>
|
||||
<td className="py-3">Generate a new token in Technitium web console and update .env</td>
|
||||
<td className="py-3 pr-4">Browser shows “Your connection is not private”</td>
|
||||
<td className="py-3 pr-4">Custom-TLD install with no DashCA trust, OR the domain doesn't point at this host</td>
|
||||
<td className="py-3">For custom TLDs, install the DashCA root certificate. For real domains, check DNS: <code>dig +short your-hostname</code> should return the host's public IP.</td>
|
||||
</tr>
|
||||
<tr className="border-b border-surface-800">
|
||||
<td className="py-3 pr-4">Dashboard shows 502 Bad Gateway</td>
|
||||
<td className="py-3 pr-4">API server not responding</td>
|
||||
<td className="py-3">Check <code>docker logs dashcaddy-api</code> for errors</td>
|
||||
</tr>
|
||||
<tr className="border-b border-surface-800">
|
||||
<td className="py-3 pr-4">Out of memory during deployment</td>
|
||||
<td className="py-3 pr-4">Insufficient RAM for container workloads</td>
|
||||
<td className="py-3">Increase host RAM or reduce concurrent service deployments</td>
|
||||
<td className="py-3 pr-4">Let's Encrypt certificate never issues</td>
|
||||
<td className="py-3 pr-4">Domain doesn't resolve publicly, OR port 80 is blocked by your ISP</td>
|
||||
<td className="py-3">Verify the A record with <code>dig</code>. If you're on CGNAT, switch to Tailscale Funnel or Cloudflare Tunnel.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="py-3 pr-4">Certificate trust errors in browser</td>
|
||||
<td className="py-3 pr-4">DashCA root cert not installed on client</td>
|
||||
<td className="py-3">Download root cert from DashCA page and install on client device</td>
|
||||
<td className="py-3 pr-4">Setup wizard loops back to step 1</td>
|
||||
<td className="py-3 pr-4">Browser stored a stale config from a previous install</td>
|
||||
<td className="py-3">Open DevTools → Application → Local Storage → clear the <code>dashcaddy-setup</code> key, then reload</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<h2>Post-install verification</h2>
|
||||
<p>After the stack is up, verify each layer with these commands:</p>
|
||||
<h2>Uninstall</h2>
|
||||
<p>
|
||||
If you want to remove DashCaddy cleanly, the installer has an uninstall mode that stops the container,
|
||||
removes the systemd unit, and (optionally) deletes the data volumes:
|
||||
</p>
|
||||
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`# Remove DashCaddy but keep your config and data (you can reinstall later)
|
||||
curl -fsSL https://get.dashcaddy.net | bash -s -- --uninstall
|
||||
|
||||
<h3>1. Check container status</h3>
|
||||
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`docker ps --filter "name=dashcaddy"
|
||||
# Expected: dashcaddy-api, dashcaddy-dashboard, caddy, technitium all running`}</code></pre>
|
||||
|
||||
<h3>2. Verify API health</h3>
|
||||
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`curl http://localhost:3001/healthz
|
||||
# Expected: {"status":"ok","version":"1.0.0"}
|
||||
|
||||
curl http://localhost:3001/readyz
|
||||
# Expected: {"status":"ready","checks":{"caddy":true,"technitium":true}}`}</code></pre>
|
||||
|
||||
<h3>3. Test Caddy Admin API</h3>
|
||||
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`curl http://localhost:2019/config/
|
||||
# Expected: JSON configuration object`}</code></pre>
|
||||
|
||||
<h3>4. Verify Technitium DNS</h3>
|
||||
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`curl http://localhost:5380/api/dns/zones/list?token=YOUR_TOKEN
|
||||
# Expected: List of DNS zones`}</code></pre>
|
||||
|
||||
<h3>5. Check Prometheus metrics</h3>
|
||||
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`curl http://localhost:3001/metrics
|
||||
# Expected: Prometheus-formatted metrics output`}</code></pre>
|
||||
|
||||
<h3>6. Access the dashboard</h3>
|
||||
<p>Open your browser and navigate to <code>http://localhost:3000</code> (or your configured domain). You should see the DashCaddy dashboard login screen.</p>
|
||||
# Remove everything including config and data (irreversible)
|
||||
curl -fsSL https://get.dashcaddy.net | bash -s -- --uninstall --purge`}</code></pre>
|
||||
|
||||
<h2>Next steps</h2>
|
||||
<p>
|
||||
Once install checks pass, head to the <a href="/docs/first-service" className="text-brand-400 hover:text-brand-300 underline">Deploy Your First Service</a> guide to
|
||||
bring your first application online.
|
||||
With DashCaddy installed and hardened, head to the <a href="/docs/first-service" className="text-brand-400 hover:text-brand-300 underline">Deploy Your First Service</a> guide
|
||||
to bring an application online through the App Selector.
|
||||
</p>
|
||||
<p>
|
||||
If you need to understand the architecture in more depth, see the <a href="/docs/overview" className="text-brand-400 hover:text-brand-300 underline">Product Overview</a>.
|
||||
If you want to understand the architecture before adding services, see the <a href="/docs/overview" className="text-brand-400 hover:text-brand-300 underline">Product Overview</a>.
|
||||
For problems during or after install, the <a href="/docs/troubleshooting" className="text-brand-400 hover:text-brand-300 underline">Troubleshooting</a> page
|
||||
walks each layer bottom-up.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
|
||||
@@ -27,7 +27,7 @@ export default function DocsIntegrationsPage() {
|
||||
Docker (and Docker Compose) is the runtime foundation for every deployment workflow, container lifecycle action,
|
||||
service discovery sweep, and template-based launch. DashCaddy communicates with the Docker daemon over the
|
||||
Unix socket (<code>/var/run/docker.sock</code>) to start, stop, restart, inspect, and adopt containers, and to
|
||||
deploy the <strong>92+ one-click application templates</strong> from the catalog.
|
||||
deploy the <strong>77 one-click application templates</strong> from the catalog.
|
||||
</p>
|
||||
<p>
|
||||
The daemon connection is established at startup. If the socket is missing or permissioned for a different user,
|
||||
|
||||
@@ -162,7 +162,7 @@ export default function DocsOverviewPage() {
|
||||
<tr className="border-b border-surface-800">
|
||||
<td className="py-3 pr-4">Docker deployment</td>
|
||||
<td className="py-3 pr-4">Manual compose files</td>
|
||||
<td className="py-3 pr-4">92+ one-click templates</td>
|
||||
<td className="py-3 pr-4">77 one-click templates</td>
|
||||
<td className="py-3">Templates + Recipes</td>
|
||||
</tr>
|
||||
<tr className="border-b border-surface-800">
|
||||
@@ -217,7 +217,7 @@ export default function DocsOverviewPage() {
|
||||
<td className="py-3 pr-4">Pricing</td>
|
||||
<td className="py-3 pr-4">Time + complexity</td>
|
||||
<td className="py-3 pr-4">Free forever</td>
|
||||
<td className="py-3">$20–$99 one-time</td>
|
||||
<td className="py-3">$20–$99 / license duration</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -250,8 +250,8 @@ export default function DocsOverviewPage() {
|
||||
|
||||
<h2>Premium licensing</h2>
|
||||
<p>
|
||||
DashCaddy Premium is a one-time purchase (not a subscription) that unlocks advanced orchestration
|
||||
features. Pricing tiers:
|
||||
DashCaddy Premium is a fixed-duration license. You buy the duration you want; Premium features unlock
|
||||
for that duration and deactivate when it expires. You purchase a new license to renew. Pricing tiers:
|
||||
</p>
|
||||
<ul>
|
||||
<li><strong>$20</strong> — 30-day license</li>
|
||||
|
||||
@@ -14,7 +14,7 @@ export default function DocsPremiumPage() {
|
||||
DashCaddy's philosophy is that the day-to-day platform should be free forever. Everything you need to run
|
||||
a single host — the dashboard, the full template catalog, Caddy + DNS + TLS automation, real-time monitoring,
|
||||
Prometheus metrics, multi-user accounts with 2FA and RBAC, the Security Center, the AI Intent Router, the MCP
|
||||
Server, the JS SDK, and backup/restore — works without a license. Premium adds four capabilities aimed at
|
||||
Server, the REST API, 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>
|
||||
@@ -39,7 +39,7 @@ export default function DocsPremiumPage() {
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td>Dashboard & web UI</td><td>✓</td><td>✓</td></tr>
|
||||
<tr><td>92+ application templates</td><td>✓</td><td>✓</td></tr>
|
||||
<tr><td>77 application templates</td><td>✓</td><td>✓</td></tr>
|
||||
<tr><td>Caddy reverse proxy + auto HTTPS</td><td>✓</td><td>✓</td></tr>
|
||||
<tr><td>Caddyfile-as-Code builder</td><td>✓</td><td>✓</td></tr>
|
||||
<tr><td>Technitium DNS automation</td><td>✓</td><td>✓</td></tr>
|
||||
@@ -52,9 +52,9 @@ export default function DocsPremiumPage() {
|
||||
<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>REST API & MCP Server</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>Internationalization (31 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>
|
||||
@@ -161,8 +161,10 @@ curl -X POST -H "Authorization: Bearer ***" \\
|
||||
|
||||
<h2>Pricing</h2>
|
||||
<p>
|
||||
Premium is sold as one-time payments for fixed license durations. There is a single Premium tier — no ladder
|
||||
of plans to navigate. Longer durations are discounted relative to the monthly rate.
|
||||
Premium is sold as a fixed-duration license. You pick the duration that fits your needs; the license unlocks
|
||||
Premium features for that period and then expires. You manually purchase a new license when the current
|
||||
one expires. There is a single Premium tier — no ladder of plans to navigate. Longer durations are
|
||||
discounted relative to the short-term rate.
|
||||
</p>
|
||||
<table>
|
||||
<thead>
|
||||
@@ -186,7 +188,7 @@ curl -X POST -H "Authorization: Bearer ***" \\
|
||||
|
||||
<h2>Plan terms</h2>
|
||||
<ul>
|
||||
<li><strong>One-time payments</strong> — no auto-renewing subscription; your license runs for the purchased duration and then expires.</li>
|
||||
<li><strong>Fixed-duration license</strong> — your license runs for the purchased duration and then expires. You re-purchase manually when you want to renew.</li>
|
||||
<li><strong>One Premium tier</strong> — every duration unlocks the same features.</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 — services keep running while you renew; Premium features are read-only during grace.</li>
|
||||
|
||||
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 98 KiB |
@@ -1,457 +1,458 @@
|
||||
'use client';
|
||||
|
||||
import Link from 'next/link';
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import FeatureCard from '@/components/FeatureCard';
|
||||
|
||||
interface FeatureItem {
|
||||
icon: string;
|
||||
label: string;
|
||||
premium?: boolean;
|
||||
}
|
||||
|
||||
interface FeatureSection {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string;
|
||||
icon: string;
|
||||
badge?: string;
|
||||
features: FeatureItem[];
|
||||
}
|
||||
|
||||
export default function FeaturesPage() {
|
||||
const features: FeatureSection[] = [
|
||||
{
|
||||
id: 'ai-self-hosting',
|
||||
title: 'AI-Powered Self-Hosting',
|
||||
description:
|
||||
'Talk to your homelab. DashCaddy\'s AI Intent Router understands natural language commands, and the built-in MCP Server lets AI assistants like Claude and GPT control your infrastructure directly. The first self-hosting platform built for the agentic era.',
|
||||
icon: '🤖',
|
||||
badge: 'New',
|
||||
features: [
|
||||
{ icon: '🎯', label: 'AI Intent Router — natural language commands' },
|
||||
{ icon: '🔌', label: 'MCP Server for Claude & GPT control' },
|
||||
{ icon: '💬', label: '"deploy Plex", "check SSL status", "restart jellyfin"' },
|
||||
{ icon: '🧠', label: 'Context-aware infrastructure reasoning' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'app-deployment',
|
||||
title: 'App Deployment',
|
||||
description:
|
||||
'Deploy from a library of 92+ pre-configured Docker templates with one click. Automatic configuration, smart defaults, and service discovery that auto-detects running containers and lets you adopt them instantly. From zero to live in seconds.',
|
||||
icon: '🚀',
|
||||
features: [
|
||||
{ icon: '⚡', label: 'One-click deployment' },
|
||||
{ icon: '📦', label: '92+ app templates' },
|
||||
{ icon: '⚙️', label: 'Automatic configuration' },
|
||||
{ icon: '🔍', label: 'Service Discovery & auto-adopt' },
|
||||
{ icon: '🧙', label: 'Smart Defaults Wizard' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'ssl-security',
|
||||
title: 'SSL & Security',
|
||||
description:
|
||||
'Enterprise-grade security, baked in. Automatic SSL via Caddy\'s internal CA, TOTP 2FA with email magic link, multi-user admin with role-based access, encrypted credential vault, comprehensive audit logs, and a Security Center that unifies events from every source.',
|
||||
icon: '🔒',
|
||||
features: [
|
||||
{ icon: '🔐', label: 'Automatic SSL certificates' },
|
||||
{ icon: '📱', label: 'TOTP 2FA + email magic link' },
|
||||
{ icon: '👥', label: 'Multi-user admin (RBAC + invites)' },
|
||||
{ icon: '🔑', label: 'Encrypted credential vault' },
|
||||
{ icon: '📝', label: 'Audit logging' },
|
||||
{ icon: '🛡️', label: 'Security Center (multi-source events)' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'dns-networking',
|
||||
title: 'DNS & Networking',
|
||||
description:
|
||||
'Full DNS automation powered by Technitium DNS, plus a Caddyfile-as-code visual builder for crafting reverse proxy rules without touching config files. Manage subdomains, routes, and proxies from a single dashboard.',
|
||||
icon: '🌐',
|
||||
features: [
|
||||
{ icon: '✨', label: 'Automatic DNS records' },
|
||||
{ icon: '🔗', label: 'Technitium DNS integration' },
|
||||
{ icon: '🏗️', label: 'Caddyfile-as-code visual builder' },
|
||||
{ icon: '🔁', label: 'Reverse proxy management' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'monitoring-ops',
|
||||
title: 'Monitoring & Operations',
|
||||
description:
|
||||
'Real-time visibility into every container and service. WebSocket live updates keep your dashboard in sync instantly, health checks catch problems early, and Prometheus metrics export feeds your existing observability stack.',
|
||||
icon: '📊',
|
||||
features: [
|
||||
{ icon: '🟢', label: 'Real-time health monitoring' },
|
||||
{ icon: '⚡', label: 'WebSocket live updates' },
|
||||
{ icon: '📈', label: 'Prometheus metrics export' },
|
||||
{ icon: '💓', label: 'Container health checks' },
|
||||
{ icon: '⏱️', label: 'Response time & resource tracking' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'backup-recovery',
|
||||
title: 'Backup & Recovery',
|
||||
description:
|
||||
'One-click backup and disaster recovery built for peace of mind. Full-system backups with checksum verification, point-in-time restore, and a disaster recovery workflow that gets you back online fast when it matters most.',
|
||||
icon: '💾',
|
||||
features: [
|
||||
{ icon: '🔙', label: 'One-click backup & restore' },
|
||||
{ icon: '🆘', label: 'Disaster Recovery mode' },
|
||||
{ icon: '✅', label: 'Full-system backup w/ checksums' },
|
||||
{ icon: '⏮️', label: 'Point-in-time restore' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'developer-platform',
|
||||
title: 'Developer Platform',
|
||||
description:
|
||||
'DashCaddy isn\'t just a UI—it\'s a platform. A typed JavaScript SDK with 39 methods, a full REST API, a plugin/extension system for custom workflows, and internationalization across 5 languages including full RTL support.',
|
||||
icon: '🛠️',
|
||||
features: [
|
||||
{ icon: '📘', label: 'JavaScript SDK + TypeScript types (39 methods)' },
|
||||
{ icon: '🔌', label: 'Plugin / Extension system' },
|
||||
{ icon: '🌐', label: 'REST API' },
|
||||
{ icon: '🌍', label: 'i18n: EN/ES/FR/DE/AR (RTL)' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'premium',
|
||||
title: 'Premium Features',
|
||||
description:
|
||||
'Power features for production and multi-host deployments. Auto-Login SSO across your apps, multi-container Recipes, Docker Swarm orchestration, and Fleet Management to register remote instances with parallel health probes and multi-host deploy plans.',
|
||||
icon: '⭐',
|
||||
features: [
|
||||
{ icon: '🔑', label: 'Auto-Login SSO', premium: true },
|
||||
{ icon: '📚', label: 'Recipes (multi-container stacks)', premium: true },
|
||||
{ icon: '🐳', label: 'Docker Swarm orchestration', premium: true },
|
||||
{ icon: '📡', label: 'Multi-Host Fleet Management', premium: true },
|
||||
{ icon: '🚀', label: 'Priority support', premium: true },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="flex flex-col min-h-screen bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
|
||||
{/* Hero Section */}
|
||||
<section className="relative py-16 sm:py-20 lg:py-24">
|
||||
<div className="absolute inset-0 -z-10">
|
||||
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-96 h-96 bg-brand-500/20 rounded-full blur-3xl opacity-30 animate-pulse" />
|
||||
</div>
|
||||
|
||||
<div className="mx-auto max-w-4xl px-4 sm:px-6 lg:px-8 text-center">
|
||||
<div className="mb-6 inline-flex items-center gap-2 rounded-full border border-brand-500/30 bg-brand-500/10 px-4 py-1.5 text-sm font-medium text-brand-300">
|
||||
<span className="relative flex h-2 w-2">
|
||||
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-brand-400 opacity-75" />
|
||||
<span className="relative inline-flex h-2 w-2 rounded-full bg-brand-400" />
|
||||
</span>
|
||||
AI-native self-hosting platform
|
||||
</div>
|
||||
<h1 className="text-4xl sm:text-5xl lg:text-6xl font-bold mb-6">
|
||||
Every Tool You Need to <span className="text-brand-400">Self-Host</span>
|
||||
</h1>
|
||||
<p className="text-xl text-surface-300 max-w-2xl mx-auto">
|
||||
From AI-powered deployment to enterprise security, real-time monitoring, and multi-host fleet management — DashCaddy brings the entire self-hosting stack under one roof.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Feature Sections */}
|
||||
<section className="relative py-16 sm:py-20 lg:py-24">
|
||||
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
|
||||
<div className="space-y-24">
|
||||
{features.map((feature, idx) => (
|
||||
<div
|
||||
key={feature.id}
|
||||
className={`grid grid-cols-1 lg:grid-cols-2 gap-12 items-center ${idx % 2 === 1 ? 'lg:flex-row-reverse' : ''}`}
|
||||
>
|
||||
{/* Content Side */}
|
||||
<div className={idx % 2 === 1 ? 'lg:order-2' : ''}>
|
||||
<div className="mb-6 inline-flex rounded-lg bg-brand-500/10 p-4 text-brand-400">
|
||||
<div className="text-3xl">{feature.icon}</div>
|
||||
</div>
|
||||
|
||||
{feature.badge && (
|
||||
<span className="mb-3 ml-2 inline-block align-middle text-xs px-2 py-1 bg-brand-500/20 text-brand-300 rounded-full font-semibold uppercase tracking-wide">
|
||||
{feature.badge}
|
||||
</span>
|
||||
)}
|
||||
|
||||
<h2 className="text-3xl sm:text-4xl font-bold mb-4 text-surface-50">
|
||||
{feature.title}
|
||||
</h2>
|
||||
<p className="text-lg text-surface-300 mb-8 leading-relaxed">
|
||||
{feature.description}
|
||||
</p>
|
||||
|
||||
{/* Feature Grid */}
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
{feature.features.map((item, itemIdx) => (
|
||||
<div
|
||||
key={itemIdx}
|
||||
className={`flex items-center gap-3 p-3 rounded-lg border transition-all ${
|
||||
item.premium
|
||||
? 'border-brand-500/30 bg-brand-500/5 hover:bg-brand-500/10'
|
||||
: 'border-surface-700/30 bg-surface-800/30 hover:border-surface-700/50'
|
||||
}`}
|
||||
>
|
||||
<span className="text-xl flex-shrink-0">{item.icon}</span>
|
||||
<span
|
||||
className={`text-sm font-medium ${item.premium ? 'text-brand-300' : 'text-surface-300'}`}
|
||||
>
|
||||
{item.label}
|
||||
{item.premium && (
|
||||
<span className="ml-1 inline-block text-xs px-2 py-0.5 bg-brand-500/20 text-brand-300 rounded font-semibold">
|
||||
Premium
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Visual Side */}
|
||||
<div className={idx % 2 === 1 ? 'lg:order-1' : ''}>
|
||||
<div className="relative">
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-brand-500/10 to-brand-600/5 rounded-2xl blur-xl" />
|
||||
<div className="relative rounded-2xl border border-surface-700/50 bg-surface-800/50 backdrop-blur p-8">
|
||||
<div className="space-y-4">
|
||||
{feature.features.slice(0, 3).map((item, itemIdx) => (
|
||||
<div
|
||||
key={itemIdx}
|
||||
className="flex items-center gap-3 p-3 bg-surface-900/50 rounded-lg border border-surface-700/30"
|
||||
>
|
||||
<div className="w-2 h-2 rounded-full bg-brand-400" />
|
||||
<span className="text-sm text-surface-300">{item.label}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="mt-6 pt-6 border-t border-surface-700/30">
|
||||
<div className="text-sm text-surface-500 text-center">
|
||||
<span className="text-brand-400 font-semibold">
|
||||
{feature.features.length} features
|
||||
</span>{' '}
|
||||
included
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* AI Features Highlight */}
|
||||
<section className="relative py-16 sm:py-20 lg:py-24 bg-gradient-to-b from-surface-900 to-surface-950">
|
||||
<div className="absolute inset-0 -z-10">
|
||||
<div className="absolute top-0 left-1/2 -translate-x-1/2 w-[40rem] h-96 bg-brand-500/10 rounded-full blur-3xl opacity-40" />
|
||||
</div>
|
||||
|
||||
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
|
||||
<div className="mb-16 text-center">
|
||||
<span className="rounded-full bg-brand-500/20 px-4 py-2 text-sm font-semibold text-brand-300">
|
||||
The Agentic Era of Self-Hosting
|
||||
</span>
|
||||
<h2 className="mt-6 text-3xl sm:text-4xl lg:text-5xl font-bold mb-4">
|
||||
Control Your Homelab With <span className="text-brand-400">Natural Language</span>
|
||||
</h2>
|
||||
<p className="text-lg text-surface-400 max-w-2xl mx-auto">
|
||||
DashCaddy speaks AI natively. Type what you want, or let your favorite assistant do it.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{/* AI Intent Router */}
|
||||
<div className="group relative overflow-hidden rounded-2xl border border-surface-700/50 bg-surface-800/50 backdrop-blur p-8 transition-all duration-300 hover:border-brand-500/50 hover:shadow-lg hover:shadow-brand-500/10">
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-brand-500/0 to-brand-500/0 group-hover:from-brand-500/5 group-hover:to-brand-500/10 transition-all duration-300 pointer-events-none" />
|
||||
<div className="relative z-10">
|
||||
<div className="mb-4 inline-flex rounded-lg bg-brand-500/10 p-3 text-brand-400 group-hover:bg-brand-500/20 group-hover:text-brand-300 transition-all duration-300">
|
||||
<div className="text-3xl">🎯</div>
|
||||
</div>
|
||||
<h3 className="mb-2 text-xl font-semibold text-surface-50">AI Intent Router</h3>
|
||||
<p className="text-surface-300 text-sm leading-relaxed mb-6">
|
||||
Skip the menus. Just type a command in plain English and DashCaddy figures out the right actions.
|
||||
</p>
|
||||
<div className="space-y-2">
|
||||
{['"deploy Plex"', '"check SSL status"', '"restart jellyfin"', '"show failing containers"'].map(
|
||||
(cmd, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="flex items-center gap-3 rounded-lg bg-surface-900/60 border border-surface-700/30 px-3 py-2"
|
||||
>
|
||||
<span className="text-brand-400 font-mono text-xs select-none">›</span>
|
||||
<span className="text-sm text-surface-300 font-mono">{cmd}</span>
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* MCP Server */}
|
||||
<div className="group relative overflow-hidden rounded-2xl border border-surface-700/50 bg-surface-800/50 backdrop-blur p-8 transition-all duration-300 hover:border-brand-500/50 hover:shadow-lg hover:shadow-brand-500/10">
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-brand-500/0 to-brand-500/0 group-hover:from-brand-500/5 group-hover:to-brand-500/10 transition-all duration-300 pointer-events-none" />
|
||||
<div className="relative z-10">
|
||||
<div className="mb-4 inline-flex rounded-lg bg-brand-500/10 p-3 text-brand-400 group-hover:bg-brand-500/20 group-hover:text-brand-300 transition-all duration-300">
|
||||
<div className="text-3xl">🔌</div>
|
||||
</div>
|
||||
<h3 className="mb-2 text-xl font-semibold text-surface-50">MCP Server</h3>
|
||||
<p className="text-surface-300 text-sm leading-relaxed mb-6">
|
||||
DashCaddy runs as a Model Context Protocol server, so AI assistants can manage your homelab directly.
|
||||
</p>
|
||||
<div className="space-y-2">
|
||||
{[
|
||||
'Claude desktop & API',
|
||||
'GPT / ChatGPT integrations',
|
||||
'Any MCP-compatible client',
|
||||
'Scoped, audited, secure access',
|
||||
].map((item, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="flex items-center gap-3 rounded-lg bg-surface-900/60 border border-surface-700/30 px-3 py-2"
|
||||
>
|
||||
<div className="w-2 h-2 rounded-full bg-brand-400" />
|
||||
<span className="text-sm text-surface-300">{item}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Free vs Premium Comparison */}
|
||||
<section className="relative py-16 sm:py-20 lg:py-24 bg-surface-950">
|
||||
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
|
||||
<div className="mb-16 text-center">
|
||||
<h2 className="text-3xl sm:text-4xl lg:text-5xl font-bold mb-4">
|
||||
Free vs <span className="text-brand-400">Premium</span>
|
||||
</h2>
|
||||
<p className="text-lg text-surface-400">
|
||||
A powerful free tier for everyone. Premium unlocks multi-host, SSO, and more.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||
{/* Free Column */}
|
||||
<div className="rounded-2xl border border-surface-700/50 bg-surface-800/40 backdrop-blur p-8">
|
||||
<div className="mb-6">
|
||||
<h3 className="text-2xl font-bold text-surface-50">Free</h3>
|
||||
<p className="text-sm text-surface-400 mt-1">Everything to self-host a single node.</p>
|
||||
</div>
|
||||
<ul className="space-y-3">
|
||||
{[
|
||||
'92+ one-click app templates',
|
||||
'Automatic SSL certificates',
|
||||
'Technitium DNS automation',
|
||||
'Reverse proxy + Caddyfile builder',
|
||||
'Real-time monitoring & WebSocket updates',
|
||||
'TOTP 2FA + multi-user admin',
|
||||
'Encrypted credential vault',
|
||||
'Audit logging',
|
||||
'Security Center',
|
||||
'One-click backup & restore',
|
||||
'AI Intent Router + MCP Server',
|
||||
'JavaScript SDK & REST API',
|
||||
'Plugin / Extension system',
|
||||
'i18n (5 languages, RTL)',
|
||||
'Prometheus metrics export',
|
||||
].map((item, i) => (
|
||||
<li key={i} className="flex items-start gap-3">
|
||||
<svg className="mt-0.5 h-5 w-5 flex-shrink-0 text-brand-400" fill="none" viewBox="0 0 24 24" strokeWidth={2} stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M4.5 12.75l6 6 9-13.5" />
|
||||
</svg>
|
||||
<span className="text-sm text-surface-300">{item}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Premium Column */}
|
||||
<div className="relative rounded-2xl border border-brand-500/40 bg-gradient-to-br from-brand-950/40 to-surface-900 p-8">
|
||||
<div className="absolute -top-3 right-6">
|
||||
<span className="rounded-full bg-brand-500 px-3 py-1 text-xs font-semibold text-white uppercase tracking-wide">
|
||||
Premium
|
||||
</span>
|
||||
</div>
|
||||
<div className="mb-6">
|
||||
<h3 className="text-2xl font-bold text-surface-50">Premium</h3>
|
||||
<p className="text-sm text-surface-400 mt-1">For production, multi-host, and teams.</p>
|
||||
</div>
|
||||
<p className="text-sm text-brand-300 mb-4 font-medium">
|
||||
Everything in Free, plus:
|
||||
</p>
|
||||
<ul className="space-y-3">
|
||||
{[
|
||||
'Auto-Login SSO for deployed apps',
|
||||
'Recipes — multi-container stack deployment',
|
||||
'Docker Swarm orchestration',
|
||||
'Multi-Host Fleet Management (remote instances, parallel health probes, multi-host deploy plans)',
|
||||
'Disaster Recovery (full-system backup w/ checksums)',
|
||||
'Priority support',
|
||||
].map((item, i) => (
|
||||
<li key={i} className="flex items-start gap-3">
|
||||
<svg className="mt-0.5 h-5 w-5 flex-shrink-0 text-brand-400" fill="none" viewBox="0 0 24 24" strokeWidth={2} stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
<span className="text-sm text-surface-200">{item}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
<Link
|
||||
href="/pricing"
|
||||
className="mt-8 inline-flex items-center gap-2 rounded-lg bg-brand-500 px-6 py-3 font-semibold text-white hover:bg-brand-600 transition-all duration-200 hover:shadow-lg hover:shadow-brand-500/30"
|
||||
>
|
||||
Explore Premium Plans
|
||||
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" strokeWidth={2} stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M13.5 6H5.25A2.25 2.25 0 003 8.25v10.5A2.25 2.25 0 005.25 21h10.5A2.25 2.25 0 0018 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25" />
|
||||
</svg>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* FAQ CTA Section */}
|
||||
<section className="relative py-16 sm:py-20 lg:py-24 bg-gradient-to-b from-surface-950 to-surface-900">
|
||||
<div className="mx-auto max-w-3xl px-4 sm:px-6 lg:px-8">
|
||||
<div className="mb-12 text-center">
|
||||
<h2 className="text-3xl sm:text-4xl font-bold mb-4">
|
||||
Questions About <span className="text-brand-400">Features?</span>
|
||||
</h2>
|
||||
<p className="text-lg text-surface-400">
|
||||
Check our documentation or contact support.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col sm:flex-row gap-4 justify-center">
|
||||
<Link
|
||||
href="/docs"
|
||||
className="inline-flex items-center justify-center gap-2 rounded-lg bg-brand-500 px-8 py-3 font-semibold text-white hover:bg-brand-600 transition-all"
|
||||
>
|
||||
Read Documentation
|
||||
</Link>
|
||||
<a
|
||||
href="mailto:support@dashcaddy.net"
|
||||
className="inline-flex items-center justify-center gap-2 rounded-lg border border-surface-700 bg-surface-800/50 px-8 py-3 font-semibold text-surface-50 hover:border-brand-400 transition-colors"
|
||||
>
|
||||
Contact Support
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
'use client';
|
||||
|
||||
import Link from 'next/link';
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import FeatureCard from '@/components/FeatureCard';
|
||||
|
||||
interface FeatureItem {
|
||||
icon: string;
|
||||
label: string;
|
||||
premium?: boolean;
|
||||
}
|
||||
|
||||
interface FeatureSection {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string;
|
||||
icon: string;
|
||||
badge?: string;
|
||||
features: FeatureItem[];
|
||||
}
|
||||
|
||||
export default function FeaturesPage() {
|
||||
const features: FeatureSection[] = [
|
||||
{
|
||||
id: 'ai-self-hosting',
|
||||
title: 'AI-Powered Self-Hosting',
|
||||
description:
|
||||
'Talk to your homelab. DashCaddy\'s AI Intent Router understands natural language commands, and the built-in MCP Server lets AI assistants like Claude and GPT control your infrastructure directly. The first self-hosting platform built for the agentic era.',
|
||||
icon: '🤖',
|
||||
badge: 'New',
|
||||
features: [
|
||||
{ icon: '🎯', label: 'AI Intent Router — natural language commands' },
|
||||
{ icon: '🔌', label: 'MCP Server for Claude & GPT control' },
|
||||
{ icon: '💬', label: '"deploy Plex", "check SSL status", "restart jellyfin"' },
|
||||
{ icon: '🧠', label: 'Context-aware infrastructure reasoning' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'app-deployment',
|
||||
title: 'App Deployment',
|
||||
description:
|
||||
'Deploy from a library of 77 pre-configured Docker templates with one click. Automatic configuration, smart defaults, and service discovery that auto-detects running containers and lets you adopt them instantly. From zero to live in seconds.',
|
||||
icon: '🚀',
|
||||
features: [
|
||||
{ icon: '⚡', label: 'One-click deployment' },
|
||||
{ icon: '📦', label: '77 app templates' },
|
||||
{ icon: '⚙️', label: 'Automatic configuration' },
|
||||
{ icon: '🔍', label: 'Service Discovery & auto-adopt' },
|
||||
{ icon: '🧙', label: 'Smart Defaults Wizard' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'ssl-security',
|
||||
title: 'SSL & Security',
|
||||
description:
|
||||
'Enterprise-grade security, baked in. Automatic SSL via Caddy\'s internal CA, TOTP 2FA with email magic link, multi-user admin with role-based access, encrypted credential vault, PII-masked audit logs, and a Security Center that unifies events from every source — with per-IP perimeter analytics for spotting attack sources.',
|
||||
icon: '🔒',
|
||||
features: [
|
||||
{ icon: '🔐', label: 'Automatic SSL certificates' },
|
||||
{ icon: '📱', label: 'TOTP 2FA + email magic link' },
|
||||
{ icon: '👥', label: 'Multi-user admin (RBAC + invites)' },
|
||||
{ icon: '🔑', label: 'Encrypted credential vault' },
|
||||
{ icon: '📝', label: 'PII-masked audit logging' },
|
||||
{ icon: '🛡️', label: 'Security Center (multi-source events)' },
|
||||
{ icon: '🌐', label: 'Perimeter analytics (per-IP, per-vhost)' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'dns-networking',
|
||||
title: 'DNS & Networking',
|
||||
description:
|
||||
'Full DNS automation powered by Technitium DNS, plus a Caddyfile-as-code visual builder for crafting reverse proxy rules without touching config files. Manage subdomains, routes, and proxies from a single dashboard.',
|
||||
icon: '🌐',
|
||||
features: [
|
||||
{ icon: '✨', label: 'Automatic DNS records' },
|
||||
{ icon: '🔗', label: 'Technitium DNS integration' },
|
||||
{ icon: '🏗️', label: 'Caddyfile-as-code visual builder' },
|
||||
{ icon: '🔁', label: 'Reverse proxy management' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'monitoring-ops',
|
||||
title: 'Monitoring & Operations',
|
||||
description:
|
||||
'Real-time visibility into every container and service. WebSocket live updates keep your dashboard in sync instantly, health checks catch problems early, and Prometheus metrics export feeds your existing observability stack.',
|
||||
icon: '📊',
|
||||
features: [
|
||||
{ icon: '🟢', label: 'Real-time health monitoring' },
|
||||
{ icon: '⚡', label: 'WebSocket live updates' },
|
||||
{ icon: '📈', label: 'Prometheus metrics export' },
|
||||
{ icon: '💓', label: 'Container health checks' },
|
||||
{ icon: '⏱️', label: 'Response time & resource tracking' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'backup-recovery',
|
||||
title: 'Backup & Recovery',
|
||||
description:
|
||||
'One-click backup and disaster recovery built for peace of mind. Full-system backups with checksum verification, point-in-time restore, and a disaster recovery workflow that gets you back online fast when it matters most.',
|
||||
icon: '💾',
|
||||
features: [
|
||||
{ icon: '🔙', label: 'One-click backup & restore' },
|
||||
{ icon: '🆘', label: 'Disaster Recovery mode' },
|
||||
{ icon: '✅', label: 'Full-system backup w/ checksums' },
|
||||
{ icon: '⏮️', label: 'Point-in-time restore' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'developer-platform',
|
||||
title: 'Developer Platform',
|
||||
description:
|
||||
'DashCaddy isn\'t just a UI—it\'s a platform. A full REST API with JSON endpoints, a plugin/extension system for custom workflows, and internationalization across 31 languages including full RTL support.',
|
||||
icon: '🛠️',
|
||||
features: [
|
||||
{ icon: '🌐', label: 'REST API (JSON, MCP protocol)' },
|
||||
{ icon: '🔌', label: 'Plugin / Extension system' },
|
||||
{ icon: '🔄', label: 'Auto-update manager' },
|
||||
{ icon: '🌍', label: 'i18n: EN/ES/FR/DE/AR (RTL)' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'premium',
|
||||
title: 'Premium Features',
|
||||
description:
|
||||
'Power features for production and multi-host deployments. Auto-Login SSO across your apps, multi-container Recipes, Docker Swarm orchestration, and Fleet Management to register remote instances with parallel health probes and multi-host deploy plans.',
|
||||
icon: '⭐',
|
||||
features: [
|
||||
{ icon: '🔑', label: 'Auto-Login SSO', premium: true },
|
||||
{ icon: '📚', label: 'Recipes (multi-container stacks)', premium: true },
|
||||
{ icon: '🐳', label: 'Docker Swarm orchestration', premium: true },
|
||||
{ icon: '📡', label: 'Multi-Host Fleet Management', premium: true },
|
||||
{ icon: '🚀', label: 'Priority support', premium: true },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="flex flex-col min-h-screen bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
|
||||
{/* Hero Section */}
|
||||
<section className="relative py-16 sm:py-20 lg:py-24">
|
||||
<div className="absolute inset-0 -z-10">
|
||||
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-96 h-96 bg-brand-500/20 rounded-full blur-3xl opacity-30 animate-pulse" />
|
||||
</div>
|
||||
|
||||
<div className="mx-auto max-w-4xl px-4 sm:px-6 lg:px-8 text-center">
|
||||
<div className="mb-6 inline-flex items-center gap-2 rounded-full border border-brand-500/30 bg-brand-500/10 px-4 py-1.5 text-sm font-medium text-brand-300">
|
||||
<span className="relative flex h-2 w-2">
|
||||
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-brand-400 opacity-75" />
|
||||
<span className="relative inline-flex h-2 w-2 rounded-full bg-brand-400" />
|
||||
</span>
|
||||
AI-native self-hosting platform
|
||||
</div>
|
||||
<h1 className="text-4xl sm:text-5xl lg:text-6xl font-bold mb-6">
|
||||
Every Tool You Need to <span className="text-brand-400">Self-Host</span>
|
||||
</h1>
|
||||
<p className="text-xl text-surface-300 max-w-2xl mx-auto">
|
||||
From AI-powered deployment to enterprise security, real-time monitoring, and multi-host fleet management — DashCaddy brings the entire self-hosting stack under one roof.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Feature Sections */}
|
||||
<section className="relative py-16 sm:py-20 lg:py-24">
|
||||
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
|
||||
<div className="space-y-24">
|
||||
{features.map((feature, idx) => (
|
||||
<div
|
||||
key={feature.id}
|
||||
className={`grid grid-cols-1 lg:grid-cols-2 gap-12 items-center ${idx % 2 === 1 ? 'lg:flex-row-reverse' : ''}`}
|
||||
>
|
||||
{/* Content Side */}
|
||||
<div className={idx % 2 === 1 ? 'lg:order-2' : ''}>
|
||||
<div className="mb-6 inline-flex rounded-lg bg-brand-500/10 p-4 text-brand-400">
|
||||
<div className="text-3xl">{feature.icon}</div>
|
||||
</div>
|
||||
|
||||
{feature.badge && (
|
||||
<span className="mb-3 ml-2 inline-block align-middle text-xs px-2 py-1 bg-brand-500/20 text-brand-300 rounded-full font-semibold uppercase tracking-wide">
|
||||
{feature.badge}
|
||||
</span>
|
||||
)}
|
||||
|
||||
<h2 className="text-3xl sm:text-4xl font-bold mb-4 text-surface-50">
|
||||
{feature.title}
|
||||
</h2>
|
||||
<p className="text-lg text-surface-300 mb-8 leading-relaxed">
|
||||
{feature.description}
|
||||
</p>
|
||||
|
||||
{/* Feature Grid */}
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
{feature.features.map((item, itemIdx) => (
|
||||
<div
|
||||
key={itemIdx}
|
||||
className={`flex items-center gap-3 p-3 rounded-lg border transition-all ${
|
||||
item.premium
|
||||
? 'border-brand-500/30 bg-brand-500/5 hover:bg-brand-500/10'
|
||||
: 'border-surface-700/30 bg-surface-800/30 hover:border-surface-700/50'
|
||||
}`}
|
||||
>
|
||||
<span className="text-xl flex-shrink-0">{item.icon}</span>
|
||||
<span
|
||||
className={`text-sm font-medium ${item.premium ? 'text-brand-300' : 'text-surface-300'}`}
|
||||
>
|
||||
{item.label}
|
||||
{item.premium && (
|
||||
<span className="ml-1 inline-block text-xs px-2 py-0.5 bg-brand-500/20 text-brand-300 rounded font-semibold">
|
||||
Premium
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Visual Side */}
|
||||
<div className={idx % 2 === 1 ? 'lg:order-1' : ''}>
|
||||
<div className="relative">
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-brand-500/10 to-brand-600/5 rounded-2xl blur-xl" />
|
||||
<div className="relative rounded-2xl border border-surface-700/50 bg-surface-800/50 backdrop-blur p-8">
|
||||
<div className="space-y-4">
|
||||
{feature.features.slice(0, 3).map((item, itemIdx) => (
|
||||
<div
|
||||
key={itemIdx}
|
||||
className="flex items-center gap-3 p-3 bg-surface-900/50 rounded-lg border border-surface-700/30"
|
||||
>
|
||||
<div className="w-2 h-2 rounded-full bg-brand-400" />
|
||||
<span className="text-sm text-surface-300">{item.label}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="mt-6 pt-6 border-t border-surface-700/30">
|
||||
<div className="text-sm text-surface-500 text-center">
|
||||
<span className="text-brand-400 font-semibold">
|
||||
{feature.features.length} features
|
||||
</span>{' '}
|
||||
included
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* AI Features Highlight */}
|
||||
<section className="relative py-16 sm:py-20 lg:py-24 bg-gradient-to-b from-surface-900 to-surface-950">
|
||||
<div className="absolute inset-0 -z-10">
|
||||
<div className="absolute top-0 left-1/2 -translate-x-1/2 w-[40rem] h-96 bg-brand-500/10 rounded-full blur-3xl opacity-40" />
|
||||
</div>
|
||||
|
||||
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
|
||||
<div className="mb-16 text-center">
|
||||
<span className="rounded-full bg-brand-500/20 px-4 py-2 text-sm font-semibold text-brand-300">
|
||||
The Agentic Era of Self-Hosting
|
||||
</span>
|
||||
<h2 className="mt-6 text-3xl sm:text-4xl lg:text-5xl font-bold mb-4">
|
||||
Control Your Homelab With <span className="text-brand-400">Natural Language</span>
|
||||
</h2>
|
||||
<p className="text-lg text-surface-400 max-w-2xl mx-auto">
|
||||
DashCaddy speaks AI natively. Type what you want, or let your favorite assistant do it.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{/* AI Intent Router */}
|
||||
<div className="group relative overflow-hidden rounded-2xl border border-surface-700/50 bg-surface-800/50 backdrop-blur p-8 transition-all duration-300 hover:border-brand-500/50 hover:shadow-lg hover:shadow-brand-500/10">
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-brand-500/0 to-brand-500/0 group-hover:from-brand-500/5 group-hover:to-brand-500/10 transition-all duration-300 pointer-events-none" />
|
||||
<div className="relative z-10">
|
||||
<div className="mb-4 inline-flex rounded-lg bg-brand-500/10 p-3 text-brand-400 group-hover:bg-brand-500/20 group-hover:text-brand-300 transition-all duration-300">
|
||||
<div className="text-3xl">🎯</div>
|
||||
</div>
|
||||
<h3 className="mb-2 text-xl font-semibold text-surface-50">AI Intent Router</h3>
|
||||
<p className="text-surface-300 text-sm leading-relaxed mb-6">
|
||||
Skip the menus. Just type a command in plain English and DashCaddy figures out the right actions.
|
||||
</p>
|
||||
<div className="space-y-2">
|
||||
{['"deploy Plex"', '"check SSL status"', '"restart jellyfin"', '"show failing containers"'].map(
|
||||
(cmd, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="flex items-center gap-3 rounded-lg bg-surface-900/60 border border-surface-700/30 px-3 py-2"
|
||||
>
|
||||
<span className="text-brand-400 font-mono text-xs select-none">›</span>
|
||||
<span className="text-sm text-surface-300 font-mono">{cmd}</span>
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* MCP Server */}
|
||||
<div className="group relative overflow-hidden rounded-2xl border border-surface-700/50 bg-surface-800/50 backdrop-blur p-8 transition-all duration-300 hover:border-brand-500/50 hover:shadow-lg hover:shadow-brand-500/10">
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-brand-500/0 to-brand-500/0 group-hover:from-brand-500/5 group-hover:to-brand-500/10 transition-all duration-300 pointer-events-none" />
|
||||
<div className="relative z-10">
|
||||
<div className="mb-4 inline-flex rounded-lg bg-brand-500/10 p-3 text-brand-400 group-hover:bg-brand-500/20 group-hover:text-brand-300 transition-all duration-300">
|
||||
<div className="text-3xl">🔌</div>
|
||||
</div>
|
||||
<h3 className="mb-2 text-xl font-semibold text-surface-50">MCP Server</h3>
|
||||
<p className="text-surface-300 text-sm leading-relaxed mb-6">
|
||||
DashCaddy runs as a Model Context Protocol server, so AI assistants can manage your homelab directly.
|
||||
</p>
|
||||
<div className="space-y-2">
|
||||
{[
|
||||
'Claude desktop & API',
|
||||
'GPT / ChatGPT integrations',
|
||||
'Any MCP-compatible client',
|
||||
'Scoped, audited, secure access',
|
||||
].map((item, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="flex items-center gap-3 rounded-lg bg-surface-900/60 border border-surface-700/30 px-3 py-2"
|
||||
>
|
||||
<div className="w-2 h-2 rounded-full bg-brand-400" />
|
||||
<span className="text-sm text-surface-300">{item}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Free vs Premium Comparison */}
|
||||
<section className="relative py-16 sm:py-20 lg:py-24 bg-surface-950">
|
||||
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
|
||||
<div className="mb-16 text-center">
|
||||
<h2 className="text-3xl sm:text-4xl lg:text-5xl font-bold mb-4">
|
||||
Free vs <span className="text-brand-400">Premium</span>
|
||||
</h2>
|
||||
<p className="text-lg text-surface-400">
|
||||
A powerful free tier for everyone. Premium unlocks multi-host, SSO, and more.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||
{/* Free Column */}
|
||||
<div className="rounded-2xl border border-surface-700/50 bg-surface-800/40 backdrop-blur p-8">
|
||||
<div className="mb-6">
|
||||
<h3 className="text-2xl font-bold text-surface-50">Free</h3>
|
||||
<p className="text-sm text-surface-400 mt-1">Everything to self-host a single node.</p>
|
||||
</div>
|
||||
<ul className="space-y-3">
|
||||
{[
|
||||
'77 one-click app templates',
|
||||
'Automatic SSL certificates',
|
||||
'Technitium DNS automation',
|
||||
'Reverse proxy + Caddyfile builder',
|
||||
'Real-time monitoring & WebSocket updates',
|
||||
'TOTP 2FA + multi-user admin',
|
||||
'Encrypted credential vault',
|
||||
'Audit logging',
|
||||
'Security Center',
|
||||
'One-click backup & restore',
|
||||
'AI Intent Router + MCP Server',
|
||||
'REST API (350+ endpoints)',
|
||||
'Plugin / Extension system',
|
||||
'i18n (31 languages, RTL)',
|
||||
'Prometheus metrics export',
|
||||
].map((item, i) => (
|
||||
<li key={i} className="flex items-start gap-3">
|
||||
<svg className="mt-0.5 h-5 w-5 flex-shrink-0 text-brand-400" fill="none" viewBox="0 0 24 24" strokeWidth={2} stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M4.5 12.75l6 6 9-13.5" />
|
||||
</svg>
|
||||
<span className="text-sm text-surface-300">{item}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Premium Column */}
|
||||
<div className="relative rounded-2xl border border-brand-500/40 bg-gradient-to-br from-brand-950/40 to-surface-900 p-8">
|
||||
<div className="absolute -top-3 right-6">
|
||||
<span className="rounded-full bg-brand-500 px-3 py-1 text-xs font-semibold text-white uppercase tracking-wide">
|
||||
Premium
|
||||
</span>
|
||||
</div>
|
||||
<div className="mb-6">
|
||||
<h3 className="text-2xl font-bold text-surface-50">Premium</h3>
|
||||
<p className="text-sm text-surface-400 mt-1">For production, multi-host, and teams.</p>
|
||||
</div>
|
||||
<p className="text-sm text-brand-300 mb-4 font-medium">
|
||||
Everything in Free, plus:
|
||||
</p>
|
||||
<ul className="space-y-3">
|
||||
{[
|
||||
'Auto-Login SSO for deployed apps',
|
||||
'Recipes — multi-container stack deployment',
|
||||
'Docker Swarm orchestration',
|
||||
'Multi-Host Fleet Management (remote instances, parallel health probes, multi-host deploy plans)',
|
||||
'Disaster Recovery (full-system backup w/ checksums)',
|
||||
'Priority support',
|
||||
].map((item, i) => (
|
||||
<li key={i} className="flex items-start gap-3">
|
||||
<svg className="mt-0.5 h-5 w-5 flex-shrink-0 text-brand-400" fill="none" viewBox="0 0 24 24" strokeWidth={2} stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
<span className="text-sm text-surface-200">{item}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
<Link
|
||||
href="/pricing"
|
||||
className="mt-8 inline-flex items-center gap-2 rounded-lg bg-brand-500 px-6 py-3 font-semibold text-white hover:bg-brand-600 transition-all duration-200 hover:shadow-lg hover:shadow-brand-500/30"
|
||||
>
|
||||
Explore Premium Plans
|
||||
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" strokeWidth={2} stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M13.5 6H5.25A2.25 2.25 0 003 8.25v10.5A2.25 2.25 0 005.25 21h10.5A2.25 2.25 0 0018 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25" />
|
||||
</svg>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* FAQ CTA Section */}
|
||||
<section className="relative py-16 sm:py-20 lg:py-24 bg-gradient-to-b from-surface-950 to-surface-900">
|
||||
<div className="mx-auto max-w-3xl px-4 sm:px-6 lg:px-8">
|
||||
<div className="mb-12 text-center">
|
||||
<h2 className="text-3xl sm:text-4xl font-bold mb-4">
|
||||
Questions About <span className="text-brand-400">Features?</span>
|
||||
</h2>
|
||||
<p className="text-lg text-surface-400">
|
||||
Check our documentation or contact support.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col sm:flex-row gap-4 justify-center">
|
||||
<Link
|
||||
href="/docs"
|
||||
className="inline-flex items-center justify-center gap-2 rounded-lg bg-brand-500 px-8 py-3 font-semibold text-white hover:bg-brand-600 transition-all"
|
||||
>
|
||||
Read Documentation
|
||||
</Link>
|
||||
<a
|
||||
href="mailto:support@dashcaddy.net"
|
||||
className="inline-flex items-center justify-center gap-2 rounded-lg border border-surface-700 bg-surface-800/50 px-8 py-3 font-semibold text-surface-50 hover:border-brand-400 transition-colors"
|
||||
>
|
||||
Contact Support
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 98 KiB |
@@ -1,62 +1,62 @@
|
||||
import type { Metadata } from "next";
|
||||
import "./globals.css";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "DashCaddy - Self-Hosting Made Beautiful",
|
||||
description:
|
||||
"Deploy 92+ Docker apps with one click. AI-powered self-hosting with automatic SSL, DNS, reverse proxy, security center, and fleet management.",
|
||||
keywords: [
|
||||
"self-hosting",
|
||||
"docker",
|
||||
"dashboard",
|
||||
"caddy",
|
||||
"reverse proxy",
|
||||
"ssl",
|
||||
"dns",
|
||||
"homelab",
|
||||
"AI self-hosting",
|
||||
],
|
||||
openGraph: {
|
||||
title: "DashCaddy - Self-Hosting Made Beautiful",
|
||||
description:
|
||||
"Deploy 92+ Docker apps with one click. AI-powered self-hosting with automatic SSL, DNS, and reverse proxy.",
|
||||
url: "https://dashcaddy.net",
|
||||
siteName: "DashCaddy",
|
||||
type: "website",
|
||||
},
|
||||
twitter: {
|
||||
card: "summary_large_image",
|
||||
title: "DashCaddy - Self-Hosting Made Beautiful",
|
||||
description:
|
||||
"Deploy 92+ Docker apps with one click. AI-powered self-hosting.",
|
||||
},
|
||||
};
|
||||
|
||||
// Inline script to set theme class BEFORE first paint — prevents FOUC
|
||||
const themeScript = `
|
||||
(function() {
|
||||
try {
|
||||
var stored = localStorage.getItem('theme');
|
||||
var prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
var theme = stored || (prefersDark ? 'dark' : 'light');
|
||||
if (theme === 'dark') document.documentElement.classList.add('dark');
|
||||
} catch(e) {}
|
||||
})();
|
||||
`;
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html lang="en" className="h-full antialiased" suppressHydrationWarning>
|
||||
<head>
|
||||
<script dangerouslySetInnerHTML={{ __html: themeScript }} />
|
||||
</head>
|
||||
<body className="min-h-full flex flex-col bg-surface-950 text-surface-100" style={{ fontFamily: "'Inter', 'Segoe UI', system-ui, -apple-system, sans-serif" }}>
|
||||
{children}
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
import type { Metadata } from "next";
|
||||
import "./globals.css";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "DashCaddy - Self-Hosting Made Beautiful",
|
||||
description:
|
||||
"Deploy 77 Docker apps with one click. AI-powered self-hosting with automatic SSL, DNS, reverse proxy, security center, and fleet management.",
|
||||
keywords: [
|
||||
"self-hosting",
|
||||
"docker",
|
||||
"dashboard",
|
||||
"caddy",
|
||||
"reverse proxy",
|
||||
"ssl",
|
||||
"dns",
|
||||
"homelab",
|
||||
"AI self-hosting",
|
||||
],
|
||||
openGraph: {
|
||||
title: "DashCaddy - Self-Hosting Made Beautiful",
|
||||
description:
|
||||
"Deploy 77 Docker apps with one click. AI-powered self-hosting with automatic SSL, DNS, and reverse proxy.",
|
||||
url: "https://dashcaddy.net",
|
||||
siteName: "DashCaddy",
|
||||
type: "website",
|
||||
},
|
||||
twitter: {
|
||||
card: "summary_large_image",
|
||||
title: "DashCaddy - Self-Hosting Made Beautiful",
|
||||
description:
|
||||
"Deploy 77 Docker apps with one click. AI-powered self-hosting.",
|
||||
},
|
||||
};
|
||||
|
||||
// Inline script to set theme class BEFORE first paint — prevents FOUC
|
||||
const themeScript = `
|
||||
(function() {
|
||||
try {
|
||||
var stored = localStorage.getItem('theme');
|
||||
var prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
var theme = stored || (prefersDark ? 'dark' : 'light');
|
||||
if (theme === 'dark') document.documentElement.classList.add('dark');
|
||||
} catch(e) {}
|
||||
})();
|
||||
`;
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html lang="en" className="h-full antialiased" suppressHydrationWarning>
|
||||
<head>
|
||||
<script dangerouslySetInnerHTML={{ __html: themeScript }} />
|
||||
</head>
|
||||
<body className="min-h-full flex flex-col bg-surface-950 text-surface-100" style={{ fontFamily: "'Inter', 'Segoe UI', system-ui, -apple-system, sans-serif" }}>
|
||||
{children}
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
import type { Metadata } from 'next';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Privacy Policy — DashCaddy',
|
||||
description: 'DashCaddy privacy policy. We collect nothing. Your data never leaves your server.',
|
||||
};
|
||||
|
||||
export default function PrivacyPage() {
|
||||
return (
|
||||
<div className="min-h-screen bg-surface-950 text-surface-100">
|
||||
<div className="mx-auto max-w-3xl px-4 sm:px-6 lg:px-8 py-20">
|
||||
<h1 className="text-4xl font-bold mb-8 text-white">Privacy Policy</h1>
|
||||
<p className="text-surface-400 mb-8">Last updated: August 2026</p>
|
||||
|
||||
<div className="prose prose-invert max-w-none space-y-6 text-surface-300 leading-relaxed">
|
||||
<section>
|
||||
<h2 className="text-2xl font-semibold text-white mb-4">The Short Version</h2>
|
||||
<p className="text-lg">
|
||||
<strong className="text-brand-400">We collect nothing. We track nothing. Your data never leaves your server.</strong>
|
||||
</p>
|
||||
<p>
|
||||
DashCaddy is self-hosted software. It runs entirely on your hardware. We have no servers in the middle,
|
||||
no databases storing your information, and no analytics watching what you do. We literally cannot see your data.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="text-2xl font-semibold text-white mb-4">What We Don't Collect</h2>
|
||||
<ul className="space-y-2">
|
||||
<li>✅ No telemetry or usage analytics</li>
|
||||
<li>✅ No error reporting sent to us (error tracking is opt-in and disabled by default)</li>
|
||||
<li>✅ No tracking pixels, cookies, or fingerprinting</li>
|
||||
<li>✅ No account creation required (TOTP-based auth, stored locally)</li>
|
||||
<li>✅ No data sold, shared, or transmitted to third parties</li>
|
||||
<li>✅ No advertising networks</li>
|
||||
<li>✅ No "phone home" functionality</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="text-2xl font-semibold text-white mb-4">What Lives on Your Server</h2>
|
||||
<p>
|
||||
All of your data — service configurations, credentials, SSL certificates, DNS records, backups,
|
||||
and logs — stays on your machine. DashCaddy stores this in a local data directory on your server.
|
||||
It is never transmitted to us or any third party.
|
||||
</p>
|
||||
<p>
|
||||
If you uninstall DashCaddy, you can delete the entire data directory. Nothing remains on external systems
|
||||
because nothing was ever sent there.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="text-2xl font-semibold text-white mb-4">VM Sandboxing & Isolation</h2>
|
||||
<p>
|
||||
DashCaddy creates a bounded virtual disk (WSL2 on Windows, Lima on macOS, loopback ext4 on Linux)
|
||||
that physically cannot exceed the storage limit you choose. Uninstalling DashCaddy removes the entire
|
||||
virtual machine and disk image. Zero data leakage.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="text-2xl font-semibold text-white mb-4">License Activation (Premium Only)</h2>
|
||||
<p>
|
||||
If you purchase a Premium license, the license key is validated locally. The initial activation
|
||||
optionally contacts our license server to verify the key, but ongoing operation works fully offline.
|
||||
No usage data is transmitted during validation — only the license key itself.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="text-2xl font-semibold text-white mb-4">Your California Privacy Rights (CCPA)</h2>
|
||||
<p>
|
||||
If you are a California resident, you have rights under the California Consumer Privacy Act (CCPA):
|
||||
</p>
|
||||
<ul className="space-y-2">
|
||||
<li><strong>Right to Know:</strong> You can request what personal information we collect about you. (Answer: none.)</li>
|
||||
<li><strong>Right to Delete:</strong> You can request deletion of your personal information. (There's nothing to delete — it's all on your server.)</li>
|
||||
<li><strong>Right to Opt-Out:</strong> You can opt out of the sale of your personal information. (We don't sell anything. There's nothing to opt out of.)</li>
|
||||
<li><strong>Right to Non-Discrimination:</strong> You will not be treated differently for exercising these rights.</li>
|
||||
</ul>
|
||||
<p className="mt-4">
|
||||
To exercise any of these rights, contact us at{' '}
|
||||
<a href="mailto:support@dashcaddy.net" className="text-brand-400 hover:text-brand-300 font-medium">
|
||||
support@dashcaddy.net
|
||||
</a>. We will respond within 30 days.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="text-2xl font-semibold text-white mb-4">Children's Privacy</h2>
|
||||
<p>
|
||||
DashCaddy does not knowingly collect any information from children under 13. Since we collect no
|
||||
information from anyone, this is inherently satisfied.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="text-2xl font-semibold text-white mb-4">Third-Party Services</h2>
|
||||
<p>
|
||||
DashCaddy deploys third-party applications (Docker containers) at your direction. These applications
|
||||
are governed by their own privacy policies. DashCaddy itself does not integrate with or transmit data
|
||||
to any third-party service.
|
||||
</p>
|
||||
<p>
|
||||
For payments, we use Stripe. Stripe's privacy policy applies to payment processing. We never see
|
||||
or store your credit card information.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="text-2xl font-semibold text-white mb-4">Changes to This Policy</h2>
|
||||
<p>
|
||||
If we ever change our privacy practices, we'll update this page. But our core commitment won't change:
|
||||
DashCaddy is self-hosted. Your data stays yours.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="text-2xl font-semibold text-white mb-4">Contact</h2>
|
||||
<p>
|
||||
Questions about privacy? Email us at{' '}
|
||||
<a href="mailto:support@dashcaddy.net" className="text-brand-400 hover:text-brand-300 font-medium">
|
||||
support@dashcaddy.net
|
||||
</a>
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
import type { Metadata } from 'next';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Terms of Service — DashCaddy',
|
||||
description: 'DashCaddy terms of service.',
|
||||
};
|
||||
|
||||
export default function TermsPage() {
|
||||
return (
|
||||
<div className="min-h-screen bg-surface-950 text-surface-100">
|
||||
<div className="mx-auto max-w-3xl px-4 sm:px-6 lg:px-8 py-20">
|
||||
<h1 className="text-4xl font-bold mb-8 text-white">Terms of Service</h1>
|
||||
<p className="text-surface-400 mb-8">Last updated: August 2026</p>
|
||||
|
||||
<div className="prose prose-invert max-w-none space-y-6 text-surface-300 leading-relaxed">
|
||||
<section>
|
||||
<h2 className="text-2xl font-semibold text-white mb-4">1. Acceptance of Terms</h2>
|
||||
<p>By installing and using DashCaddy, you agree to these terms. If you don't agree, don't use it.</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="text-2xl font-semibold text-white mb-4">2. What DashCaddy Is</h2>
|
||||
<p>
|
||||
DashCaddy is self-hosted software that helps you deploy and manage Docker containers, reverse proxy
|
||||
configuration, SSL certificates, and DNS records. It runs entirely on your hardware.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="text-2xl font-semibold text-white mb-4">3. User Responsibility</h2>
|
||||
<p>
|
||||
You are solely responsible for what you deploy using DashCaddy. DashCaddy provides deployment tools —
|
||||
it does not monitor, screen, or control what applications you choose to run.
|
||||
</p>
|
||||
<p className="mt-4">
|
||||
<strong className="text-brand-400">You must comply with all applicable laws, including copyright and intellectual property laws.</strong>
|
||||
Always stream and access content that you own or have legal rights to. DashCaddy does not condone or
|
||||
support piracy of any kind.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="text-2xl font-semibold text-white mb-4">4. No Warranty</h2>
|
||||
<p>
|
||||
DashCaddy is provided "as is" without warranty of any kind. We are not liable for data loss,
|
||||
service interruptions, security issues, or any damages arising from the use of DashCaddy.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="text-2xl font-semibold text-white mb-4">5. Premium License</h2>
|
||||
<p>
|
||||
Premium features require a paid license. Licenses are validated locally. Refunds are available within
|
||||
14 days of purchase. Contact support@dashcaddy.net.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="text-2xl font-semibold text-white mb-4">6. Privacy</h2>
|
||||
<p>
|
||||
We collect no data. See our{' '}
|
||||
<a href="/policies/privacy" className="text-brand-400 hover:text-brand-300 font-medium">Privacy Policy</a>{' '}
|
||||
for details.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="text-2xl font-semibold text-white mb-4">7. Contact</h2>
|
||||
<p>
|
||||
Questions? Email{' '}
|
||||
<a href="mailto:support@dashcaddy.net" className="text-brand-400 hover:text-brand-300 font-medium">
|
||||
support@dashcaddy.net
|
||||
</a>
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||