Files
dashcaddy.net/src/app/docs/installation/page.tsx
T

292 lines
18 KiB
TypeScript

import Navbar from '@/components/Navbar';
import Footer from '@/components/Footer';
import DocsLayout from '@/components/docs/DocsLayout';
export default function DocsInstallationPage() {
return (
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
<Navbar />
<DocsLayout
title="Installation Guide"
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>Before you start</h2>
<p>
You only need three things to install DashCaddy:
</p>
<ul>
<li><strong>A Linux host that stays on</strong> Ubuntu 22.04 LTS, Debian 12, or any recent Linux distribution. 2&nbsp;GB RAM and a few GB of disk are enough to start.</li>
<li><strong>Root or sudo access</strong> the installer writes to <code>/etc/dashcaddy</code>, <code>/opt/dockerdata</code>, and the system Docker daemon.</li>
<li><strong>A hostname</strong> a domain you own, a custom-TLD like <code>dashcaddy.home</code>, or just your machine&apos;s IP for local-only use.</li>
</ul>
<h2>Pick your hostname</h2>
<p>
DashCaddy uses a single base hostname and creates subdomains under it for every service you add (for example
<code> plex.dashcaddy.home</code>, <code>homeassistant.dashcaddy.home</code>). Choose the option that matches
your situation:
</p>
<h3>Option A A real domain you own (recommended)</h3>
<p>
If you own <code>example.com</code>, register <code>dashcaddy.example.com</code> as a DNS A record pointing at
your host&apos;s public IP. You can buy a domain for about $10/year from any registrar (Namecheap, Cloudflare,
Porkbun, Google Domains). With a real domain, DashCaddy automatically gets a trusted Let&apos;s Encrypt
certificate on first launch no certificate warnings, no client setup.
</p>
<h3>Option B A custom TLD on your home network</h3>
<p>
Use a domain like <code>dashcaddy.home</code>, <code>dashcaddy.lan</code>, or <code>dashcaddy.local</code>.
These will not resolve on the public internet, which is exactly what you want for a home server. Two ways to
make them resolve:
</p>
<ul>
<li><strong>Router DNS</strong> most home routers let you add a local DNS entry. Point <code>*.dashcaddy.home</code> at your server&apos;s LAN IP. All devices on your network pick it up automatically.</li>
<li><strong>Tailscale MagicDNS</strong> install Tailscale on the host and your devices. Their MagicDNS gives every machine a stable <code>name.tailnet.ts.net</code> name, no DNS editing needed.</li>
</ul>
<p>
Custom TLDs require installing DashCaddy&apos;s internal CA certificate (DashCA) on each device that connects.
The setup wizard walks you through this on first run.
</p>
<h3>Option C A direct IP address</h3>
<p>
If you don&apos;t want to set up DNS at all, you can reach DashCaddy at <code>https://192.168.x.x:port</code>.
This works for a single machine on a LAN, but the URL changes when the host gets a new IP and you lose
subdomain support. Useful for a quick test, not a permanent setup.
</p>
<blockquote className="border-l-4 border-brand-500/50 bg-brand-500/5 p-4 rounded-r-lg">
<p className="text-surface-300">
<strong className="text-brand-400">No port forwarding?</strong> If your ISP puts you behind CGNAT or you
don&apos;t want to open ports 80 and 443, DashCaddy works fine behind a Tailscale tunnel or Cloudflare
Tunnel. Set up the tunnel first, point your hostname at the tunnel&apos;s origin, then run the installer
with <code>--no-caddy</code> and let the tunnel handle HTTPS termination.
</p>
</blockquote>
<h2>Run the installer</h2>
<p>
Open a terminal on the host you want to install DashCaddy on and run one of these:
</p>
<h3>Three-command install (most users)</h3>
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`# 1. Download and run the installer
curl -fsSL https://get.dashcaddy.net | bash
# 2. Answer the prompts (domain, install location, storage size)
# Defaults are sensible — just press Enter to accept.
# 3. Open the URL the installer prints at the end`}</code></pre>
<h3>Two-keystroke install (no typing)</h3>
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`# Install with a public domain — only thing you type is the domain
curl -fsSL https://get.dashcaddy.net | bash -s -- --domain dashcaddy.example.com
# Or use a custom TLD (your router / Tailscale handles DNS)
curl -fsSL https://get.dashcaddy.net | bash -s -- --domain dashcaddy.home
# Or skip DNS entirely and use a local address (good for a first test)
curl -fsSL https://get.dashcaddy.net | bash -s -- quick`}</code></pre>
<h3>What the installer does</h3>
<p>
The installer is a single shell script that runs in seven steps, all idempotent re-running it on an existing
install reconciles the stack rather than clobbering your config:
</p>
<ol>
<li><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>
<p>
If the installer ever hits a problem, it stops at the step that failed and tells you exactly which command to
re-run to retry. It does not silently leave a half-installed state behind.
</p>
<h2>The first-run setup wizard</h2>
<p>
When you open the dashboard URL the installer printed, you land on the setup wizard. It runs once and writes
your configuration to disk every choice is editable later from the settings page.
</p>
<p>The wizard walks through these screens:</p>
<ol>
<li><strong>Pick a configuration profile</strong> <em>Professional Home Lab</em> (recommended for most users), <em>Simple</em> (minimal config), or <em>Public</em> (for VPS deployments exposed to the internet)</li>
<li><strong>Configure your network</strong> your domain, the LAN IP that&apos;ll be used for service routing, and whether you want to use Tailscale for remote access</li>
<li><strong>Set storage limits</strong> max disk usage for Docker data and how much headroom to keep free</li>
<li><strong>Review your choices</strong> a summary screen with the resolved values so you can sanity-check before saving</li>
<li><strong>Disk-safety confirmation</strong> confirms you understand that bound storage will be created and what happens on uninstall</li>
</ol>
<p>
After the wizard saves, you&apos;re asked to create the first admin account. Pick a strong password this
account controls every service DashCaddy manages.
</p>
<h2>Set up two-factor authentication</h2>
<p>
Right after creating your admin account, the dashboard prompts you to enroll a TOTP authenticator
(Google Authenticator, Authy, 1Password, or any RFC&nbsp;6238 app). Scan the QR code with your phone, enter the
6-digit code to confirm, and store the recovery codes somewhere safe they are the only way back into your
account if you lose your phone.
</p>
<blockquote className="border-l-4 border-brand-500/50 bg-brand-500/5 p-4 rounded-r-lg">
<p className="text-surface-300">
<strong className="text-brand-400">Don&apos;t skip 2FA.</strong> DashCaddy can manage DNS records, start and
stop containers, and read credentials for the services it deploys. A leaked password without a second
factor is a full compromise. A leaked password with 2FA is just a login screen.
</p>
</blockquote>
<h2>Install the DashCA root certificate (custom-TLD installs only)</h2>
<p>
If you chose Option B (a custom TLD like <code>dashcaddy.home</code>) or Option C (a direct IP), your browser
will warn that the certificate is not trusted because it isn&apos;t, to anyone outside your network. DashCaddy
ships an internal certificate authority called <strong>DashCA</strong> so internal hostnames get a real TLS
certificate instead of self-signed warnings.
</p>
<p>
Visit the <strong>DashCA</strong> page in the dashboard and follow the per-platform instructions:
</p>
<ul>
<li><strong>macOS</strong> open the downloaded <code>.crt</code>, add it to the System keychain, set it to &ldquo;Always Trust&rdquo;</li>
<li><strong>Windows</strong> double-click the <code>.crt</code>, install to &ldquo;Trusted Root Certification Authorities&rdquo;</li>
<li><strong>Linux</strong> copy to <code>/usr/local/share/ca-certificates/</code> and run <code>sudo update-ca-certificates</code></li>
<li><strong>iOS / Android</strong> download the profile, install it via Settings, then enable full trust for the CA</li>
</ul>
<p>
You need to install the root certificate on every device that connects to your DashCaddy services, not just
the server. There&apos;s no way around this for custom-TLD installs TLS is what keeps your traffic private.
</p>
<h2>Harden the host right after install</h2>
<p>
DashCaddy protects the services it manages, but it runs on top of a normal Linux box that&apos;s still exposed
on your network. Spend five minutes on these four steps:
</p>
<h3>1. Enable the firewall</h3>
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`# UFW (Ubuntu / Debian)
sudo ufw default deny incoming
sudo ufw allow ssh # if you SSH in
sudo ufw allow 80/tcp # HTTP (Let's Encrypt + redirect)
sudo ufw allow 443/tcp # HTTPS
sudo ufw enable
# If you put DashCaddy behind a tunnel (Tailscale / Cloudflare), skip 80/443 above
# and only allow the tunnel interface — see your tunnel provider's docs`}</code></pre>
<h3>2. Disable password SSH login</h3>
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`# Edit /etc/ssh/sshd_config and set:
PasswordAuthentication no
PermitRootLogin prohibit-password # or "no" if you use a sudo user
sudo systemctl restart sshd`}</code></pre>
<h3>3. Turn on automatic security updates</h3>
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`# Debian / Ubuntu
sudo apt install -y unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades # answer "Yes"`}</code></pre>
<h3>4. Set up backups</h3>
<p>
All your DashCaddy state services, config, encrypted credentials, DashCA lives in
<code> /opt/dockerdata</code> and <code>/etc/dashcaddy</code>. Snapshot those two directories somewhere off
the host. Any tool works: <code>rsync</code> to a NAS, <code>restic</code> to Backblaze B2, a systemd timer
that tars them up nightly. The dashboard&apos;s Backup button exports a single JSON file you can store
anywhere if you want a no-setup option.
</p>
<h2>Verify the install</h2>
<p>
After the wizard finishes and you&apos;ve logged in, run through this checklist to confirm everything is wired
up correctly:
</p>
<ol>
<li><strong>Container is running</strong> <code>docker ps</code> should show <code>dashcaddy-api</code> with status <code>Up</code></li>
<li><strong>API is healthy</strong> from the host, <code>curl http://localhost:3001/healthz</code> returns <code>{`{"status":"alive"}`}</code> and <code>/readyz</code> returns 200 with all checks passing</li>
<li><strong>Dashboard responds</strong> opening the URL the installer printed shows the dashboard, not a browser error</li>
<li><strong>TLS works</strong> the address bar shows a padlock with no warnings (a real cert if you used Option A; trusted if you used Option B with DashCA installed)</li>
<li><strong>2FA works</strong> log out and back in with your password + TOTP code</li>
<li><strong>Updates work</strong> the dashboard shows the current version in the footer and the update button is enabled</li>
</ol>
<h2>Troubleshooting a fresh install</h2>
<div className="overflow-x-auto">
<table className="w-full text-left text-sm">
<thead>
<tr className="border-b border-surface-700">
<th className="py-3 pr-4 font-semibold text-surface-200">Symptom</th>
<th className="py-3 pr-4 font-semibold text-surface-200">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">Installer says &ldquo;Docker not found&rdquo;</td>
<td className="py-3 pr-4">Docker Engine isn&apos;t installed or you&apos;re not in the docker group</td>
<td className="py-3">Run <code>sudo usermod -aG docker $USER</code>, log out, log back in. Re-run the installer.</td>
</tr>
<tr className="border-b border-surface-800">
<td className="py-3 pr-4">Installer fails on &ldquo;port 80/443 already in use&rdquo;</td>
<td className="py-3 pr-4">Another web server (nginx, Apache, another Caddy) is bound to those ports</td>
<td className="py-3">Stop the conflicting service, or move it to different ports and re-run the installer</td>
</tr>
<tr className="border-b border-surface-800">
<td className="py-3 pr-4">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">Browser shows &ldquo;Your connection is not private&rdquo;</td>
<td className="py-3 pr-4">Custom-TLD install with no DashCA trust, OR the domain doesn&apos;t point at this host</td>
<td className="py-3">For custom TLDs, install the DashCA root certificate. For real domains, check DNS: <code>dig +short your-hostname</code> should return the host&apos;s public IP.</td>
</tr>
<tr className="border-b border-surface-800">
<td className="py-3 pr-4">Let&apos;s Encrypt certificate never issues</td>
<td className="py-3 pr-4">Domain doesn&apos;t resolve publicly, OR port 80 is blocked by your ISP</td>
<td className="py-3">Verify the A record with <code>dig</code>. If you&apos;re on CGNAT, switch to Tailscale Funnel or Cloudflare Tunnel.</td>
</tr>
<tr>
<td className="py-3 pr-4">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>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
# Remove everything including config and data (irreversible)
curl -fsSL https://get.dashcaddy.net | bash -s -- --uninstall --purge`}</code></pre>
<h2>Next steps</h2>
<p>
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 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 />
</div>
);
}