[grade=B] Align premium marketing and documentation
This commit is contained in:
+204
-204
@@ -1,204 +1,204 @@
|
||||
import Navbar from "@/components/Navbar";
|
||||
import Footer from "@/components/Footer";
|
||||
import Link from "next/link";
|
||||
|
||||
export default function AboutPage() {
|
||||
return (
|
||||
<>
|
||||
<Navbar />
|
||||
<main className="flex-1">
|
||||
{/* Hero */}
|
||||
<section className="relative pt-32 pb-20 px-4">
|
||||
<div className="absolute inset-0 hero-glow pointer-events-none" />
|
||||
<div className="max-w-4xl mx-auto text-center relative z-10">
|
||||
<h1 className="text-4xl md:text-5xl font-bold text-white mb-6">
|
||||
Built for the{" "}
|
||||
<span className="gradient-text">Self-Hosting Community</span>
|
||||
</h1>
|
||||
<p className="text-xl text-surface-300 max-w-2xl mx-auto">
|
||||
DashCaddy was born from the frustration of managing dozens of
|
||||
Docker containers, SSL certificates, and DNS records by hand. We
|
||||
built the tool we wished existed — and then made it AI-powered.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Story */}
|
||||
<section className="py-20 px-4">
|
||||
<div className="max-w-3xl mx-auto">
|
||||
<h2 className="text-2xl font-bold text-white mb-6">Our Story</h2>
|
||||
<div className="space-y-4 text-surface-300 leading-relaxed">
|
||||
<p>
|
||||
Self-hosting is powerful. You own your data, you control your
|
||||
infrastructure, and you're not at the mercy of SaaS
|
||||
providers who can change their terms, raise prices, or shut down
|
||||
overnight. But let's be honest — it can also be a
|
||||
pain.
|
||||
</p>
|
||||
<p>
|
||||
Every new service means editing Caddyfiles, creating DNS
|
||||
records, configuring SSL certificates, writing Docker Compose
|
||||
files, and hoping everything plays nicely together. Multiply that
|
||||
by 20, 30, or 50 services, and you've got a full-time
|
||||
operations job on your hands.
|
||||
</p>
|
||||
<p>
|
||||
DashCaddy was built to solve this. One click to deploy an app.
|
||||
SSL, DNS, and reverse proxy configuration happen automatically.
|
||||
A beautiful dashboard with real-time WebSocket updates to monitor
|
||||
everything. Natural language AI commands so you can just say
|
||||
“deploy Plex” and it happens. And when something
|
||||
goes wrong, you know about it immediately — not when a
|
||||
family member texts you that Plex is down.
|
||||
</p>
|
||||
<p>
|
||||
We believe self-hosting should be accessible to everyone, not
|
||||
just people who enjoy writing YAML at 2 AM. DashCaddy makes it
|
||||
beautiful, effortless, and intelligent.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Values */}
|
||||
<section className="py-20 px-4 border-t border-surface-800">
|
||||
<div className="max-w-5xl mx-auto">
|
||||
<h2 className="text-2xl font-bold text-white mb-12 text-center">
|
||||
What We Believe In
|
||||
</h2>
|
||||
<div className="grid md:grid-cols-3 gap-8">
|
||||
{[
|
||||
{
|
||||
icon: "🔒",
|
||||
title: "Proprietary & Polished",
|
||||
description:
|
||||
"DashCaddy is proprietary software built with care. The core platform is free to use, with Premium features for advanced orchestration.",
|
||||
},
|
||||
{
|
||||
icon: "🏠",
|
||||
title: "Your Data, Your Server",
|
||||
description:
|
||||
"DashCaddy runs entirely on your hardware. No cloud dependency, no telemetry, no phoning home. Your data never leaves your network.",
|
||||
},
|
||||
{
|
||||
icon: "🤖",
|
||||
title: "AI-Native",
|
||||
description:
|
||||
"From the AI Intent Router to MCP server integration, DashCaddy is built for the AI era. Control your homelab with natural language or let AI assistants manage it for you.",
|
||||
},
|
||||
].map((value) => (
|
||||
<div
|
||||
key={value.title}
|
||||
className="glass-card rounded-xl p-8 text-center"
|
||||
>
|
||||
<div className="text-4xl mb-4">{value.icon}</div>
|
||||
<h3 className="text-lg font-semibold text-white mb-3">
|
||||
{value.title}
|
||||
</h3>
|
||||
<p className="text-surface-400">{value.description}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Tech Stack */}
|
||||
<section className="py-20 px-4 border-t border-surface-800">
|
||||
<div className="max-w-5xl mx-auto">
|
||||
<h2 className="text-2xl font-bold text-white mb-12 text-center">
|
||||
Built With
|
||||
</h2>
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-6">
|
||||
{[
|
||||
{ name: "Caddy", role: "Reverse Proxy & SSL", icon: "🔒" },
|
||||
{ name: "Docker", role: "Container Runtime", icon: "🐳" },
|
||||
{ name: "Node.js", role: "API Backend", icon: "🟢" },
|
||||
{ name: "Technitium", role: "DNS Server", icon: "🌐" },
|
||||
{ name: "Express", role: "REST API + WebSocket", icon: "⚡" },
|
||||
{ name: "MCP", role: "AI Protocol Layer", icon: "🤖" },
|
||||
{ name: "Stripe", role: "Payment Processing", icon: "💳" },
|
||||
{ name: "Jest", role: "Testing (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'd love to hear from you.
|
||||
</p>
|
||||
<div className="flex flex-col sm:flex-row gap-4 justify-center">
|
||||
<a
|
||||
href="mailto:support@dashcaddy.net"
|
||||
className="px-6 py-3 rounded-lg bg-brand-600 hover:bg-brand-500 text-white font-medium transition-colors"
|
||||
>
|
||||
Email Us
|
||||
</a>
|
||||
<Link
|
||||
href="/docs"
|
||||
className="px-6 py-3 rounded-lg border border-surface-700 hover:border-surface-500 text-surface-300 font-medium transition-colors"
|
||||
>
|
||||
Read the Docs
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* CTA */}
|
||||
<section className="py-20 px-4 border-t border-surface-800">
|
||||
<div className="max-w-3xl mx-auto text-center">
|
||||
<h2 className="text-3xl font-bold text-white mb-4">
|
||||
Ready to simplify your homelab?
|
||||
</h2>
|
||||
<p className="text-surface-300 mb-8">
|
||||
Install DashCaddy and start deploying services today. Free forever.
|
||||
</p>
|
||||
<Link
|
||||
href="/pricing"
|
||||
className="inline-block px-8 py-4 rounded-lg bg-brand-600 hover:bg-brand-500 text-white font-semibold text-lg transition-colors"
|
||||
>
|
||||
View Pricing
|
||||
</Link>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<Footer />
|
||||
</>
|
||||
);
|
||||
}
|
||||
import Navbar from "@/components/Navbar";
|
||||
import Footer from "@/components/Footer";
|
||||
import Link from "next/link";
|
||||
|
||||
export default function AboutPage() {
|
||||
return (
|
||||
<>
|
||||
<Navbar />
|
||||
<main className="flex-1">
|
||||
{/* Hero */}
|
||||
<section className="relative pt-32 pb-20 px-4">
|
||||
<div className="absolute inset-0 hero-glow pointer-events-none" />
|
||||
<div className="max-w-4xl mx-auto text-center relative z-10">
|
||||
<h1 className="text-4xl md:text-5xl font-bold text-white mb-6">
|
||||
Built for the{" "}
|
||||
<span className="gradient-text">Self-Hosting Community</span>
|
||||
</h1>
|
||||
<p className="text-xl text-surface-300 max-w-2xl mx-auto">
|
||||
DashCaddy was born from the frustration of managing dozens of
|
||||
Docker containers, SSL certificates, and DNS records by hand. We
|
||||
built the tool we wished existed — and then made it AI-powered.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Story */}
|
||||
<section className="py-20 px-4">
|
||||
<div className="max-w-3xl mx-auto">
|
||||
<h2 className="text-2xl font-bold text-white mb-6">Our Story</h2>
|
||||
<div className="space-y-4 text-surface-300 leading-relaxed">
|
||||
<p>
|
||||
Self-hosting is powerful. You own your data, you control your
|
||||
infrastructure, and you're not at the mercy of SaaS
|
||||
providers who can change their terms, raise prices, or shut down
|
||||
overnight. But let's be honest — it can also be a
|
||||
pain.
|
||||
</p>
|
||||
<p>
|
||||
Every new service means editing Caddyfiles, creating DNS
|
||||
records, configuring SSL certificates, writing Docker Compose
|
||||
files, and hoping everything plays nicely together. Multiply that
|
||||
by 20, 30, or 50 services, and you've got a full-time
|
||||
operations job on your hands.
|
||||
</p>
|
||||
<p>
|
||||
DashCaddy was built to solve this. One click to deploy an app.
|
||||
SSL, DNS, and reverse proxy configuration happen automatically.
|
||||
A beautiful dashboard with real-time WebSocket updates to monitor
|
||||
everything. Natural language AI commands so you can just say
|
||||
“deploy Plex” and it happens. And when something
|
||||
goes wrong, you know about it immediately — not when a
|
||||
family member texts you that Plex is down.
|
||||
</p>
|
||||
<p>
|
||||
We believe self-hosting should be accessible to everyone, not
|
||||
just people who enjoy writing YAML at 2 AM. DashCaddy makes it
|
||||
beautiful, effortless, and intelligent.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Values */}
|
||||
<section className="py-20 px-4 border-t border-surface-800">
|
||||
<div className="max-w-5xl mx-auto">
|
||||
<h2 className="text-2xl font-bold text-white mb-12 text-center">
|
||||
What We Believe In
|
||||
</h2>
|
||||
<div className="grid md:grid-cols-3 gap-8">
|
||||
{[
|
||||
{
|
||||
icon: "🔒",
|
||||
title: "Proprietary & Polished",
|
||||
description:
|
||||
"DashCaddy is proprietary software built with care. The core platform is free to use, with Premium features for advanced orchestration.",
|
||||
},
|
||||
{
|
||||
icon: "🏠",
|
||||
title: "Your Data, Your Server",
|
||||
description:
|
||||
"DashCaddy runs entirely on your hardware. No cloud dependency, no telemetry, no phoning home. Your data never leaves your network.",
|
||||
},
|
||||
{
|
||||
icon: "🤖",
|
||||
title: "AI-Native",
|
||||
description:
|
||||
"From the AI Intent Router to MCP server integration, DashCaddy is built for the AI era. Control your homelab with natural language or let AI assistants manage it for you.",
|
||||
},
|
||||
].map((value) => (
|
||||
<div
|
||||
key={value.title}
|
||||
className="glass-card rounded-xl p-8 text-center"
|
||||
>
|
||||
<div className="text-4xl mb-4">{value.icon}</div>
|
||||
<h3 className="text-lg font-semibold text-white mb-3">
|
||||
{value.title}
|
||||
</h3>
|
||||
<p className="text-surface-400">{value.description}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Tech Stack */}
|
||||
<section className="py-20 px-4 border-t border-surface-800">
|
||||
<div className="max-w-5xl mx-auto">
|
||||
<h2 className="text-2xl font-bold text-white mb-12 text-center">
|
||||
Built With
|
||||
</h2>
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-6">
|
||||
{[
|
||||
{ name: "Caddy", role: "Reverse Proxy & SSL", icon: "🔒" },
|
||||
{ name: "Docker", role: "Container Runtime", icon: "🐳" },
|
||||
{ name: "Node.js", role: "API Backend", icon: "🟢" },
|
||||
{ name: "Technitium", role: "DNS Server", icon: "🌐" },
|
||||
{ name: "Express", role: "REST API + WebSocket", icon: "⚡" },
|
||||
{ name: "MCP", role: "AI Protocol Layer", icon: "🤖" },
|
||||
{ name: "Stripe", role: "Payment Processing", icon: "💳" },
|
||||
{ name: "Jest", role: "Testing (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'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 />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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 GB RAM and a few GB of disk are enough to start.</li>
|
||||
<li><strong>Root or sudo access</strong> — the installer writes to <code>/etc/dashcaddy</code>, <code>/opt/dockerdata</code>, and the system Docker daemon.</li>
|
||||
<li><strong>A hostname</strong> — a domain you own, a custom-TLD like <code>dashcaddy.home</code>, or just your machine's IP for local-only use.</li>
|
||||
</ul>
|
||||
|
||||
<h2>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's public IP. You can buy a domain for about $10/year from any registrar (Namecheap, Cloudflare,
|
||||
Porkbun, Google Domains). With a real domain, DashCaddy automatically gets a trusted Let's Encrypt
|
||||
certificate on first launch — no certificate warnings, no client setup.
|
||||
</p>
|
||||
|
||||
<h3>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'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's internal CA certificate (DashCA) on each device that connects.
|
||||
The setup wizard walks you through this on first run.
|
||||
</p>
|
||||
|
||||
<h3>Option C — A direct IP address</h3>
|
||||
<p>
|
||||
If you don't want to set up DNS at all, you can reach DashCaddy at <code>https://192.168.x.x:port</code>.
|
||||
This works for a single machine on a LAN, but the URL changes when the host gets a new IP and you lose
|
||||
subdomain support. Useful for a quick test, not a permanent setup.
|
||||
</p>
|
||||
<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't want to open ports 80 and 443, DashCaddy works fine behind a Tailscale tunnel or Cloudflare
|
||||
Tunnel. Set up the tunnel first, point your hostname at the tunnel's origin, then run the installer
|
||||
with <code>--no-caddy</code> and let the tunnel handle HTTPS termination.
|
||||
</p>
|
||||
</blockquote>
|
||||
|
||||
<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'll be used for service routing, and whether you want to use Tailscale for remote access</li>
|
||||
<li><strong>Set storage limits</strong> — max disk usage for Docker data and how much headroom to keep free</li>
|
||||
<li><strong>Review your choices</strong> — a summary screen with the resolved values so you can sanity-check before saving</li>
|
||||
<li><strong>Disk-safety confirmation</strong> — confirms you understand that bound storage will be created and what happens on uninstall</li>
|
||||
</ol>
|
||||
|
||||
<p>
|
||||
After the wizard saves, you're asked to create the first admin account. Pick a strong password — this
|
||||
account controls every service DashCaddy manages.
|
||||
</p>
|
||||
|
||||
<h2>Set up two-factor authentication</h2>
|
||||
<p>
|
||||
Right after creating your admin account, the dashboard prompts you to enroll a TOTP authenticator
|
||||
(Google Authenticator, Authy, 1Password, or any RFC 6238 app). Scan the QR code with your phone, enter the
|
||||
6-digit code to confirm, and store the recovery codes somewhere safe — they are the only way back into your
|
||||
account if you lose your phone.
|
||||
</p>
|
||||
<blockquote className="border-l-4 border-brand-500/50 bg-brand-500/5 p-4 rounded-r-lg">
|
||||
<p className="text-surface-300">
|
||||
<strong className="text-brand-400">Don't skip 2FA.</strong> DashCaddy can manage DNS records, start and
|
||||
stop containers, and read credentials for the services it deploys. A leaked password without a second
|
||||
factor is a full compromise. A leaked password with 2FA is just a login screen.
|
||||
</p>
|
||||
</blockquote>
|
||||
|
||||
<h2>Install the DashCA root certificate (custom-TLD installs only)</h2>
|
||||
<p>
|
||||
If you chose Option B (a custom TLD like <code>dashcaddy.home</code>) or Option C (a direct IP), your browser
|
||||
will warn that the certificate is not trusted — because it isn't, to anyone outside your network. DashCaddy
|
||||
ships an internal certificate authority called <strong>DashCA</strong> so internal hostnames get a real TLS
|
||||
certificate instead of self-signed warnings.
|
||||
</p>
|
||||
<p>
|
||||
Visit the <strong>DashCA</strong> page in the dashboard and follow the per-platform instructions:
|
||||
</p>
|
||||
<ul>
|
||||
<li><strong>macOS</strong> — open the downloaded <code>.crt</code>, add it to the System keychain, set it to “Always Trust”</li>
|
||||
<li><strong>Windows</strong> — double-click the <code>.crt</code>, install to “Trusted Root Certification Authorities”</li>
|
||||
<li><strong>Linux</strong> — copy to <code>/usr/local/share/ca-certificates/</code> and run <code>sudo update-ca-certificates</code></li>
|
||||
<li><strong>iOS / Android</strong> — download the profile, install it via Settings, then enable full trust for the CA</li>
|
||||
</ul>
|
||||
<p>
|
||||
You need to install the root certificate on every device that connects to your DashCaddy services, not just
|
||||
the server. There's no way around this for custom-TLD installs — TLS is what keeps your traffic private.
|
||||
</p>
|
||||
|
||||
<h2>Harden the host right after install</h2>
|
||||
<p>
|
||||
DashCaddy protects the services it manages, but it runs on top of a normal Linux box that's still exposed
|
||||
on your network. Spend five minutes on these four steps:
|
||||
</p>
|
||||
|
||||
<h3>1. Enable the firewall</h3>
|
||||
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`# UFW (Ubuntu / Debian)
|
||||
sudo ufw default deny incoming
|
||||
sudo ufw allow ssh # if you SSH in
|
||||
sudo ufw allow 80/tcp # HTTP (Let's Encrypt + redirect)
|
||||
sudo ufw allow 443/tcp # HTTPS
|
||||
sudo ufw enable
|
||||
|
||||
# If you put DashCaddy behind a tunnel (Tailscale / Cloudflare), skip 80/443 above
|
||||
# and only allow the tunnel interface — see your tunnel provider's docs`}</code></pre>
|
||||
|
||||
<h3>2. Disable password SSH login</h3>
|
||||
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`# Edit /etc/ssh/sshd_config and set:
|
||||
PasswordAuthentication no
|
||||
PermitRootLogin prohibit-password # or "no" if you use a sudo user
|
||||
|
||||
sudo systemctl restart sshd`}</code></pre>
|
||||
|
||||
<h3>3. Turn on automatic security updates</h3>
|
||||
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`# Debian / Ubuntu
|
||||
sudo apt install -y unattended-upgrades
|
||||
sudo dpkg-reconfigure -plow unattended-upgrades # answer "Yes"`}</code></pre>
|
||||
|
||||
<h3>4. Set up backups</h3>
|
||||
<p>
|
||||
All your DashCaddy state — services, config, encrypted credentials, DashCA — lives in
|
||||
<code> /opt/dockerdata</code> and <code>/etc/dashcaddy</code>. Snapshot those two directories somewhere off
|
||||
the host. Any tool works: <code>rsync</code> to a NAS, <code>restic</code> to Backblaze B2, a systemd timer
|
||||
that tars them up nightly. The dashboard's Backup button exports a single JSON file you can store
|
||||
anywhere if you want a no-setup option.
|
||||
</p>
|
||||
|
||||
<h2>Verify the install</h2>
|
||||
<p>
|
||||
After the wizard finishes and you've logged in, run through this checklist to confirm everything is wired
|
||||
up correctly:
|
||||
</p>
|
||||
<ol>
|
||||
<li><strong>Container is running</strong> — <code>docker ps</code> should show <code>dashcaddy-api</code> with status <code>Up</code></li>
|
||||
<li><strong>API is healthy</strong> — from the host, <code>curl http://localhost:3001/healthz</code> returns <code>{`{"status":"alive"}`}</code> and <code>/readyz</code> returns 200 with all checks passing</li>
|
||||
<li><strong>Dashboard responds</strong> — opening the URL the installer printed shows the dashboard, not a browser error</li>
|
||||
<li><strong>TLS works</strong> — the address bar shows a padlock with no warnings (a real cert if you used Option A; trusted if you used Option B with DashCA installed)</li>
|
||||
<li><strong>2FA works</strong> — log out and back in with your password + TOTP code</li>
|
||||
<li><strong>Updates work</strong> — the dashboard shows the current version in the footer and the update button is enabled</li>
|
||||
</ol>
|
||||
|
||||
<h2>Troubleshooting a fresh install</h2>
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-left text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-surface-700">
|
||||
<th className="py-3 pr-4 font-semibold text-surface-200">Symptom</th>
|
||||
<th className="py-3 pr-4 font-semibold text-surface-200">Cause</th>
|
||||
<th className="py-3 font-semibold text-surface-200">Solution</th>
|
||||
<th className="py-3 pr-4 font-semibold text-surface-200">Likely cause</th>
|
||||
<th className="py-3 font-semibold text-surface-200">Fix</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="text-surface-300">
|
||||
<tr className="border-b border-surface-800">
|
||||
<td className="py-3 pr-4">Port 80 or 443 already in use</td>
|
||||
<td className="py-3 pr-4">Another web server (nginx, Apache) is running</td>
|
||||
<td className="py-3">Stop the conflicting service or change Caddy's ports in docker-compose.yml</td>
|
||||
<td className="py-3 pr-4">Installer says “Docker not found”</td>
|
||||
<td className="py-3 pr-4">Docker Engine isn't installed or you're not in the docker group</td>
|
||||
<td className="py-3">Run <code>sudo usermod -aG docker $USER</code>, log out, log back in. Re-run the installer.</td>
|
||||
</tr>
|
||||
<tr className="border-b border-surface-800">
|
||||
<td className="py-3 pr-4">Docker permission denied</td>
|
||||
<td className="py-3 pr-4">Current user not in docker group</td>
|
||||
<td className="py-3">Run <code>sudo usermod -aG docker $USER</code> and log out/in</td>
|
||||
<td className="py-3 pr-4">Installer fails on “port 80/443 already in use”</td>
|
||||
<td className="py-3 pr-4">Another web server (nginx, Apache, another Caddy) is bound to those ports</td>
|
||||
<td className="py-3">Stop the conflicting service, or move it to different ports and re-run the installer</td>
|
||||
</tr>
|
||||
<tr className="border-b border-surface-800">
|
||||
<td className="py-3 pr-4">Caddy Admin API unreachable</td>
|
||||
<td className="py-3 pr-4">Caddy not running or Admin API disabled</td>
|
||||
<td className="py-3">Ensure Caddy is running with <code>admin :2019</code> in its config</td>
|
||||
<td className="py-3 pr-4">Dashboard URL gives a connection refused</td>
|
||||
<td className="py-3 pr-4">Container failed to start, or the host firewall is blocking the port</td>
|
||||
<td className="py-3"><code>docker ps</code> — if the container exited, <code>docker logs dashcaddy-api</code> shows why. If running, check the firewall.</td>
|
||||
</tr>
|
||||
<tr className="border-b border-surface-800">
|
||||
<td className="py-3 pr-4">Technitium API token invalid</td>
|
||||
<td className="py-3 pr-4">Token not set or expired</td>
|
||||
<td className="py-3">Generate a new token in Technitium web console and update .env</td>
|
||||
<td className="py-3 pr-4">Browser shows “Your connection is not private”</td>
|
||||
<td className="py-3 pr-4">Custom-TLD install with no DashCA trust, OR the domain doesn't point at this host</td>
|
||||
<td className="py-3">For custom TLDs, install the DashCA root certificate. For real domains, check DNS: <code>dig +short your-hostname</code> should return the host's public IP.</td>
|
||||
</tr>
|
||||
<tr className="border-b border-surface-800">
|
||||
<td className="py-3 pr-4">Dashboard shows 502 Bad Gateway</td>
|
||||
<td className="py-3 pr-4">API server not responding</td>
|
||||
<td className="py-3">Check <code>docker logs dashcaddy-api</code> for errors</td>
|
||||
</tr>
|
||||
<tr className="border-b border-surface-800">
|
||||
<td className="py-3 pr-4">Out of memory during deployment</td>
|
||||
<td className="py-3 pr-4">Insufficient RAM for container workloads</td>
|
||||
<td className="py-3">Increase host RAM or reduce concurrent service deployments</td>
|
||||
<td className="py-3 pr-4">Let's Encrypt certificate never issues</td>
|
||||
<td className="py-3 pr-4">Domain doesn't resolve publicly, OR port 80 is blocked by your ISP</td>
|
||||
<td className="py-3">Verify the A record with <code>dig</code>. If you're on CGNAT, switch to Tailscale Funnel or Cloudflare Tunnel.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="py-3 pr-4">Certificate trust errors in browser</td>
|
||||
<td className="py-3 pr-4">DashCA root cert not installed on client</td>
|
||||
<td className="py-3">Download root cert from DashCA page and install on client device</td>
|
||||
<td className="py-3 pr-4">Setup wizard loops back to step 1</td>
|
||||
<td className="py-3 pr-4">Browser stored a stale config from a previous install</td>
|
||||
<td className="py-3">Open DevTools → Application → Local Storage → clear the <code>dashcaddy-setup</code> key, then reload</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<h2>Post-install verification</h2>
|
||||
<p>After the stack is up, verify each layer with these commands:</p>
|
||||
<h2>Uninstall</h2>
|
||||
<p>
|
||||
If you want to remove DashCaddy cleanly, the installer has an uninstall mode that stops the container,
|
||||
removes the systemd unit, and (optionally) deletes the data volumes:
|
||||
</p>
|
||||
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`# Remove DashCaddy but keep your config and data (you can reinstall later)
|
||||
curl -fsSL https://get.dashcaddy.net | bash -s -- --uninstall
|
||||
|
||||
<h3>1. Check container status</h3>
|
||||
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`docker ps --filter "name=dashcaddy"
|
||||
# Expected: dashcaddy-api, dashcaddy-dashboard, caddy, technitium all running`}</code></pre>
|
||||
|
||||
<h3>2. Verify API health</h3>
|
||||
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`curl http://localhost:3001/healthz
|
||||
# Expected: {"status":"ok","version":"1.0.0"}
|
||||
|
||||
curl http://localhost:3001/readyz
|
||||
# Expected: {"status":"ready","checks":{"caddy":true,"technitium":true}}`}</code></pre>
|
||||
|
||||
<h3>3. Test Caddy Admin API</h3>
|
||||
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`curl http://localhost:2019/config/
|
||||
# Expected: JSON configuration object`}</code></pre>
|
||||
|
||||
<h3>4. Verify Technitium DNS</h3>
|
||||
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`curl http://localhost:5380/api/dns/zones/list?token=YOUR_TOKEN
|
||||
# Expected: List of DNS zones`}</code></pre>
|
||||
|
||||
<h3>5. Check Prometheus metrics</h3>
|
||||
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`curl http://localhost:3001/metrics
|
||||
# Expected: Prometheus-formatted metrics output`}</code></pre>
|
||||
|
||||
<h3>6. Access the dashboard</h3>
|
||||
<p>Open your browser and navigate to <code>http://localhost:3000</code> (or your configured domain). You should see the DashCaddy dashboard login screen.</p>
|
||||
# Remove everything including config and data (irreversible)
|
||||
curl -fsSL https://get.dashcaddy.net | bash -s -- --uninstall --purge`}</code></pre>
|
||||
|
||||
<h2>Next steps</h2>
|
||||
<p>
|
||||
Once install checks pass, head to the <a href="/docs/first-service" className="text-brand-400 hover:text-brand-300 underline">Deploy Your First Service</a> guide to
|
||||
bring your first application online.
|
||||
With DashCaddy installed and hardened, head to the <a href="/docs/first-service" className="text-brand-400 hover:text-brand-300 underline">Deploy Your First Service</a> guide
|
||||
to bring an application online through the App Selector.
|
||||
</p>
|
||||
<p>
|
||||
If you need to understand the architecture in more depth, see the <a href="/docs/overview" className="text-brand-400 hover:text-brand-300 underline">Product Overview</a>.
|
||||
If you want to understand the architecture before adding services, see the <a href="/docs/overview" className="text-brand-400 hover:text-brand-300 underline">Product Overview</a>.
|
||||
For problems during or after install, the <a href="/docs/troubleshooting" className="text-brand-400 hover:text-brand-300 underline">Troubleshooting</a> page
|
||||
walks each layer bottom-up.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
|
||||
@@ -27,7 +27,7 @@ export default function DocsIntegrationsPage() {
|
||||
Docker (and Docker Compose) is the runtime foundation for every deployment workflow, container lifecycle action,
|
||||
service discovery sweep, and template-based launch. DashCaddy communicates with the Docker daemon over the
|
||||
Unix socket (<code>/var/run/docker.sock</code>) to start, stop, restart, inspect, and adopt containers, and to
|
||||
deploy the <strong>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,
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -39,7 +39,7 @@ export default function DocsPremiumPage() {
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td>Dashboard & 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
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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>
|
||||
|
||||
@@ -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't the right tool for everyone. Here'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>
|
||||
);
|
||||
}
|
||||
@@ -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’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’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 “deploy
|
||||
Plex” or “check SSL status.” 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’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’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’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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user