[grade=B] Align premium marketing and documentation

This commit is contained in:
Krystie
2026-08-22 19:08:01 -07:00
parent e58130756e
commit ac2feb5ab7
13 changed files with 2114 additions and 1727 deletions
+204 -204
View File
@@ -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&apos;re not at the mercy of SaaS
providers who can change their terms, raise prices, or shut down
overnight. But let&apos;s be honest &mdash; 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&apos;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
&ldquo;deploy Plex&rdquo; and it happens. And when something
goes wrong, you know about it immediately &mdash; 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 (1770+ 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: "93", label: "App Templates" },
{ value: "1770+", label: "Test Cases" },
{ value: "260+", 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&apos;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&apos;re not at the mercy of SaaS
providers who can change their terms, raise prices, or shut down
overnight. But let&apos;s be honest &mdash; 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&apos;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
&ldquo;deploy Plex&rdquo; and it happens. And when something
goes wrong, you know about it immediately &mdash; 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 (2200+ 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: "2200+", 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&apos;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 />
</>
);
}
+2 -2
View File
@@ -34,7 +34,7 @@ export default function DocsFirstServicePage() {
<h2>Three ways to add a service</h2>
<h3>1. Pick from 93 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 93 applications.
opens with a searchable grid of 77 applications.
</p>
<h3>Step 2: Find Plex</h3>
+223 -317
View File
@@ -8,375 +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>
<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>
<ul>
<li><strong>A Linux host that stays on</strong> Ubuntu 22.04 LTS, Debian 12, or any recent Linux distribution. 2&nbsp;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&apos;s IP for local-only use.</li>
</ul>
<h2>Pick your hostname</h2>
<p>
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>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>{`curl -fsSL https://get.dashcaddy.net | bash
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>
<h3>Option A A real domain you own (recommended)</h3>
<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.
If you own <code>example.com</code>, register <code>dashcaddy.example.com</code> as a DNS A record pointing at
your host&apos;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&apos;s Encrypt
certificate on first launch no certificate warnings, no client setup.
</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>
<h3>Option B A custom TLD on your home network</h3>
<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:
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>
<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>
<ul>
<li><strong>Router DNS</strong> most home routers let you add a local DNS entry. Point <code>*.dashcaddy.home</code> at your server&apos;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>
DashCaddy's behavior is controlled through environment variables in the <code>.env</code> file. Here's
a complete reference:
Custom TLDs require installing DashCaddy&apos;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&apos;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>
<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.
<strong className="text-brand-400">No port forwarding?</strong> If your ISP puts you behind CGNAT or you
don&apos;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&apos;s origin, then run the installer
with <code>--no-caddy</code> and let the tunnel handle HTTPS termination.
</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>
</ul>
<h2>Manual setup</h2>
<h2>Run the installer</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>:
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>Run the installer: <code>curl -fsSL https://get.dashcaddy.net | bash</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&apos;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&apos;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&nbsp;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&apos;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&apos;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 &ldquo;Always Trust&rdquo;</li>
<li><strong>Windows</strong> double-click the <code>.crt</code>, install to &ldquo;Trusted Root Certification Authorities&rdquo;</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&apos;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&apos;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&apos;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&apos;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 &ldquo;Docker not found&rdquo;</td>
<td className="py-3 pr-4">Docker Engine isn&apos;t installed or you&apos;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 &ldquo;port 80/443 already in use&rdquo;</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 &ldquo;Your connection is not private&rdquo;</td>
<td className="py-3 pr-4">Custom-TLD install with no DashCA trust, OR the domain doesn&apos;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&apos;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&apos;s Encrypt certificate never issues</td>
<td className="py-3 pr-4">Domain doesn&apos;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&apos;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 />
+1 -1
View File
@@ -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>93 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,
+4 -4
View File
@@ -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">93 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>
+6 -4
View File
@@ -39,7 +39,7 @@ export default function DocsPremiumPage() {
</thead>
<tbody>
<tr><td>Dashboard &amp; web UI</td><td></td><td></td></tr>
<tr><td>93 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>
@@ -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>
+457 -457
View File
@@ -1,457 +1,457 @@
'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 93 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: '93 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 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">
{[
'93 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 (260+ 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>
);
}
'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, 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 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>
);
}
+62 -62
View File
@@ -1,62 +1,62 @@
import type { Metadata } from "next";
import "./globals.css";
export const metadata: Metadata = {
title: "DashCaddy - Self-Hosting Made Beautiful",
description:
"Deploy 93 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 93 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 93 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>
);
}
+669 -657
View File
File diff suppressed because it is too large Load Diff
+3 -19
View File
@@ -9,7 +9,7 @@ interface App {
}
// All apps below have real deployable templates in DashCaddy.
// Verified against src/docker/app-templates.js (93 templates, Aug 2026).
// Verified against src/docker/app-templates.js (77 templates, Aug 2026).
const apps: App[] = [
// Media
{ name: 'Plex', icon: '🎬', category: 'Media' },
@@ -21,7 +21,6 @@ const apps: App[] = [
{ name: 'Calibre-Web', icon: '📖', category: 'Media' },
{ name: 'Komga', icon: '📚', category: 'Media' },
{ name: 'Kavita', icon: '📖', category: 'Media' },
{ name: 'Subsonic', icon: '🎵', category: 'Media' },
// Media Management
{ name: 'Sonarr', icon: '📺', category: 'Media Mgmt' },
@@ -50,10 +49,6 @@ const apps: App[] = [
{ name: 'Paperless-ngx', icon: '📚', category: 'Productivity' },
{ name: 'Trilium Notes', icon: '🗒️', category: 'Productivity' },
{ name: 'Excalidraw', icon: '🎨', category: 'Productivity' },
{ name: 'HedgeDoc', icon: '📝', category: 'Productivity' },
{ name: 'OpenProject', icon: '📋', category: 'Productivity' },
{ name: 'Plane', icon: '✈️', category: 'Productivity' },
{ name: 'Vikunja', icon: '✅', category: 'Productivity' },
// Photos
{ name: 'Immich', icon: '📸', category: 'Photos' },
@@ -67,38 +62,28 @@ const apps: App[] = [
// Management
{ name: 'Portainer', icon: '🐳', category: 'Management' },
{ name: 'Watchtower', icon: '👓', category: 'Management' },
{ name: 'Yacht', icon: '⛵', category: 'Management' },
// Security
{ name: 'Vaultwarden', icon: '🔑', category: 'Security' },
{ name: 'DashCA', icon: '🔐', category: 'Security' },
{ name: 'Authentik', icon: '🔐', category: 'Security' },
{ name: 'CrowdSec', icon: '🛡️', category: 'Security' },
{ name: 'Authelia', icon: '🔐', category: 'Security' },
{ name: 'Bitwarden', icon: '🔑', category: 'Security' },
{ name: 'Keycloak', icon: '🔑', category: 'Security' },
// Development
{ name: 'VS Code Server', icon: '💻', category: 'Development' },
{ name: 'Gitea', icon: '🦊', category: 'Development' },
{ name: 'Jenkins', icon: '🔧', category: 'Development' },
{ name: 'Drone CI', icon: '🐝', category: 'Development' },
{ name: 'Forgejo', icon: '🦊', category: 'Development' },
{ name: 'GitLab', icon: '🦊', category: 'Development' },
// Monitoring
{ name: 'Grafana', icon: '📊', category: 'Monitoring' },
{ name: 'Uptime Kuma', icon: '📈', category: 'Monitoring' },
{ name: 'Speedtest Tracker', icon: '⚡', category: 'Monitoring' },
{ name: 'Dozzle', icon: '📜', category: 'Monitoring' },
{ name: 'Kibana', icon: '📊', category: 'Monitoring' },
{ name: 'Netdata', icon: '📈', category: 'Monitoring' },
{ name: 'Prometheus', icon: '🔥', category: 'Monitoring' },
// Networking
{ name: 'Pi-hole', icon: '🛡️', category: 'Networking' },
{ name: 'WireGuard VPN', icon: '🔒', category: 'Networking' },
{ name: 'OpenVPN', icon: '🔒', category: 'Networking' },
// DNS
{ name: 'Technitium DNS Server', icon: '🌐', category: 'DNS' },
@@ -125,7 +110,6 @@ const apps: App[] = [
// Home Automation
{ name: 'Home Assistant', icon: '🏠', category: 'Home Automation' },
{ name: 'Node-RED', icon: '🔴', category: 'Home Automation' },
{ name: 'OpenHAB', icon: '🏠', category: 'Home Automation' },
// Utilities
{ name: 'Weather', icon: '🌤️', category: 'Utilities' },
@@ -174,7 +158,7 @@ export default function AppShowcase() {
{/* Header */}
<div className="mb-12 text-center">
<h2 className="text-3xl sm:text-4xl font-bold text-surface-50 mb-4">
93 One-Click App Templates
77 One-Click App Templates
</h2>
<p className="text-lg text-surface-400 max-w-2xl mx-auto">
Deploy your favorite apps instantly with pre-configured templates, automatic SSL certificates, and integrated DNS management.
@@ -232,7 +216,7 @@ export default function AppShowcase() {
<div className="mt-16 grid grid-cols-3 gap-4 sm:gap-8">
<div className="text-center">
<div className="text-3xl sm:text-4xl font-bold text-brand-400 mb-2">
93
77
</div>
<p className="text-sm text-surface-400">App Templates</p>
</div>
+170
View File
@@ -0,0 +1,170 @@
'use client';
interface Capability {
feature: string;
dashcaddy: boolean | string;
values: Record<string, boolean | string>;
}
// The "values" map uses string "—" to indicate "not applicable / not available"
// and true / false for the supported check. Where accurate, we use short text
// like "Partial" or "Plugin" to be honest about the gap.
const rows: Capability[] = [
{
feature: 'AI Intent Router (natural language)',
dashcaddy: true,
values: { 'Portainer': false, 'CasaOS': false, 'YunoHost': false, 'Unraid': false, 'Cosmos Cloud': false },
},
{
feature: 'MCP Server for AI assistants',
dashcaddy: true,
values: { 'Portainer': false, 'CasaOS': false, 'YunoHost': false, 'Unraid': false, 'Cosmos Cloud': false },
},
{
feature: 'One-click Docker templates',
dashcaddy: '77 apps',
values: { 'Portainer': 'Manual', 'CasaOS': '~120 apps', 'YunoHost': '~500 apps', 'Unraid': 'Community', 'Cosmos Cloud': '~30 apps' },
},
{
feature: 'Automatic SSL (Let\'s Encrypt or internal CA)',
dashcaddy: true,
values: { 'Portainer': 'Manual', 'CasaOS': true, 'YunoHost': true, 'Unraid': 'Manual', 'Cosmos Cloud': true },
},
{
feature: 'Automatic DNS record management',
dashcaddy: true,
values: { 'Portainer': false, 'CasaOS': false, 'YunoHost': 'Partial', 'Unraid': false, 'Cosmos Cloud': 'Partial' },
},
{
feature: 'Internal PKI / CA for private TLDs',
dashcaddy: true,
values: { 'Portainer': false, 'CasaOS': false, 'YunoHost': false, 'Unraid': false, 'Cosmos Cloud': false },
},
{
feature: 'WebSocket real-time updates',
dashcaddy: true,
values: { 'Portainer': 'Partial', 'CasaOS': false, 'YunoHost': false, 'Unraid': false, 'Cosmos Cloud': 'Partial' },
},
{
feature: 'Unified Security Center (multi-source events)',
dashcaddy: true,
values: { 'Portainer': false, 'CasaOS': false, 'YunoHost': false, 'Unraid': false, 'Cosmos Cloud': false },
},
{
feature: 'Multi-host fleet management',
dashcaddy: 'Premium',
values: { 'Portainer': 'Business', 'CasaOS': false, 'YunoHost': false, 'Unraid': true, 'Cosmos Cloud': false },
},
{
feature: 'Plugin / extension system',
dashcaddy: true,
values: { 'Portainer': 'Limited', 'CasaOS': true, 'YunoHost': true, 'Unraid': 'Community', 'Cosmos Cloud': 'Limited' },
},
{
feature: 'Open-source core',
dashcaddy: 'Proprietary',
values: { 'Portainer': 'CE + Business', 'CasaOS': true, 'YunoHost': true, 'Unraid': 'Proprietary', 'Cosmos Cloud': true },
},
{
feature: 'Built-in user accounts + RBAC',
dashcaddy: true,
values: { 'Portainer': 'Business', 'CasaOS': false, 'YunoHost': true, 'Unraid': 'Plugin', 'Cosmos Cloud': 'Partial' },
},
];
const competitors = ['Portainer', 'CasaOS', 'YunoHost', 'Unraid', 'Cosmos Cloud'] as const;
function Cell({ value, highlight }: { value: boolean | string; highlight?: boolean }) {
if (value === true) {
return (
<span className={`inline-flex items-center justify-center w-6 h-6 rounded-full ${highlight ? 'bg-brand-500/20 text-brand-400' : 'bg-green-500/20 text-green-400'}`}>
<svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" strokeWidth={3} stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" d="M4.5 12.75l6 6 9-13.5" />
</svg>
</span>
);
}
if (value === false) {
return <span className="text-surface-600"></span>;
}
return <span className={`text-xs ${highlight ? 'text-brand-400 font-medium' : 'text-surface-400'}`}>{value}</span>;
}
export default function CompetitorComparison() {
return (
<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-7xl px-4 sm:px-6 lg:px-8">
<div className="mb-12 text-center">
<span className="inline-flex items-center rounded-full bg-brand-500/20 px-4 py-2 text-sm font-semibold text-brand-300">
Honest comparison
</span>
<h2 className="mt-6 text-3xl sm:text-4xl lg:text-5xl font-bold text-surface-50">
How DashCaddy compares
</h2>
<p className="mt-4 text-lg text-surface-400 max-w-2xl mx-auto">
DashCaddy isn&apos;t the right tool for everyone. Here&apos;s where it wins and where the alternatives are stronger.
</p>
</div>
<div className="overflow-x-auto rounded-2xl border border-surface-700/50 bg-surface-900/50 backdrop-blur-sm">
<table className="w-full min-w-[820px]">
<thead>
<tr className="border-b border-surface-700/50">
<th className="px-4 py-4 text-left text-sm font-semibold text-surface-300 bg-surface-900/70">
Capability
</th>
<th className="px-4 py-4 text-center text-sm font-semibold text-brand-400 bg-surface-900/80 border-l border-r border-brand-500/30">
DashCaddy
</th>
{[...competitors].map((c) => (
<th key={c} className="px-4 py-4 text-center text-sm font-semibold text-surface-300 bg-surface-900/50">
{c}
</th>
))}
</tr>
</thead>
<tbody className="divide-y divide-surface-700/30">
{rows.map((row, i) => (
<tr key={row.feature} className={`hover:bg-surface-800/30 transition-colors ${i % 2 === 1 ? 'bg-surface-900/20' : ''}`}>
<td className="px-4 py-4 text-sm text-surface-300">{row.feature}</td>
<td className="px-4 py-4 text-center border-l border-r border-brand-500/20 bg-brand-500/5">
<Cell value={row.dashcaddy} highlight />
</td>
{competitors.map((c) => (
<td key={c} className="px-4 py-4 text-center">
<Cell value={row.values[c]} />
</td>
))}
</tr>
))}
</tbody>
</table>
</div>
<div className="mt-8 grid grid-cols-1 md:grid-cols-3 gap-4">
<div className="rounded-lg border border-surface-700/50 bg-surface-800/40 p-4">
<div className="text-2xl mb-2">🎯</div>
<h3 className="text-sm font-semibold text-surface-50 mb-1">Where DashCaddy wins</h3>
<p className="text-xs text-surface-400">
AI-native control plane, MCP server, automatic DNS+SSL chain, internal PKI for private TLDs, unified security events.
</p>
</div>
<div className="rounded-lg border border-surface-700/50 bg-surface-800/40 p-4">
<div className="text-2xl mb-2">🤝</div>
<h3 className="text-sm font-semibold text-surface-50 mb-1">Where it ties</h3>
<p className="text-xs text-surface-400">
One-click apps, real-time updates, plugin systems, user accounts with RBAC are table stakes. DashCaddy does all of them.
</p>
</div>
<div className="rounded-lg border border-surface-700/50 bg-surface-800/40 p-4">
<div className="text-2xl mb-2">🏆</div>
<h3 className="text-sm font-semibold text-surface-50 mb-1">Where others win</h3>
<p className="text-xs text-surface-400">
YunoHost has 500+ packaged apps. Unraid has a mature community app store. If raw app count is the only thing you care about, those are better.
</p>
</div>
</div>
</div>
</section>
);
}
+173
View File
@@ -0,0 +1,173 @@
'use client';
import { useState } from 'react';
import Link from 'next/link';
interface Faq {
question: string;
answer: React.ReactNode;
}
const faqs: Faq[] = [
{
question: 'Is DashCaddy really free?',
answer: (
<>
Yes. The core platform Docker deployment, Caddy + DNS + TLS automation, the full 77-app template catalog,
AI Intent Router, MCP Server, Security Center, monitoring, backups, REST API, plugin system, and 31-language
i18n is free forever. Premium adds a focused set of advanced features (SSO, Recipes, Docker Swarm,
Multi-Host Fleet) available as a fixed-duration license. The license runs for the duration you buy
and then expires; you re-purchase manually to renew.
</>
),
},
{
question: 'Where does my data live?',
answer: (
<>
On your own hardware. DashCaddy runs on a Linux host you control a desktop, a NUC, a Raspberry Pi 5, or a VPS.
No cloud dependency, no telemetry, no phoning home. Your services, your credentials, your DNS records, your SSL
keys all live on your network. A license validation ping is the only outbound call, and you can run unlicensed
without it.
</>
),
},
{
question: 'What if I already have Docker containers running?',
answer: (
<>
Use <strong>Service Discovery</strong>. DashCaddy scans the Docker socket, lists every running container, and
lets you adopt any of them with one click. It adds the Caddy reverse-proxy route, DNS record, and TLS
certificate without touching the running container. Your existing data, volumes, and network config stay
exactly as they are.
</>
),
},
{
question: 'Do I need a public domain?',
answer: (
<>
No. You can use a custom TLD (<code>dashcaddy.home</code>, <code>dashcaddy.lan</code>) on your home network,
or a direct IP for a quick test. Custom TLDs need DashCA&rsquo;s internal CA root certificate installed on
each device that connects the setup wizard walks you through that. A real domain is the easiest path: it
auto-issues trusted Let&rsquo;s Encrypt certificates and you skip the CA install step.
</>
),
},
{
question: 'Can my AI assistant actually control DashCaddy?',
answer: (
<>
Yes, two ways. The <strong>AI Intent Router</strong> understands plain-English commands like &ldquo;deploy
Plex&rdquo; or &ldquo;check SSL status.&rdquo; The <strong>MCP Server</strong> exposes DashCaddy as a tool
endpoint for Claude, GPT, and any MCP-compatible AI assistant your assistant can list services, deploy
templates, manage DNS, and rebuild the catalog, all through the standardized Model Context Protocol.
</>
),
},
{
question: 'What happens to my services if I uninstall DashCaddy?',
answer: (
<>
By default, the uninstall stops the DashCaddy container but leaves your Docker containers and volumes
untouched. If you want a clean removal, the uninstaller has a <code>--purge</code> flag that also deletes
the DashCaddy config and data directories. Either way, your services keep running until you stop them.
</>
),
},
{
question: 'How does Premium compare to other paid dashboards?',
answer: (
<>
Premium is a fixed-duration license. $99 covers a full year (365 days) of SSO, Recipes, Docker Swarm, and
Fleet Management on one machine. The license expires at the end of its term and you purchase a new one
to renew. There&rsquo;s no per-user fee and no per-app fee. The free tier already covers everything a single
host needs, so Premium only makes sense if you specifically want SSO across deployed apps,
multi-container stacks, or to orchestrate a fleet.
</>
),
},
{
question: 'Is DashCaddy open source?',
answer: (
<>
DashCaddy is proprietary software, but it&rsquo;s built on a fully open-source stack Caddy, Docker, Node.js,
Express, Technitium DNS, Tailscale. The AI Intent Router, MCP Server, plugin system, and Security Center are
all built on open standards. You get vendor-friendly source-available documentation, and your data never
leaves your hardware.
</>
),
},
];
export default function FaqSection() {
const [open, setOpen] = useState<number | null>(0);
return (
<section className="relative py-16 sm:py-20 lg:py-24 bg-surface-950">
<div className="mx-auto max-w-3xl px-4 sm:px-6 lg:px-8">
<div className="mb-12 text-center">
<span className="inline-flex items-center rounded-full bg-brand-500/20 px-4 py-2 text-sm font-semibold text-brand-300">
FAQ
</span>
<h2 className="mt-6 text-3xl sm:text-4xl lg:text-5xl font-bold text-surface-50">
Questions, <span className="text-brand-400">answered</span>
</h2>
<p className="mt-4 text-lg text-surface-400">
The most common things people ask before installing DashCaddy.
</p>
</div>
<div className="space-y-3">
{faqs.map((faq, idx) => (
<div
key={idx}
className={`rounded-xl border transition-all overflow-hidden ${
open === idx
? 'border-brand-500/40 bg-surface-900/70 shadow-lg shadow-brand-500/5'
: 'border-surface-700/50 bg-surface-900/40 hover:border-surface-700'
}`}
>
<button
onClick={() => setOpen(open === idx ? null : idx)}
className="w-full px-6 py-4 flex items-center justify-between text-left gap-4"
>
<h3 className="text-base sm:text-lg font-semibold text-surface-50">
{faq.question}
</h3>
<svg
className={`w-5 h-5 flex-shrink-0 text-brand-400 transition-transform ${
open === idx ? 'rotate-180' : ''
}`}
fill="none"
viewBox="0 0 24 24"
strokeWidth={2}
stroke="currentColor"
>
<path strokeLinecap="round" strokeLinejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5" />
</svg>
</button>
{open === idx && (
<div className="border-t border-surface-700/50 bg-surface-950/40 px-6 py-5">
<p className="text-surface-300 leading-relaxed">{faq.answer}</p>
</div>
)}
</div>
))}
</div>
<div className="mt-10 text-center text-surface-400 text-sm">
Have a question that&rsquo;s not here?{' '}
<Link href="/docs" className="text-brand-400 hover:text-brand-300 underline">
Read the docs
</Link>{' '}
or{' '}
<a href="mailto:support@dashcaddy.net" className="text-brand-400 hover:text-brand-300 underline">
email support
</a>
.
</div>
</div>
</section>
);
}
+140
View File
@@ -0,0 +1,140 @@
'use client';
import { useState } from 'react';
interface InstallOption {
label: string;
description: string;
command: string;
}
const options: InstallOption[] = [
{
label: 'Public domain',
description: 'You own example.com — automatic Let\'s Encrypt certs, no client setup.',
command: 'curl -fsSL https://get.dashcaddy.net | bash -s -- --domain dashcaddy.example.com',
},
{
label: 'Custom TLD',
description: 'Home network with dashcaddy.home — clients install the DashCA root cert.',
command: 'curl -fsSL https://get.dashcaddy.net | bash -s -- --domain dashcaddy.home',
},
{
label: 'Quick test',
description: 'Local IP install — ideal for a first run on a fresh VM.',
command: 'curl -fsSL https://get.dashcaddy.net | bash -s -- quick',
},
];
export default function InstallCommand() {
const [active, setActive] = useState(0);
const [copied, setCopied] = useState(false);
const onCopy = async () => {
try {
await navigator.clipboard.writeText(options[active].command);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
} catch {
/* noop */
}
};
return (
<section className="relative py-16 sm:py-20 lg:py-24 overflow-hidden">
<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-[40rem] h-[40rem] bg-brand-500/10 rounded-full blur-3xl opacity-40" />
</div>
<div className="mx-auto max-w-5xl px-4 sm:px-6 lg:px-8">
<div className="text-center mb-10">
<span className="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-400">
<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>
One-line install
</span>
<h2 className="mt-6 text-3xl sm:text-4xl lg:text-5xl font-bold text-surface-50">
Live in <span className="text-brand-400">60 seconds</span>
</h2>
<p className="mt-4 text-lg text-surface-400 max-w-2xl mx-auto">
Pick your mode, paste one command, and the installer handles Docker, Caddy, the wizard, and your first login.
</p>
</div>
<div className="rounded-2xl border border-surface-700/50 bg-surface-900/60 backdrop-blur p-1 shadow-2xl shadow-brand-500/10">
{/* Mode tabs */}
<div className="grid grid-cols-1 sm:grid-cols-3 gap-1 p-1">
{options.map((opt, i) => (
<button
key={opt.label}
onClick={() => setActive(i)}
className={`rounded-xl px-4 py-3 text-left transition-all ${
active === i
? 'bg-surface-800/80 ring-1 ring-brand-500/40'
: 'hover:bg-surface-800/40'
}`}
>
<div className={`text-sm font-semibold ${active === i ? 'text-brand-400' : 'text-surface-200'}`}>
{opt.label}
</div>
<div className="text-xs text-surface-500 mt-0.5 hidden sm:block">
{opt.description}
</div>
</button>
))}
</div>
{/* Terminal-style command display */}
<div className="relative rounded-xl mt-2 overflow-hidden border border-surface-700/50 bg-surface-950">
<div className="flex items-center justify-between border-b border-surface-700/50 px-4 py-2 bg-surface-900/70">
<div className="flex items-center gap-2">
<div className="w-3 h-3 rounded-full bg-red-500/60" />
<div className="w-3 h-3 rounded-full bg-yellow-500/60" />
<div className="w-3 h-3 rounded-full bg-green-500/60" />
</div>
<span className="text-xs font-mono text-surface-500">get.dashcaddy.net</span>
<button
onClick={onCopy}
className="text-xs text-surface-400 hover:text-brand-400 transition-colors font-medium"
>
{copied ? '✓ Copied' : 'Copy'}
</button>
</div>
<div className="p-5 font-mono text-sm overflow-x-auto">
<span className="text-green-400 select-none">$ </span>
<span className="text-surface-100">{options[active].command}</span>
<span className="ml-1 inline-block w-2 h-4 bg-brand-400 animate-pulse align-middle" />
</div>
</div>
{/* After-install expectations */}
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3 mt-4 px-1">
<div className="flex items-start gap-3 p-3 rounded-lg bg-surface-900/40 border border-surface-700/30">
<span className="text-brand-400 text-lg"></span>
<div>
<div className="text-sm font-semibold text-surface-100">~60 seconds</div>
<div className="text-xs text-surface-500">From curl to ready dashboard</div>
</div>
</div>
<div className="flex items-start gap-3 p-3 rounded-lg bg-surface-900/40 border border-surface-700/30">
<span className="text-brand-400 text-lg">🧙</span>
<div>
<div className="text-sm font-semibold text-surface-100">Zero YAML</div>
<div className="text-xs text-surface-500">Wizard configures everything</div>
</div>
</div>
<div className="flex items-start gap-3 p-3 rounded-lg bg-surface-900/40 border border-surface-700/30">
<span className="text-brand-400 text-lg">🔄</span>
<div>
<div className="text-sm font-semibold text-surface-100">Idempotent</div>
<div className="text-xs text-surface-500">Re-run safely to update</div>
</div>
</div>
</div>
</div>
</div>
</section>
);
}