Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9fbd4f7be3 | ||
|
|
e780e5aa60 | ||
|
|
00aa123f73 | ||
|
|
3792c9c22d | ||
|
|
fbe1a7c95c | ||
|
|
4f03b8cf47 | ||
|
|
bb69b96816 | ||
|
|
aedaf4b4b5 | ||
|
|
f5104578c9 |
@@ -1,31 +1,7 @@
|
||||
# Stripe Configuration
|
||||
# Get your keys from https://dashboard.stripe.com/apikeys
|
||||
STRIPE_SECRET_KEY=sk_live_your_secret_key_here
|
||||
# DashCaddy website configuration
|
||||
NEXT_PUBLIC_APP_URL=https://dashcaddy.net
|
||||
NEXT_PUBLIC_LICENSE_SERVER_URL=https://licenses.dashcaddy.net
|
||||
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_your_publishable_key_here
|
||||
|
||||
# Stripe Webhook Secret
|
||||
# Get this from https://dashboard.stripe.com/webhooks after creating an endpoint
|
||||
STRIPE_WEBHOOK_SECRET=whsec_your_webhook_secret_here
|
||||
|
||||
# Stripe Price IDs (one per plan duration)
|
||||
# Create these Products + Prices in your Stripe Dashboard:
|
||||
# - DashCaddy Premium Monthly ($25/mo)
|
||||
# - DashCaddy Premium Quarterly ($50/3mo)
|
||||
# - DashCaddy Premium Semi-Annual ($65/6mo)
|
||||
# - DashCaddy Premium Annual ($99/yr)
|
||||
STRIPE_PRICE_MONTHLY=price_your_monthly_price_id
|
||||
STRIPE_PRICE_QUARTERLY=price_your_quarterly_price_id
|
||||
STRIPE_PRICE_SEMIANNUAL=price_your_semiannual_price_id
|
||||
STRIPE_PRICE_YEARLY=price_your_annual_price_id
|
||||
|
||||
# Stripe Payment Links (for static export checkout)
|
||||
# Create these in Stripe Dashboard → Payment Links
|
||||
# Then paste the URLs here and in src/app/pricing/page.tsx (STRIPE_LINKS)
|
||||
STRIPE_PAYMENT_LINK_MONTHLY=https://buy.stripe.com/REPLACE_MONTHLY_LINK
|
||||
STRIPE_PAYMENT_LINK_QUARTERLY=https://buy.stripe.com/REPLACE_QUARTERLY_LINK
|
||||
STRIPE_PAYMENT_LINK_SEMIANNUAL=https://buy.stripe.com/REPLACE_SEMIANNUAL_LINK
|
||||
STRIPE_PAYMENT_LINK_ANNUAL=https://buy.stripe.com/REPLACE_ANNUAL_LINK
|
||||
|
||||
# App URL
|
||||
NEXT_PUBLIC_APP_URL=https://dashcaddy.net
|
||||
NEXT_PUBLIC_LOOKUP_URL=https://licenses.dashcaddy.net/api/checkout/session
|
||||
# The website no longer owns Stripe secret or webhook handling.
|
||||
# Stripe Checkout sessions and webhook processing live in dashcaddy-license-server.
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
| `CLAUDE.md` | Project instructions for Claude |
|
||||
| `FILELIST.md` | This file |
|
||||
| `README.md` | Project overview and setup guide |
|
||||
| `STRIPE_SETUP.md` | Step-by-step Stripe configuration guide |
|
||||
| `STRIPE_SETUP.md` | Billing architecture and license-server setup guide |
|
||||
| `eslint.config.mjs` | ESLint configuration |
|
||||
| `next-env.d.ts` | Next.js TypeScript declarations |
|
||||
| `next.config.ts` | Next.js configuration |
|
||||
@@ -53,8 +53,8 @@
|
||||
|
||||
| File | Endpoint | Purpose |
|
||||
|------|----------|---------|
|
||||
| `src/app/api/checkout/route.ts` | `POST /api/checkout` | Creates Stripe Checkout session with 14-day trial |
|
||||
| `src/app/api/webhooks/stripe/route.ts` | `POST /api/webhooks/stripe` | Handles Stripe subscription lifecycle events |
|
||||
| `src/app/api/checkout/route.ts` | `POST /api/checkout` | Disabled compatibility stub; production checkout lives in dashcaddy-license-server |
|
||||
| `src/app/api/webhooks/stripe/route.ts` | `POST /api/webhooks/stripe` | Disabled compatibility stub; production webhook lives in dashcaddy-license-server |
|
||||
|
||||
## Components (`src/components/`)
|
||||
|
||||
|
||||
@@ -36,11 +36,11 @@ Open [http://localhost:3000](http://localhost:3000) to see the site.
|
||||
|
||||
## Stripe Integration
|
||||
|
||||
See [STRIPE_SETUP.md](./STRIPE_SETUP.md) for detailed setup instructions.
|
||||
See [STRIPE_SETUP.md](./STRIPE_SETUP.md) for the current billing and license-server setup.
|
||||
|
||||
**API Routes:**
|
||||
- `POST /api/checkout` - Creates Stripe Checkout session
|
||||
- `POST /api/webhooks/stripe` - Handles Stripe webhook events
|
||||
- Website pricing uses the external DashCaddy license server for checkout initiation.
|
||||
- Website-local Stripe routes are disabled compatibility stubs and are not the production billing path.
|
||||
|
||||
## Logo Setup
|
||||
|
||||
|
||||
@@ -1,83 +1,65 @@
|
||||
# Stripe Setup Guide for DashCaddy.net
|
||||
# DashCaddy Billing Setup
|
||||
|
||||
## 1. Create a Stripe Account
|
||||
DashCaddy website no longer processes Stripe webhooks locally.
|
||||
|
||||
Go to [stripe.com](https://stripe.com) and create an account (or log in).
|
||||
Current architecture:
|
||||
- `dashcaddy.net` handles pricing UI and starts checkout by calling the external license server
|
||||
- `dashcaddy-license-server` owns Stripe secret usage, webhook handling, subscription state, and license generation
|
||||
- DashCaddy app instances validate/deactivate against the external license server
|
||||
|
||||
## 2. Create Your Product and Prices
|
||||
## Required website environment
|
||||
|
||||
In the Stripe Dashboard:
|
||||
```env
|
||||
NEXT_PUBLIC_APP_URL=https://dashcaddy.net
|
||||
NEXT_PUBLIC_LICENSE_SERVER_URL=https://licenses.dashcaddy.net
|
||||
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_your_publishable_key_here
|
||||
```
|
||||
|
||||
1. Go to **Products** > **Add Product**
|
||||
2. Name: `DashCaddy Premium`
|
||||
3. Description: `Premium license for DashCaddy - Self-hosting dashboard`
|
||||
4. Create two prices:
|
||||
- **Monthly**: $20.00 USD / month (recurring)
|
||||
- **Yearly**: $99.00 USD / year (recurring)
|
||||
5. Note down both **Price IDs** (they look like `price_1234...`)
|
||||
## Required license server environment
|
||||
|
||||
## 3. Get Your API Keys
|
||||
```env
|
||||
PORT=3010
|
||||
APP_BASE_URL=https://licenses.dashcaddy.net
|
||||
DASHCADDY_WEBSITE_URL=https://dashcaddy.net
|
||||
STRIPE_SECRET_KEY=sk_live_...
|
||||
STRIPE_PUBLISHABLE_KEY=pk_live_...
|
||||
STRIPE_WEBHOOK_SECRET=whsec_...
|
||||
```
|
||||
|
||||
1. Go to **Developers** > **API Keys**
|
||||
2. Copy your **Publishable key** (`pk_test_...` or `pk_live_...`)
|
||||
3. Copy your **Secret key** (`sk_test_...` or `sk_live_...`)
|
||||
## Stripe product model
|
||||
|
||||
## 4. Set Up Webhooks
|
||||
One Premium tier, four subscription cadences:
|
||||
- 1 month — $25
|
||||
- 3 months — $50
|
||||
- 6 months — $65
|
||||
- 12 months — $99
|
||||
|
||||
1. Go to **Developers** > **Webhooks**
|
||||
2. Click **Add endpoint**
|
||||
3. URL: `https://dashcaddy.net/api/webhooks/stripe`
|
||||
4. Select these events:
|
||||
No free trial.
|
||||
7-day grace period.
|
||||
Cancel at period end.
|
||||
One active machine at a time.
|
||||
Premium features only: `sso`, `recipes`, `swarm`.
|
||||
|
||||
## Stripe webhook target
|
||||
|
||||
Configure Stripe to send events to the license server, not the website.
|
||||
|
||||
Recommended webhook endpoint:
|
||||
|
||||
```text
|
||||
https://licenses.dashcaddy.net/api/stripe/webhook
|
||||
```
|
||||
|
||||
Recommended events:
|
||||
- `checkout.session.completed`
|
||||
- `customer.subscription.created`
|
||||
- `customer.subscription.updated`
|
||||
- `customer.subscription.deleted`
|
||||
- `invoice.payment_failed`
|
||||
5. Copy the **Webhook signing secret** (`whsec_...`)
|
||||
|
||||
## 5. Configure Environment Variables
|
||||
## Website behavior
|
||||
|
||||
Copy `.env.example` to `.env.local` and fill in your values:
|
||||
The website pricing page should start checkout via:
|
||||
- `POST /api/checkout/session` on the external license server
|
||||
|
||||
```bash
|
||||
cp .env.example .env.local
|
||||
```
|
||||
|
||||
Edit `.env.local`:
|
||||
|
||||
```env
|
||||
STRIPE_SECRET_KEY=sk_live_your_actual_secret_key
|
||||
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_your_actual_publishable_key
|
||||
STRIPE_WEBHOOK_SECRET=whsec_your_actual_webhook_secret
|
||||
STRIPE_PRICE_MONTHLY=price_your_monthly_price_id
|
||||
STRIPE_PRICE_YEARLY=price_your_yearly_price_id
|
||||
NEXT_PUBLIC_APP_URL=https://dashcaddy.net
|
||||
```
|
||||
|
||||
## 6. Test with Stripe CLI (Optional)
|
||||
|
||||
For local development, use Stripe CLI to forward webhooks:
|
||||
|
||||
```bash
|
||||
stripe listen --forward-to localhost:3000/api/webhooks/stripe
|
||||
```
|
||||
|
||||
Use test card `4242 4242 4242 4242` with any future date and any CVC.
|
||||
|
||||
## 7. License Key Delivery
|
||||
|
||||
The webhook handler at `src/app/api/webhooks/stripe/route.ts` has TODO comments
|
||||
where you need to implement:
|
||||
|
||||
1. **Generate license key** using the same format as DashCaddy's license-keygen
|
||||
(DC-XXXXX-XXXXX-XXXXX-XXXXX-XXXXX)
|
||||
2. **Store it** in a database (Stripe metadata can also hold it)
|
||||
3. **Email it** to the customer (use Stripe's receipt email or a service like
|
||||
SendGrid/Resend)
|
||||
4. **Link it** to the Stripe subscription ID so you can manage renewals/cancellations
|
||||
|
||||
## Pricing Strategy Notes
|
||||
|
||||
- **Monthly ($20/mo)**: Positioned as the flexibility option
|
||||
- **Yearly ($99/yr)**: ~$8.25/mo — 58% savings, this will be the primary seller
|
||||
- **14-day free trial**: Enabled on both plans via `trial_period_days: 14`
|
||||
- **Promotion codes**: Enabled via `allow_promotion_codes: true`
|
||||
The website-local routes under `src/app/api/checkout` and `src/app/api/webhooks/stripe` are intentionally disabled compatibility stubs and should not be used for production billing.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
const nextConfig = {
|
||||
output: "export",
|
||||
trailingSlash: true,
|
||||
images: { unoptimized: true }
|
||||
import type { NextConfig } from "next";
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
/* config options here */
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
# Enable directory index resolution (serves index.html from subdirectories)
|
||||
DirectoryIndex index.html index.html.var
|
||||
|
||||
# Redirect old doc paths to new ones — using RewriteRule for broader compatibility
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine On
|
||||
RewriteRule ^docs/product-overview/?$ /docs/overview/ [R=301,L]
|
||||
RewriteRule ^docs/deploy-first-service/?$ /docs/first-service/ [R=301,L]
|
||||
RewriteRule ^docs/premium-features/?$ /docs/premium/ [R=301,L]
|
||||
RewriteRule ^docs/infrastructure/?$ /docs/integrations/ [R=301,L]
|
||||
</IfModule>
|
||||
|
||||
# Fallback using mod_alias Redirect if available
|
||||
<IfModule mod_alias.c>
|
||||
Redirect 301 /docs/product-overview /docs/overview/
|
||||
Redirect 301 /docs/deploy-first-service /docs/first-service/
|
||||
Redirect 301 /docs/premium-features /docs/premium/
|
||||
Redirect 301 /docs/infrastructure /docs/integrations/
|
||||
</IfModule>
|
||||
|
||||
# Set cache headers for static assets
|
||||
<IfModule mod_expires.c>
|
||||
ExpiresActive On
|
||||
ExpiresByType text/css "access plus 1 month"
|
||||
ExpiresByType application/javascript "access plus 1 month"
|
||||
ExpiresByType image/png "access plus 1 month"
|
||||
ExpiresByType image/svg+xml "access plus 1 month"
|
||||
ExpiresByType image/x-icon "access plus 1 year"
|
||||
</IfModule>
|
||||
|
||||
# Enable compression
|
||||
<IfModule mod_deflate.c>
|
||||
AddOutputFilterByType DEFLATE text/html text/plain text/css application/javascript application/json
|
||||
</IfModule>
|
||||
|
||||
# Custom 404 page
|
||||
ErrorDocument 404 /404.html
|
||||
|
Before Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 6.3 KiB |
@@ -0,0 +1,83 @@
|
||||
# DashCaddy Tutorial Project Summary
|
||||
|
||||
## Workflows Covered
|
||||
|
||||
1. **Getting Started** - Setup wizard, timezone selection, deployment mode selection
|
||||
2. **Theme Customization** - Theme builder modal, preset themes, color customization
|
||||
3. **Viewing Logs** - Log viewer modal, live streaming, log filtering
|
||||
4. **Quick Search** - Quick search modal, keyboard shortcuts, navigation
|
||||
5. **Backup & Restore** - Manual backup, automated backup, restore workflow
|
||||
6. **Stats Monitoring** - Resource monitor, live stats, alerts view
|
||||
|
||||
## Themes Captured
|
||||
|
||||
- **Light theme**: Full coverage across all workflows
|
||||
- **Dark theme**: Partial - theme builder modal captured, but quick theme toggle button doesn't switch themes (it's display-only)
|
||||
|
||||
## Screenshots
|
||||
|
||||
**Total screenshots: 30+**
|
||||
|
||||
Organized by workflow:
|
||||
- `01-getting-started/` - 5 screenshots (light)
|
||||
- `02-deploy-app/` - 5 screenshots (light, but app selector was empty)
|
||||
- `03-theme-customization/` - 5 screenshots (2 dark, 3 light)
|
||||
- `04-viewing-logs/` - 4 screenshots (light)
|
||||
- `05-quick-search/` - 3 screenshots (light)
|
||||
- `06-backup-restore/` - 2 screenshots (light)
|
||||
- `07-stats-monitoring/` - 2 screenshots (light)
|
||||
- Plus additional test/diagnostic screenshots
|
||||
|
||||
## Files Added to dashcaddy.net Repo
|
||||
|
||||
### Source Tutorials (`src/tutorials/`)
|
||||
- 01-getting-started.md
|
||||
- 02-theme-customization.md
|
||||
- 03-viewing-logs.md
|
||||
- 04-quick-search.md
|
||||
- 05-backup-restore.md
|
||||
- 06-stats-monitoring.md
|
||||
|
||||
### Public Files (`public/tutorials/`)
|
||||
- dashcaddy-getting-started.html (self-contained HTML guide)
|
||||
- dashcaddy-getting-started.md (markdown version)
|
||||
- issues.md (test instance notes)
|
||||
- screenshots/ (all workflow screenshots)
|
||||
|
||||
## Known Gaps
|
||||
|
||||
1. **App Selector Empty** - The test instance App Selector modal shows no apps. Unable to document the "Deploy Your First App" workflow.
|
||||
|
||||
2. **Dark Theme Toggle** - The "🎨 Light" button in the top bar displays current theme but doesn't toggle. Theme switching only works through Theme Builder modal.
|
||||
|
||||
3. **Menu Navigation** - Some menu items in the TOOLS/STATUS/ADMIN sections weren't directly clickable in headless Chrome. Had to use JavaScript workarounds to open modals.
|
||||
|
||||
4. **Live Data** - Logs and Stats show "Loading..." because the test instance doesn't have actual container data.
|
||||
|
||||
5. **Mobile Screenshots** - Not captured (viewport 1920x1080 only).
|
||||
|
||||
6. **Delete/Cleanup Workflows** - Not fully explored (would need running services).
|
||||
|
||||
7. **Smart Arr Setup** - Not explored.
|
||||
|
||||
8. **Tailscale Integration** - Not explored.
|
||||
|
||||
9. **Docker Resources** - Not explored in detail.
|
||||
|
||||
## PR Information
|
||||
|
||||
**Branch:** `tutorials/dashcaddy-getting-started-2026-05-01`
|
||||
**Base Branch:** `master`
|
||||
**PR/MR URL:** https://git.dashcaddy.net/sami7777/dashcaddy.net/pulls/new/tutorials/dashcaddy-getting-started-2026-05-01
|
||||
|
||||
## Questions for User
|
||||
|
||||
1. Should tutorials be integrated into the `/docs` page, or kept as standalone downloadable content?
|
||||
2. Should I integrate the tutorials into the Next.js app router (create pages at `/tutorials/*`) or keep them as static markdown files?
|
||||
3. The app selector is empty in the test instance - should I skip the "Deploy First App" tutorial or mark it as needing backend configuration?
|
||||
4. Is the 11MB tarball size acceptable, or should screenshots be optimized/compressed?
|
||||
5. Should I try to capture mobile (390x844) screenshots, or is 1920x1080 sufficient?
|
||||
|
||||
## Local Backup
|
||||
|
||||
Tarball location: `/root/dashcaddy-tutorial.tar.gz` (11MB)
|
||||
@@ -0,0 +1,385 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>DashCaddy Getting Started Guide</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
|
||||
line-height: 1.6;
|
||||
color: #333;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
color: #1a1a2e;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.8rem;
|
||||
margin-top: 2rem;
|
||||
margin-bottom: 1rem;
|
||||
color: #1a1a2e;
|
||||
border-bottom: 2px solid #ddd;
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.3rem;
|
||||
margin-top: 1.5rem;
|
||||
margin-bottom: 0.75rem;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.intro {
|
||||
font-size: 1.1rem;
|
||||
color: #666;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.toc {
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.toc h2 {
|
||||
margin-top: 0;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.toc ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.toc li {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.toc a {
|
||||
color: #0066cc;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.toc a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.feature-card {
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.feature-card h3 {
|
||||
margin-top: 0;
|
||||
color: #0066cc;
|
||||
}
|
||||
|
||||
.feature-card p {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.feature-link {
|
||||
display: inline-block;
|
||||
margin-top: 0.5rem;
|
||||
color: #0066cc;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.feature-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
ul, ol {
|
||||
margin-left: 1.5rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
code {
|
||||
background: #f0f0f0;
|
||||
padding: 0.2rem 0.4rem;
|
||||
border-radius: 3px;
|
||||
font-family: 'Monaco', 'Consolas', monospace;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
kbd {
|
||||
background: #e0e0e0;
|
||||
padding: 0.2rem 0.5rem;
|
||||
border-radius: 3px;
|
||||
font-family: 'Monaco', 'Consolas', monospace;
|
||||
font-size: 0.85rem;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
th, td {
|
||||
padding: 0.75rem;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
|
||||
th {
|
||||
background: #f5f5f5;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
border-left: 4px solid #0066cc;
|
||||
padding-left: 1rem;
|
||||
margin: 1rem 0;
|
||||
color: #666;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.shortcut-table td:first-child {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
footer {
|
||||
text-align: center;
|
||||
margin-top: 3rem;
|
||||
padding-top: 1rem;
|
||||
border-top: 1px solid #ddd;
|
||||
color: #666;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>DashCaddy Getting Started Guide</h1>
|
||||
<p class="intro">A practical guide to getting started with DashCaddy — the self-hosted app dashboard.</p>
|
||||
|
||||
<div class="toc">
|
||||
<h2>Table of Contents</h2>
|
||||
<ul>
|
||||
<li><a href="#getting-started">Getting Started</a> — First-time setup and configuration</li>
|
||||
<li><a href="#theme-customization">Theme Customization</a> — Customize the dashboard appearance</li>
|
||||
<li><a href="#viewing-logs">Viewing Logs</a> — Monitor service logs in real-time</li>
|
||||
<li><a href="#quick-search">Quick Search</a> — Fast access to services and settings</li>
|
||||
<li><a href="#backup-restore">Backup & Restore</a> — Protect your configuration</li>
|
||||
<li><a href="#monitoring">Monitoring Resources</a> — Track CPU, memory, and disk usage</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<h2 id="getting-started">Getting Started</h2>
|
||||
<div class="feature-card">
|
||||
<h3>First-Time Setup</h3>
|
||||
<p>Learn how to log in, configure your timezone, and choose a deployment mode.</p>
|
||||
<p><strong>What you'll learn:</strong></p>
|
||||
<ul>
|
||||
<li>How to log in with TOTP authentication</li>
|
||||
<li>How to select your timezone</li>
|
||||
<li>How to choose a deployment mode (Simple, Home Lab, Public, Custom)</li>
|
||||
<li>How to complete or skip the initial setup wizard</li>
|
||||
</ul>
|
||||
<a class="feature-link" href="tutorials/01-getting-started.html">Read full tutorial →</a>
|
||||
</div>
|
||||
|
||||
<h2 id="theme-customization">Theme Customization</h2>
|
||||
<div class="feature-card">
|
||||
<h3>Customize the Dashboard</h3>
|
||||
<p>Make DashCaddy your own with custom themes and colors.</p>
|
||||
<p><strong>What you'll learn:</strong></p>
|
||||
<ul>
|
||||
<li>How to open the Theme Builder</li>
|
||||
<li>How to use preset themes (Dark, Light, Nord, Dracula, etc.)</li>
|
||||
<li>How to customize colors for backgrounds, text, accents, and status indicators</li>
|
||||
<li>How to save, import, and export themes</li>
|
||||
</ul>
|
||||
<a class="feature-link" href="tutorials/02-theme-customization.html">Read full tutorial →</a>
|
||||
</div>
|
||||
|
||||
<h2 id="viewing-logs">Viewing Logs</h2>
|
||||
<div class="feature-card">
|
||||
<h3>Monitor Service Logs</h3>
|
||||
<p>Monitor your services with real-time log streaming.</p>
|
||||
<p><strong>What you'll learn:</strong></p>
|
||||
<ul>
|
||||
<li>How to access the logs viewer</li>
|
||||
<li>How to navigate and filter log entries</li>
|
||||
<li>How to use live log streaming</li>
|
||||
<li>How to control the number of displayed log lines</li>
|
||||
</ul>
|
||||
<a class="feature-link" href="tutorials/03-viewing-logs.html">Read full tutorial →</a>
|
||||
</div>
|
||||
|
||||
<h2 id="quick-search">Quick Search</h2>
|
||||
<div class="feature-card">
|
||||
<h3>Fast Access to Everything</h3>
|
||||
<p>Find services and settings instantly.</p>
|
||||
<p><strong>What you'll learn:</strong></p>
|
||||
<ul>
|
||||
<li>How to open Quick Search (<kbd>Ctrl+K</kbd> / <kbd>Cmd+K</kbd>)</li>
|
||||
<li>How to search for services and settings</li>
|
||||
<li>How to navigate and select results with keyboard shortcuts</li>
|
||||
</ul>
|
||||
<a class="feature-link" href="tutorials/04-quick-search.html">Read full tutorial →</a>
|
||||
</div>
|
||||
|
||||
<h2 id="backup-restore">Backup & Restore</h2>
|
||||
<div class="feature-card">
|
||||
<h3>Protect Your Configuration</h3>
|
||||
<p>Create full backups of your entire DashCaddy setup.</p>
|
||||
<p><strong>What you'll learn:</strong></p>
|
||||
<ul>
|
||||
<li>How to create a manual backup</li>
|
||||
<li>What's included in a full backup (services, credentials, themes)</li>
|
||||
<li>How to restore from a backup file</li>
|
||||
<li>How to set up automated backups</li>
|
||||
<li>How to view backup history</li>
|
||||
</ul>
|
||||
<a class="feature-link" href="tutorials/05-backup-restore.html">Read full tutorial →</a>
|
||||
</div>
|
||||
|
||||
<h2 id="monitoring">Monitoring Resources</h2>
|
||||
<div class="feature-card">
|
||||
<h3>Track System Performance</h3>
|
||||
<p>Monitor CPU, memory, network, and disk usage in real-time.</p>
|
||||
<p><strong>What you'll learn:</strong></p>
|
||||
<ul>
|
||||
<li>How to access the Resource Monitor</li>
|
||||
<li>How to interpret live stats and historical summaries</li>
|
||||
<li>How to set up alerts for resource thresholds</li>
|
||||
<li>Understanding CPU, memory, network, and disk metrics</li>
|
||||
</ul>
|
||||
<a class="feature-link" href="tutorials/06-stats-monitoring.html">Read full tutorial →</a>
|
||||
</div>
|
||||
|
||||
<h2>Keyboard Shortcuts</h2>
|
||||
<table class="shortcut-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Shortcut</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><kbd>Ctrl+K</kbd> / <kbd>Cmd+K</kbd></td>
|
||||
<td>Open Quick Search</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><kbd>↑</kbd> / <kbd>↓</kbd></td>
|
||||
<td>Navigate in Quick Search</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><kbd>Enter</kbd></td>
|
||||
<td>Select in Quick Search</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><kbd>ESC</kbd></td>
|
||||
<td>Close modal / Cancel</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2>Common Tasks</h2>
|
||||
|
||||
<h3>Adding Your First App</h3>
|
||||
<blockquote>
|
||||
<strong>Note:</strong> App deployment requires the app catalog to be configured with your DashCaddy instance.
|
||||
</blockquote>
|
||||
<ol>
|
||||
<li>Navigate to <strong>TOOLS</strong> → <strong>App Selector</strong></li>
|
||||
<li>Browse available apps or use Quick Search</li>
|
||||
<li>Select an app template</li>
|
||||
<li>Configure the app settings (URL, subdomain, credentials)</li>
|
||||
<li>Deploy and verify</li>
|
||||
</ol>
|
||||
|
||||
<h3>Checking Service Health</h3>
|
||||
<ol>
|
||||
<li>Navigate to <strong>STATUS</strong> → <strong>Health Status</strong></li>
|
||||
<li>View the health overview for all services</li>
|
||||
<li>Check individual service status cards</li>
|
||||
<li>Review any active incidents</li>
|
||||
</ol>
|
||||
|
||||
<h3>Updating Services</h3>
|
||||
<ol>
|
||||
<li>Navigate to <strong>ADMIN</strong> → <strong>Updates</strong></li>
|
||||
<li>View available updates</li>
|
||||
<li>Review changelogs and release notes</li>
|
||||
<li>Apply updates to individual services or all at once</li>
|
||||
</ol>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Issue</th>
|
||||
<th>Solution</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Can't log in with TOTP</td>
|
||||
<td>Ensure your device clock is synchronized (TOTP is time-based). Try regenerating the code — they expire every 30 seconds.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Dashboard shows no services</td>
|
||||
<td>Services need to be added via the App Selector. Check that Docker is running on your server.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Logs show "Loading..."</td>
|
||||
<td>The service may not have generated any logs yet. Check that the service is actually running.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Theme changes not saving</td>
|
||||
<td>Click "Save Theme" before closing the Theme Builder. Try a different browser if issues persist.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2>Next Steps</h2>
|
||||
<ul>
|
||||
<li>Complete the initial setup wizard</li>
|
||||
<li>Add your first service via App Selector</li>
|
||||
<li>Customize the theme to your liking</li>
|
||||
<li>Set up automated backups</li>
|
||||
<li>Explore the Admin section for advanced settings</li>
|
||||
<li>Join the community for tips and support</li>
|
||||
</ul>
|
||||
|
||||
<footer>
|
||||
<p>This guide was created for DashCaddy version as deployed on test.dashcaddy.net</p>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,175 @@
|
||||
# DashCaddy Getting Started Guide
|
||||
|
||||
A practical guide to getting started with DashCaddy — the self-hosted app dashboard.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Getting Started](#getting-started) — First-time setup and configuration
|
||||
2. [Theme Customization](#theme-customization) — Customize the dashboard appearance
|
||||
3. [Viewing Logs](#viewing-logs) — Monitor service logs in real-time
|
||||
4. [Quick Search](#quick-search) — Fast access to services and settings
|
||||
5. [Backup & Restore](#backup--restore) — Protect your configuration
|
||||
6. [Monitoring Resources](#monitoring-resources) — Track CPU, memory, and disk usage
|
||||
|
||||
---
|
||||
|
||||
## Getting Started
|
||||
|
||||
Learn how to log in, configure your timezone, and choose a deployment mode.
|
||||
|
||||
[Read full tutorial](tutorials/01-getting-started.md)
|
||||
|
||||
**What you'll learn:**
|
||||
- How to log in with TOTP authentication
|
||||
- How to select your timezone
|
||||
- How to choose a deployment mode (Simple, Home Lab, Public, Custom)
|
||||
- How to complete or skip the initial setup wizard
|
||||
|
||||
---
|
||||
|
||||
## Theme Customization
|
||||
|
||||
Make DashCaddy your own with custom themes and colors.
|
||||
|
||||
[Read full tutorial](tutorials/02-theme-customization.md)
|
||||
|
||||
**What you'll learn:**
|
||||
- How to open the Theme Builder
|
||||
- How to use preset themes (Dark, Light, Nord, Dracula, etc.)
|
||||
- How to customize colors for backgrounds, text, accents, and status indicators
|
||||
- How to save, import, and export themes
|
||||
|
||||
---
|
||||
|
||||
## Viewing Logs
|
||||
|
||||
Monitor your services with real-time log streaming.
|
||||
|
||||
[Read full tutorial](tutorials/03-viewing-logs.md)
|
||||
|
||||
**What you'll learn:**
|
||||
- How to access the logs viewer
|
||||
- How to navigate and filter log entries
|
||||
- How to use live log streaming
|
||||
- How to control the number of displayed log lines
|
||||
|
||||
---
|
||||
|
||||
## Quick Search
|
||||
|
||||
Find services and settings instantly.
|
||||
|
||||
[Read full tutorial](tutorials/04-quick-search.md)
|
||||
|
||||
**What you'll learn:**
|
||||
- How to open Quick Search (Ctrl+K / Cmd+K)
|
||||
- How to search for services and settings
|
||||
- How to navigate and select results with keyboard shortcuts
|
||||
- Keyboard shortcuts: ESC, ↑↓ Navigate, Enter Select
|
||||
|
||||
---
|
||||
|
||||
## Backup & Restore
|
||||
|
||||
Protect your configuration with full backups.
|
||||
|
||||
[Read full tutorial](tutorials/05-backup-restore.md)
|
||||
|
||||
**What you'll learn:**
|
||||
- How to create a manual backup
|
||||
- What's included in a full backup (services, credentials, themes)
|
||||
- How to restore from a backup file
|
||||
- How to set up automated backups
|
||||
- How to view backup history
|
||||
|
||||
---
|
||||
|
||||
## Monitoring Resources
|
||||
|
||||
Track CPU, memory, network, and disk usage in real-time.
|
||||
|
||||
[Read full tutorial](tutorials/06-stats-monitoring.md)
|
||||
|
||||
**What you'll learn:**
|
||||
- How to access the Resource Monitor
|
||||
- How to interpret live stats and historical summaries
|
||||
- How to set up alerts for resource thresholds
|
||||
- Understanding CPU, memory, network, and disk metrics
|
||||
|
||||
---
|
||||
|
||||
## Common Tasks
|
||||
|
||||
### Adding Your First App
|
||||
|
||||
> **Note:** App deployment requires the app catalog to be configured with your DashCaddy instance.
|
||||
|
||||
1. Navigate to **TOOLS** → **App Selector**
|
||||
2. Browse available apps or use Quick Search
|
||||
3. Select an app template
|
||||
4. Configure the app settings (URL, subdomain, credentials)
|
||||
5. Deploy and verify
|
||||
|
||||
### Checking Service Health
|
||||
|
||||
1. Navigate to **STATUS** → **Health Status**
|
||||
2. View the health overview for all services
|
||||
3. Check individual service status cards
|
||||
4. Review any active incidents
|
||||
|
||||
### Updating Services
|
||||
|
||||
1. Navigate to **ADMIN** → **Updates**
|
||||
2. View available updates
|
||||
3. Review changelogs and release notes
|
||||
4. Apply updates to individual services or all at once
|
||||
|
||||
---
|
||||
|
||||
## Keyboard Shortcuts
|
||||
|
||||
| Shortcut | Action |
|
||||
|----------|--------|
|
||||
| `Ctrl+K` / `Cmd+K` | Open Quick Search |
|
||||
| `↑` / `↓` | Navigate in Quick Search |
|
||||
| `Enter` | Select in Quick Search |
|
||||
| `ESC` | Close modal / Cancel |
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Can't log in with TOTP
|
||||
- Ensure your device clock is synchronized (TOTP is time-based)
|
||||
- Try regenerating the code — they expire every 30 seconds
|
||||
- Contact your administrator if the code is consistently rejected
|
||||
|
||||
### Dashboard shows no services
|
||||
- Services need to be added via the App Selector
|
||||
- Check that Docker is running on your server
|
||||
- Verify network connectivity to your services
|
||||
|
||||
### Logs show "Loading..."
|
||||
- The service may not have generated any logs yet
|
||||
- Check that the service is actually running
|
||||
- Verify the logging system is configured correctly
|
||||
|
||||
### Theme changes not saving
|
||||
- Click "Save Theme" before closing the Theme Builder
|
||||
- Check browser console for JavaScript errors
|
||||
- Try a different browser if issues persist
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [ ] Complete the initial setup wizard
|
||||
- [ ] Add your first service via App Selector
|
||||
- [ ] Customize the theme to your liking
|
||||
- [ ] Set up automated backups
|
||||
- [ ] Explore the Admin section for advanced settings
|
||||
- [ ] Join the community for tips and support
|
||||
|
||||
---
|
||||
|
||||
*This guide was created for DashCaddy version as deployed on test.dashcaddy.net*
|
||||
@@ -0,0 +1,69 @@
|
||||
# Issues and Notes
|
||||
|
||||
## Test Instance Observations
|
||||
|
||||
### App Selector Empty
|
||||
The App Selector modal opens but shows no apps in the grid. The "Choose an App" message appears with just a Cancel button. This could mean:
|
||||
- The test instance doesn't have app templates loaded
|
||||
- The app catalog API isn't responding
|
||||
- The apps need to be fetched from a backend that's not configured
|
||||
|
||||
**Impact:** Could not complete "Deploy Your First App" workflow
|
||||
|
||||
**Workaround:** App selector needs backend/app catalog to be configured
|
||||
|
||||
### Menu Navigation Issues
|
||||
The TOOLS, STATUS, and ADMIN sections in the dashboard have expandable menus. Clicking on section headers (like "TOOLS") didn't expand the menu via standard click methods in headless Chrome.
|
||||
|
||||
**Impact:** Could not navigate to some features through normal menu interaction
|
||||
|
||||
**Workaround:** Used JavaScript to directly show modals by manipulating CSS display property
|
||||
|
||||
### Theme Switching Not Working Properly
|
||||
The "🎨 Light" button in the top bar appears to be a display of current theme rather than a toggle. Clicking it doesn't switch themes.
|
||||
|
||||
**Impact:** Theme workflow only partially captured - theme builder modal opens but theme switching via the quick button doesn't work
|
||||
|
||||
**Workaround:** Use Theme Builder modal to change themes
|
||||
|
||||
### Logs/Services Show "Loading..."
|
||||
Some services show "Loading logs..." or "Loading container stats..." which suggests the backend APIs aren't returning data in the test instance.
|
||||
|
||||
**Impact:** Could see the UI for these features but not actual data
|
||||
|
||||
**Workaround:** Document UI even when no data is present
|
||||
|
||||
## Technical Observations
|
||||
|
||||
### Puppeteer/Chromium Issues
|
||||
- Background Chromium processes were being killed by SIGKILL
|
||||
- Some clickable elements weren't being recognized as clickable by puppeteer
|
||||
- Had to use JavaScript clicks (`element.click()` via `page.evaluate()`) for some interactions
|
||||
|
||||
### Session Management
|
||||
- TOTP codes expire every 30 seconds
|
||||
- Had to generate fresh TOTP for each session
|
||||
- Used `process.exit(0)` to avoid graceful cleanup issues that triggered process kills
|
||||
|
||||
## Screenshots Captured
|
||||
|
||||
### Workflows Documented:
|
||||
1. **Getting Started** - Setup wizard, timezone, deployment mode (partial)
|
||||
2. **Theme Customization** - Theme builder modal, presets (partial)
|
||||
3. **Viewing Logs** - Complete workflow
|
||||
4. **Quick Search** - Complete workflow
|
||||
5. **Backup & Restore** - Complete workflow (UI)
|
||||
6. **Stats Monitoring** - Complete workflow (UI)
|
||||
|
||||
### Workflows Not Fully Documented:
|
||||
1. **Deploy First App** - App selector empty, couldn't complete
|
||||
2. **Dark Theme Toggle** - Theme builder works but quick toggle doesn't
|
||||
3. **Service Status Cards** - Not deeply explored
|
||||
4. **Smart Arr Setup** - Not explored
|
||||
|
||||
## Recommendations
|
||||
|
||||
1. App Selector needs app catalog/backend to show templates
|
||||
2. Menu expansion should be tested with real user interaction
|
||||
3. Test instance should have some sample services running to show actual data in logs/stats
|
||||
4. Consider adding a "demo mode" with fake data for tutorial purposes
|
||||
|
After Width: | Height: | Size: 166 KiB |
|
After Width: | Height: | Size: 166 KiB |
|
After Width: | Height: | Size: 159 KiB |
|
After Width: | Height: | Size: 159 KiB |
|
After Width: | Height: | Size: 160 KiB |
|
After Width: | Height: | Size: 166 KiB |
|
After Width: | Height: | Size: 166 KiB |
|
After Width: | Height: | Size: 166 KiB |
|
After Width: | Height: | Size: 166 KiB |
|
After Width: | Height: | Size: 166 KiB |
|
After Width: | Height: | Size: 176 KiB |
|
After Width: | Height: | Size: 176 KiB |
|
After Width: | Height: | Size: 175 KiB |
|
After Width: | Height: | Size: 176 KiB |
|
After Width: | Height: | Size: 176 KiB |
|
After Width: | Height: | Size: 174 KiB |
|
After Width: | Height: | Size: 135 KiB |
|
After Width: | Height: | Size: 174 KiB |
|
After Width: | Height: | Size: 174 KiB |
|
After Width: | Height: | Size: 175 KiB |
|
After Width: | Height: | Size: 130 KiB |
|
After Width: | Height: | Size: 128 KiB |
|
After Width: | Height: | Size: 174 KiB |
|
After Width: | Height: | Size: 136 KiB |
|
After Width: | Height: | Size: 94 KiB |
|
After Width: | Height: | Size: 174 KiB |
|
After Width: | Height: | Size: 140 KiB |
|
After Width: | Height: | Size: 175 KiB |
|
After Width: | Height: | Size: 166 KiB |
|
After Width: | Height: | Size: 166 KiB |
|
After Width: | Height: | Size: 86 KiB |
@@ -1,14 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Regenerate the /docs/catalog tree from the current
|
||||
# /opt/dashcaddy/dashcaddy-api/src/docker/app-templates.js.
|
||||
#
|
||||
# Run this whenever templates are added/changed. Outputs 78 static pages
|
||||
# under src/app/docs/catalog/ (1 index + 77 per-template).
|
||||
#
|
||||
# Idempotent — overwrites in place.
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
node /tmp/generate-template-docs.js
|
||||
echo "Verifying build..."
|
||||
npx --no-install next build 2>&1 | tail -3
|
||||
echo "Done. Review with: git diff --stat src/app/docs/catalog/"
|
||||
@@ -18,7 +18,7 @@ export default function AboutPage() {
|
||||
<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.
|
||||
built the tool we wished existed.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
@@ -45,16 +45,14 @@ export default function AboutPage() {
|
||||
<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
|
||||
A beautiful dashboard to monitor everything. 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.
|
||||
beautiful and effortless.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -69,10 +67,10 @@ export default function AboutPage() {
|
||||
<div className="grid md:grid-cols-3 gap-8">
|
||||
{[
|
||||
{
|
||||
icon: "🔒",
|
||||
title: "Proprietary & Polished",
|
||||
icon: "🔓",
|
||||
title: "Open Core",
|
||||
description:
|
||||
"DashCaddy is proprietary software built with care. The core platform is free to use, with Premium features for advanced orchestration.",
|
||||
"The core of DashCaddy is free and always will be. Premium features fund development, but the essentials are open to everyone.",
|
||||
},
|
||||
{
|
||||
icon: "🏠",
|
||||
@@ -81,10 +79,10 @@ export default function AboutPage() {
|
||||
"DashCaddy runs entirely on your hardware. No cloud dependency, no telemetry, no phoning home. Your data never leaves your network.",
|
||||
},
|
||||
{
|
||||
icon: "🤖",
|
||||
title: "AI-Native",
|
||||
icon: "🛠️",
|
||||
title: "Built to Last",
|
||||
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.",
|
||||
"We use proven technologies — Caddy, Docker, Node.js. No bleeding-edge frameworks that break every six months. Stable, reliable, boring (in the best way).",
|
||||
},
|
||||
].map((value) => (
|
||||
<div
|
||||
@@ -114,10 +112,6 @@ export default function AboutPage() {
|
||||
{ name: "Docker", role: "Container Runtime", icon: "🐳" },
|
||||
{ name: "Node.js", role: "API Backend", icon: "🟢" },
|
||||
{ name: "Technitium", role: "DNS Server", icon: "🌐" },
|
||||
{ name: "Express", role: "REST API + WebSocket", icon: "⚡" },
|
||||
{ name: "MCP", role: "AI Protocol Layer", icon: "🤖" },
|
||||
{ name: "Stripe", role: "Payment Processing", icon: "💳" },
|
||||
{ name: "Jest", role: "Testing (2,500+ tests)", icon: "✅" },
|
||||
].map((tech) => (
|
||||
<div
|
||||
key={tech.name}
|
||||
@@ -132,36 +126,12 @@ export default function AboutPage() {
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Stats */}
|
||||
<section className="py-20 px-4 border-t border-surface-800">
|
||||
<div className="max-w-4xl mx-auto">
|
||||
<h2 className="text-2xl font-bold text-white mb-12 text-center">
|
||||
By the Numbers
|
||||
</h2>
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-8">
|
||||
{[
|
||||
{ value: "77", label: "App Templates" },
|
||||
{ value: "2,500+", label: "Test Cases" },
|
||||
{ value: "350+", label: "API Endpoints" },
|
||||
{ value: "31", label: "Languages" },
|
||||
].map((stat) => (
|
||||
<div key={stat.label} className="text-center">
|
||||
<div className="text-4xl font-bold text-brand-400 mb-2">
|
||||
{stat.value}
|
||||
</div>
|
||||
<p className="text-sm text-surface-400">{stat.label}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Contact / Support */}
|
||||
<section className="py-20 px-4 border-t border-surface-800">
|
||||
<div className="max-w-3xl mx-auto text-center">
|
||||
<h2 className="text-2xl font-bold text-white mb-6">Get In Touch</h2>
|
||||
<p className="text-surface-300 mb-8">
|
||||
Have questions or feedback? We'd love to hear from you.
|
||||
Have questions, feedback, or want to contribute? We'd love to hear from you.
|
||||
</p>
|
||||
<div className="flex flex-col sm:flex-row gap-4 justify-center">
|
||||
<a
|
||||
@@ -170,12 +140,22 @@ export default function AboutPage() {
|
||||
>
|
||||
Email Us
|
||||
</a>
|
||||
<Link
|
||||
href="/docs"
|
||||
<a
|
||||
href="https://discord.gg/dashcaddy"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
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>
|
||||
Join Discord
|
||||
</a>
|
||||
<a
|
||||
href="https://github.com/dashcaddy"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="px-6 py-3 rounded-lg border border-surface-700 hover:border-surface-500 text-surface-300 font-medium transition-colors"
|
||||
>
|
||||
GitHub
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -187,7 +167,7 @@ export default function AboutPage() {
|
||||
Ready to simplify your homelab?
|
||||
</h2>
|
||||
<p className="text-surface-300 mb-8">
|
||||
Install DashCaddy and start deploying services today. Free forever.
|
||||
Start with the free tier. Upgrade when you're ready.
|
||||
</p>
|
||||
<Link
|
||||
href="/pricing"
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
return NextResponse.json({ error: 'Checkout disabled for static export' }, { status: 503 });
|
||||
export async function POST() {
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: "Website-local checkout is disabled. Use the external DashCaddy license service."
|
||||
},
|
||||
{ status: 410 }
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
return NextResponse.json({ error: 'Webhooks disabled for static export' }, { status: 503 });
|
||||
}
|
||||
@@ -1,4 +1,10 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
export async function POST(request: NextRequest) {
|
||||
return NextResponse.json({ error: 'disabled' }, { status: 503 });
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
export async function POST() {
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: "Website-local Stripe webhooks are disabled. Use the external DashCaddy license service."
|
||||
},
|
||||
{ status: 410 }
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
import Link from 'next/link';
|
||||
import Image from 'next/image';
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
|
||||
interface TutorialStep {
|
||||
title: string;
|
||||
body: string;
|
||||
screenshot?: string;
|
||||
screenshotAlt?: string;
|
||||
}
|
||||
|
||||
interface TroubleshootingItem {
|
||||
issue: string;
|
||||
solution: string;
|
||||
}
|
||||
|
||||
interface TutorialLayoutProps {
|
||||
title: string;
|
||||
description: string;
|
||||
icon: string;
|
||||
heroScreenshot?: string;
|
||||
steps: TutorialStep[];
|
||||
troubleshooting?: TroubleshootingItem[];
|
||||
prevTutorial?: { slug: string; title: string };
|
||||
nextTutorial?: { slug: string; title: string };
|
||||
}
|
||||
|
||||
export default function TutorialLayout({
|
||||
title,
|
||||
description,
|
||||
icon,
|
||||
heroScreenshot,
|
||||
steps,
|
||||
troubleshooting,
|
||||
prevTutorial,
|
||||
nextTutorial,
|
||||
}: TutorialLayoutProps) {
|
||||
return (
|
||||
<div className="flex flex-col min-h-screen bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
|
||||
{/* Hero */}
|
||||
<section className="relative py-12 sm:py-16">
|
||||
<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-64 bg-brand-500/10 rounded-full blur-3xl opacity-30" />
|
||||
</div>
|
||||
<div className="mx-auto max-w-4xl px-4 sm:px-6 lg:px-8">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<span className="text-3xl">{icon}</span>
|
||||
<span className="text-surface-400 text-sm">
|
||||
<Link href="/docs" className="hover:text-brand-400 transition-colors">Docs</Link>
|
||||
{' / '}
|
||||
<span className="text-surface-300">{title}</span>
|
||||
</span>
|
||||
</div>
|
||||
<h1 className="text-3xl sm:text-4xl font-bold mb-4">{title}</h1>
|
||||
<p className="text-lg text-surface-300 max-w-2xl">{description}</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Hero Screenshot */}
|
||||
{heroScreenshot && (
|
||||
<section className="py-4">
|
||||
<div className="mx-auto max-w-4xl px-4 sm:px-6 lg:px-8">
|
||||
<div className="rounded-xl overflow-hidden border border-surface-700/50">
|
||||
<Image
|
||||
src={heroScreenshot}
|
||||
alt={title}
|
||||
width={1200}
|
||||
height={675}
|
||||
className="w-full h-auto"
|
||||
unoptimized
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{/* Steps */}
|
||||
<section className="py-8">
|
||||
<div className="mx-auto max-w-4xl px-4 sm:px-6 lg:px-8">
|
||||
<div className="space-y-12">
|
||||
{steps.map((step, i) => (
|
||||
<div key={i} className="flex flex-col sm:flex-row gap-6">
|
||||
<div className="flex-shrink-0 flex items-start gap-4 sm:flex-col sm:items-center sm:w-12">
|
||||
<div className="flex-shrink-0 w-8 h-8 rounded-full bg-brand-500/20 text-brand-400 flex items-center justify-center font-bold text-sm sm:w-10 sm:h-10 sm:text-base">
|
||||
{i + 1}
|
||||
</div>
|
||||
{i < steps.length - 1 && (
|
||||
<div className="hidden sm:block w-px flex-1 bg-surface-700/50 min-h-[2rem]" />
|
||||
)}
|
||||
</div>
|
||||
<div className="flex-1 pb-8">
|
||||
<h2 className="text-xl font-semibold text-surface-50 mb-3">{step.title}</h2>
|
||||
<p className="text-surface-300 leading-relaxed">{step.body}</p>
|
||||
{step.screenshot && (
|
||||
<div className="mt-4 rounded-lg overflow-hidden border border-surface-700/50">
|
||||
<Image
|
||||
src={step.screenshot}
|
||||
alt={step.screenshotAlt || step.title}
|
||||
width={900}
|
||||
height={506}
|
||||
className="w-full h-auto"
|
||||
unoptimized
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Troubleshooting */}
|
||||
{troubleshooting && troubleshooting.length > 0 && (
|
||||
<section className="py-12 bg-surface-900/30">
|
||||
<div className="mx-auto max-w-4xl px-4 sm:px-6 lg:px-8">
|
||||
<h2 className="text-2xl font-bold text-surface-50 mb-6">Common Issues</h2>
|
||||
<div className="space-y-4">
|
||||
{troubleshooting.map(({ issue, solution }) => (
|
||||
<div key={issue} className="p-5 rounded-lg border border-surface-700/40 bg-surface-800/20">
|
||||
<div className="font-semibold text-surface-50 mb-2">{issue}</div>
|
||||
<div className="text-surface-300 text-sm">{solution}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{/* Navigation */}
|
||||
<section className="py-8">
|
||||
<div className="mx-auto max-w-4xl px-4 sm:px-6 lg:px-8">
|
||||
<div className="flex justify-between gap-4">
|
||||
{prevTutorial ? (
|
||||
<Link
|
||||
href={`/docs/${prevTutorial.slug}`}
|
||||
className="flex items-center gap-2 px-4 py-3 rounded-lg border border-surface-700/50 bg-surface-800/30 hover:border-brand-500/50 transition-colors text-surface-300 hover:text-brand-400"
|
||||
>
|
||||
<span>←</span>
|
||||
<span>{prevTutorial.title}</span>
|
||||
</Link>
|
||||
) : (
|
||||
<div />
|
||||
)}
|
||||
{nextTutorial ? (
|
||||
<Link
|
||||
href={`/docs/${nextTutorial.slug}`}
|
||||
className="flex items-center gap-2 px-4 py-3 rounded-lg border border-surface-700/50 bg-surface-800/30 hover:border-brand-500/50 transition-colors text-surface-300 hover:text-brand-400"
|
||||
>
|
||||
<span>{nextTutorial.title}</span>
|
||||
<span>→</span>
|
||||
</Link>
|
||||
) : (
|
||||
<div />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Back to Docs */}
|
||||
<section className="pb-12">
|
||||
<div className="mx-auto max-w-4xl px-4 sm:px-6 lg:px-8">
|
||||
<Link
|
||||
href="/docs"
|
||||
className="text-brand-400 hover:text-brand-300 transition-colors text-sm"
|
||||
>
|
||||
← Back to all documentation
|
||||
</Link>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,344 +0,0 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export default function DocsApiPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="API and Automation"
|
||||
intro="DashCaddy is more than a dashboard — it exposes a real API and automation surface so you can drive deployments, DNS, proxy, certificates, monitoring, and operations programmatically or through AI."
|
||||
>
|
||||
<p>
|
||||
Every action available in the DashCaddy UI is also available through a programmatic surface: a versioned REST
|
||||
API, a JavaScript automation layer, an AI Intent Router for natural-language commands, an MCP Server for AI assistant
|
||||
integration, a WebSocket channel for real-time events, a Prometheus endpoint for metrics, and a plugin system
|
||||
for extending the platform. This guide covers each surface with concrete examples.
|
||||
</p>
|
||||
<p>
|
||||
Whether you are wiring DashCaddy into a CI/CD pipeline, building a custom dashboard, or letting an AI assistant
|
||||
manage your infrastructure, the automation layer is designed to be the primary interface — the web UI is just
|
||||
one consumer of it.
|
||||
</p>
|
||||
|
||||
<h2>REST API</h2>
|
||||
<p>
|
||||
All platform operations are available under <code>/api/v1/</code>. The API covers service management, app
|
||||
deployment, DNS automation, Caddy reverse-proxy integration, certificate workflows, health and status
|
||||
reporting, user and admin operations, backup/restore, and more. The repository ships with an OpenAPI definition
|
||||
so the public contract can mature into a full reference.
|
||||
</p>
|
||||
<p>
|
||||
Requests and responses are JSON. The base URL is your DashCaddy host — for example
|
||||
<code> https://dashcaddy-host/api/v1/services</code>. All endpoints require authentication (see below) and
|
||||
return structured error codes rather than opaque messages.
|
||||
</p>
|
||||
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`# List all services
|
||||
curl -H "Authorization: Bearer ***" \\
|
||||
https://dashcaddy-host/api/v1/services
|
||||
|
||||
# Deploy from a template
|
||||
curl -X POST -H "Authorization: Bearer ***" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{"template":"jellyfin","name":"media","hostname":"media.lab"}' \\
|
||||
https://dashcaddy-host/api/v1/services
|
||||
|
||||
# Restart a service
|
||||
curl -X POST -H "Authorization: Bearer ***" \\
|
||||
https://dashcaddy-host/api/v1/services/media/restart`}</code></pre>
|
||||
|
||||
<h2>Authentication</h2>
|
||||
<p>
|
||||
DashCaddy supports two authentication methods, chosen by how you access the API:
|
||||
</p>
|
||||
<h3>Session cookie (browser)</h3>
|
||||
<p>
|
||||
The web dashboard authenticates with a session cookie set after login (email magic link or username/password
|
||||
with optional TOTP 2FA). API calls made from the browser carry the cookie automatically. This is the right
|
||||
method for in-dashboard automation and userscripts.
|
||||
</p>
|
||||
<h3>API key (Bearer token)</h3>
|
||||
<p>
|
||||
For server-to-server automation, scripts, and integrations, use an API key. Generate keys from
|
||||
<strong> Settings → API Keys</strong>. Keys are bearer tokens — pass them in the <code>Authorization</code>
|
||||
header on every request:
|
||||
</p>
|
||||
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`Authorization: Bearer dc_live_xxxxxxxxxxxxxxxxxxxx`}</code></pre>
|
||||
<blockquote className="border-l-4 border-brand-500/50 bg-brand-500/5 p-4 rounded-r-lg">
|
||||
<p className="text-surface-300">
|
||||
<strong className="text-brand-400">Security:</strong> API keys grant the same permissions as the user who
|
||||
created them, scoped by RBAC role. Store keys in a secret manager — never commit them to source control.
|
||||
Rotate keys immediately if one is leaked.
|
||||
</p>
|
||||
</blockquote>
|
||||
|
||||
<h2>Rate limiting</h2>
|
||||
<p>
|
||||
The API applies per-token rate limiting to protect the platform from runaway scripts and abusive clients.
|
||||
Limits are generous for normal operation: interactive dashboard usage will never hit them. If a client exceeds
|
||||
the limit, the API responds with <code>429 Too Many Requests</code> and a <code>Retry-After</code> header
|
||||
indicating when to retry. Back off and retry — do not hammer the endpoint.
|
||||
</p>
|
||||
<p>
|
||||
For high-volume automation (e.g. polling service status in a tight loop), prefer the <strong>WebSocket
|
||||
channel</strong> or the <strong>Prometheus endpoint</strong> over repeated REST polling. Both are designed for
|
||||
frequent reads and do not count against the REST rate limit.
|
||||
</p>
|
||||
|
||||
<h2>JavaScript automation</h2>
|
||||
<p>
|
||||
For programmatic automation, use the REST API directly with <code>fetch</code> or any HTTP client. The API is
|
||||
JSON-based, uses Bearer token authentication, and returns structured error codes. Here is a minimal helper
|
||||
you can drop into any Node.js, Bun, or browser project:
|
||||
</p>
|
||||
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{['class DashCaddy {', '',
|
||||
' constructor(opts) {', '',
|
||||
' this.baseUrl = opts.baseUrl;', '',
|
||||
' this.token = opts.token;', '',
|
||||
' }', '',
|
||||
'', '',
|
||||
' async request(path, options) {', '',
|
||||
' options = options || {};', '',
|
||||
' var url = this.baseUrl + "/api/v1" + path;', '',
|
||||
' var res = await fetch(url, {', '',
|
||||
' method: options.method || "GET",', '',
|
||||
' body: options.body,', '',
|
||||
' headers: {', '',
|
||||
' "Content-Type": "application/json",', '',
|
||||
' "Authorization": "Bearer " + this.token', '',
|
||||
' }', '',
|
||||
' });', '',
|
||||
' var body = await res.json();', '',
|
||||
' if (!res.ok) throw { code: body.error, status: res.status };', '',
|
||||
' return body;', '',
|
||||
' }', '',
|
||||
'', '',
|
||||
' // List services', '',
|
||||
' services() { return this.request("/services"); }', '',
|
||||
'', '',
|
||||
' // Deploy from template', '',
|
||||
' deploy(template, name, hostname) {', '',
|
||||
' return this.request("/services", {', '',
|
||||
' method: "POST",', '',
|
||||
' body: JSON.stringify({ template, name, hostname })', '',
|
||||
' });', '',
|
||||
' }', '',
|
||||
'', '',
|
||||
' // Restart a service', '',
|
||||
' restart(id) {', '',
|
||||
' return this.request("/services/" + id + "/restart", { method: "POST" });', '',
|
||||
' }', '',
|
||||
'}'].join('\n')}</code></pre>
|
||||
<p>
|
||||
Every request returns a structured JSON response or throws an error object carrying the error
|
||||
code, HTTP status, and message — so your automation can branch on specific failure conditions.
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h2>AI Intent Router</h2>
|
||||
<p>
|
||||
The <strong>AI Intent Router</strong> accepts natural-language commands and translates them into real
|
||||
infrastructure actions through the same API. This turns ad-hoc operator requests (“restart the media
|
||||
server”, “is postgres up?”, “deploy redis”) into reproducible, logged operations
|
||||
— no need to remember endpoint paths or parameter names.
|
||||
</p>
|
||||
<p>
|
||||
The router parses intent, maps it to the correct API call, executes it, and returns both a human-readable
|
||||
summary and the raw API result. Every intent execution is recorded in the audit log just like a manual action.
|
||||
</p>
|
||||
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`# Natural-language operation
|
||||
POST /api/v1/ai/intent
|
||||
{
|
||||
"message": "Restart the media server and check its health"
|
||||
}
|
||||
|
||||
# Response
|
||||
{
|
||||
"summary": "Restarted 'media' and confirmed health: healthy",
|
||||
"actions": [
|
||||
{ "method": "POST", "path": "/api/v1/services/media/restart", "status": 200 },
|
||||
{ "method": "GET", "path": "/api/v1/services/media/health", "status": 200 }
|
||||
]
|
||||
}`}</code></pre>
|
||||
<p>
|
||||
Example intents: “deploy the postgres template as <code>db</code> on <code>db.lab</code>”,
|
||||
“list all unhealthy services”, “rotate the TLS cert for <code>wiki.lab</code>”,
|
||||
“create a DNS record for <code>api.lab</code> pointing at 10.0.0.5”.
|
||||
</p>
|
||||
|
||||
<h2>MCP Server</h2>
|
||||
<p>
|
||||
The built-in <strong>MCP (Model Context Protocol) Server</strong> exposes DashCaddy operations as tools that
|
||||
AI assistants and external automation can call directly. Connect your assistant to the MCP endpoint and it can
|
||||
list services, deploy templates, manage DNS, inspect health, and trigger operations — all through the standard
|
||||
MCP tool interface, with full audit logging.
|
||||
</p>
|
||||
<p>
|
||||
To connect Claude Desktop, GPT, or another MCP-compatible assistant, add the DashCaddy MCP server to your
|
||||
client's MCP configuration:
|
||||
</p>
|
||||
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`{
|
||||
"mcpServers": {
|
||||
"dashcaddy": {
|
||||
"url": "https://dashcaddy-host/mcp",
|
||||
"headers": {
|
||||
"Authorization": "Bearer dc_live_xxxxxxxxxxxxxxxxxxxx"
|
||||
}
|
||||
}
|
||||
}
|
||||
}`}</code></pre>
|
||||
<p>
|
||||
Once connected, the assistant discovers DashCaddy's tools automatically and can invoke them in response to
|
||||
your requests — “ask DashCaddy which services are down”, “have DashCaddy deploy Grafana”,
|
||||
etc. This is the most natural way to operate infrastructure through conversation.
|
||||
</p>
|
||||
|
||||
<h2>WebSocket real-time events</h2>
|
||||
<p>
|
||||
The dashboard subscribes to a <strong>WebSocket channel</strong> for live updates: service health changes,
|
||||
container starts and stops, deployment progress, DNS changes, and fleet events arrive in real time without
|
||||
polling. You can consume the same channel in your own dashboards, chatops bots, or automation.
|
||||
</p>
|
||||
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`const ws = new WebSocket('wss://dashcaddy-host/api/v1/events', {
|
||||
headers: { Authorization: 'Bearer ' + process.env.DC_TOKEN },
|
||||
});
|
||||
|
||||
ws.on('message', (data) => {
|
||||
const event = JSON.parse(data);
|
||||
console.log(event.type, event.payload);
|
||||
});`}</code></pre>
|
||||
<p>
|
||||
Common event types you will see on the channel:
|
||||
</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Event type</th>
|
||||
<th>Emitted when</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td><code>service.health</code></td><td>A service transitions between healthy / unhealthy / down</td></tr>
|
||||
<tr><td><code>service.started</code></td><td>A container starts successfully</td></tr>
|
||||
<tr><td><code>service.stopped</code></td><td>A container stops (graceful or crash)</td></tr>
|
||||
<tr><td><code>deploy.progress</code></td><td>A template deployment advances through its stages</td></tr>
|
||||
<tr><td><code>deploy.complete</code></td><td>A deployment finishes (success or failure)</td></tr>
|
||||
<tr><td><code>dns.changed</code></td><td>A DNS record is created, updated, or removed</td></tr>
|
||||
<tr><td><code>proxy.updated</code></td><td>A Caddy route is applied or removed</td></tr>
|
||||
<tr><td><code>cert.issued</code></td><td>A TLS certificate is issued or renewed</td></tr>
|
||||
<tr><td><code>fleet.host</code></td><td>A fleet host changes state (Premium)</td></tr>
|
||||
<tr><td><code>audit.event</code></td><td>A user or API action is logged for audit</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2>Prometheus metrics endpoint</h2>
|
||||
<p>
|
||||
DashCaddy exposes a Prometheus-format metrics endpoint at <code>/metrics</code> for service health, container
|
||||
status, request counts, certificate expiry, and system indicators. Scrape it with Prometheus and visualize in
|
||||
Grafana. See <a href="/docs/integrations">Integrations</a> for a full scrape config.
|
||||
</p>
|
||||
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`# Scrape config (prometheus.yml)
|
||||
scrape_configs:
|
||||
- job_name: 'dashcaddy'
|
||||
metrics_path: /metrics
|
||||
static_configs:
|
||||
- targets: ['dashcaddy-host:3000']
|
||||
|
||||
# Sample exported metrics
|
||||
dashcaddy_service_health{service="media"} 1
|
||||
dashcaddy_container_running{container="db"} 1
|
||||
dashcaddy_http_requests_total{service="wiki",code="200"} 48213
|
||||
dashcaddy_cert_expiry_days{domain="media.lab"} 87`}</code></pre>
|
||||
|
||||
<h2>Health and readiness probes</h2>
|
||||
<p>
|
||||
Two lightweight probes let orchestrators and load balancers check DashCaddy itself:
|
||||
</p>
|
||||
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`# Liveness — is the process up?
|
||||
GET /healthz
|
||||
|
||||
# Readiness — can it serve (Docker, Caddy, DNS connected)?
|
||||
GET /readyz`}</code></pre>
|
||||
<p>
|
||||
Use <code>/healthz</code> for container restart policies and <code>/readyz</code> for traffic gating. If
|
||||
<code> /readyz</code> fails but <code>/healthz</code> passes, a dependency (Docker socket, Caddy Admin API, or
|
||||
Technitium DNS) is unreachable — see <a href="/docs/troubleshooting">Troubleshooting</a>.
|
||||
</p>
|
||||
|
||||
<h2>Plugin & extension system</h2>
|
||||
<p>
|
||||
DashCaddy includes a <strong>plugin/extension system</strong> with hooks into the deployment, DNS, proxy, and
|
||||
monitoring pipelines. Write extensions to react to service lifecycle events, inject custom Caddy directives,
|
||||
emit additional metrics, or integrate third-party tools — without forking the core.
|
||||
</p>
|
||||
<p>
|
||||
Plugins register for lifecycle hooks (e.g. <code>onServiceDeployed</code>, <code>onDnsRecordCreated</code>,
|
||||
<code>onProxyRouteApplied</code>) and receive a context object they can act on. A plugin can modify the
|
||||
generated Caddyfile before it is applied, push a notification when a service goes unhealthy, or export custom
|
||||
metrics alongside the built-in ones. Plugins are loaded at startup and run in the same process.
|
||||
</p>
|
||||
|
||||
<h2>Structured error codes</h2>
|
||||
<p>
|
||||
The API returns <strong>80 structured error codes</strong> across <strong>12 modules</strong> rather
|
||||
than opaque messages, so your automation can branch on specific failure conditions — DNS token invalid, Caddy
|
||||
unreachable, license expired, rate limited — instead of parsing strings. Every error response includes the
|
||||
machine-readable code, the HTTP status, and a human-readable message.
|
||||
</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Module</th>
|
||||
<th>Example error codes</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td>auth</td><td><code>AUTH_INVALID_TOKEN</code>, <code>AUTH_PERMISSION_DENIED</code>, <code>AUTH_2FA_REQUIRED</code></td></tr>
|
||||
<tr><td>service</td><td><code>SERVICE_NOT_FOUND</code>, <code>SERVICE_ALREADY_EXISTS</code>, <code>SERVICE_UNHEALTHY</code></td></tr>
|
||||
<tr><td>deploy</td><td><code>DEPLOY_TEMPLATE_INVALID</code>, <code>DEPLOY_PORT_CONFLICT</code>, <code>DEPLOY_FAILED</code></td></tr>
|
||||
<tr><td>dns</td><td><code>DNS_TOKEN_INVALID</code>, <code>DNS_ZONE_NOT_FOUND</code>, <code>DNS_RECORD_EXISTS</code></td></tr>
|
||||
<tr><td>proxy</td><td><code>PROXY_CADDY_UNREACHABLE</code>, <code>PROXY_CONFIG_INVALID</code>, <code>PROXY_UPSTREAM_TIMEOUT</code></td></tr>
|
||||
<tr><td>cert</td><td><code>CERT_ISSUANCE_FAILED</code>, <code>CERT_EXPIRED</code>, <code>CERT_NOT_TRUSTED</code></td></tr>
|
||||
<tr><td>license</td><td><code>LICENSE_EXPIRED</code>, <code>LICENSE_INVALID</code>, <code>LICENSE_MACHINE_LIMIT</code></td></tr>
|
||||
<tr><td>user</td><td><code>USER_NOT_FOUND</code>, <code>USER_ALREADY_EXISTS</code>, <code>USER_INVITE_EXPIRED</code></td></tr>
|
||||
<tr><td>backup</td><td><code>BACKUP_FAILED</code>, <code>BACKUP_CORRUPT</code>, <code>RESTORE_CONFLICT</code></td></tr>
|
||||
<tr><td>recipe</td><td><code>RECIPE_INVALID</code>, <code>RECIPE_COMPONENT_FAILED</code> (Premium)</td></tr>
|
||||
<tr><td>swarm</td><td><code>SWARM_NOT_INITIALIZED</code>, <code>SWARM_NODE_UNREACHABLE</code> (Premium)</td></tr>
|
||||
<tr><td>fleet</td><td><code>FLEET_HOST_OFFLINE</code>, <code>FLEET_DEPLOY_PLAN_FAILED</code> (Premium)</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>
|
||||
Handle errors by code in your automation:
|
||||
</p>
|
||||
<pre className="mt-4 overflow-x-auto rounded-lg border border-surface-700/50 bg-surface-950/80 p-4 text-sm"><code>{`try {
|
||||
await dc.services.deploy({ template: 'postgres', name: 'db', hostname: 'db.lab' });
|
||||
} catch (err) {
|
||||
if (err.code === 'DEPLOY_PORT_CONFLICT') {
|
||||
// pick a different port and retry
|
||||
} else if (err.code === 'LICENSE_EXPIRED') {
|
||||
// alert ops to renew
|
||||
} else {
|
||||
throw err; // unknown — surface to the operator
|
||||
}
|
||||
}`}</code></pre>
|
||||
|
||||
<h2>Why automation matters</h2>
|
||||
<p>
|
||||
DashCaddy can execute the full infrastructure chain around a service, not just report its state after the fact.
|
||||
Between the REST API, the AI Intent Router, MCP, WebSockets, Prometheus, and the plugin system, you
|
||||
have every surface you need to make DashCaddy a first-class citizen of your automation stack. Start with a
|
||||
simple <code>curl</code> call, and add AI and event-driven flows as your needs grow.
|
||||
</p>
|
||||
<p>
|
||||
For the infrastructure that backs all of this, see <a href="/docs/integrations">Integrations</a>. When things go
|
||||
wrong, the <a href="/docs/troubleshooting">Troubleshooting</a> guide walks each layer with commands and fixes.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
import TutorialLayout from '../_components/TutorialLayout';
|
||||
|
||||
const backupRestore = {
|
||||
title: 'Backup & Restore',
|
||||
icon: '💾',
|
||||
description: 'Protect your configuration with full backups. Export and restore services, credentials, themes, and all settings in one file.',
|
||||
heroScreenshot: '/tutorials/screenshots/06-backup-restore/light/02-backup-modal-open.png',
|
||||
steps: [
|
||||
{
|
||||
title: 'Access the Backup Modal',
|
||||
body: 'The Backup feature is found in the TOOLS section of the dashboard. Navigate to TOOLS and click on Backup to open the Backup & Restore modal.',
|
||||
screenshot: '/tutorials/screenshots/06-backup-restore/light/01-dashboard.png',
|
||||
},
|
||||
{
|
||||
title: 'The Backup Modal Interface',
|
||||
body: 'The Backup modal opens with three tabs: Manual, Automated, and History. The modal displays a title "💾 Backup & Restore" and a description: "Full backup of your entire DashCaddy setup — server config, credentials, themes, and browser preferences in one file."',
|
||||
screenshot: '/tutorials/screenshots/06-backup-restore/light/02-backup-modal-open.png',
|
||||
},
|
||||
{
|
||||
title: 'Create a Manual Backup',
|
||||
body: 'The Manual tab is selected by default. Click "⬇️ Download Full Backup" to download a complete backup of your configuration. The backup file includes all service configurations, Caddy reverse proxy configuration, encrypted credentials, encryption keys, custom themes, and browser preferences. Save this file in a safe location.',
|
||||
},
|
||||
{
|
||||
title: 'Restore from Backup',
|
||||
body: 'The 📥 Restore Backup section lets you restore from a previously saved backup file. Click the upload/restore button, select your backup file from your computer, and confirm the restore operation. Warning: restoring a backup will replace your current configuration, so export your current config first if needed.',
|
||||
},
|
||||
{
|
||||
title: 'Set Up Automated Backups',
|
||||
body: 'The Automated tab lets you schedule regular backups so you always have recent restore points. Configure the backup frequency (daily, weekly, etc.) and retention settings. Automated backups ensure you can recover from configuration errors without losing recent changes.',
|
||||
},
|
||||
],
|
||||
troubleshooting: [
|
||||
{
|
||||
issue: 'Download button not working',
|
||||
solution: 'Check browser popup blocker settings. Try a different browser if downloads aren\'t working.',
|
||||
},
|
||||
{
|
||||
issue: 'Restore fails',
|
||||
solution: 'Verify the backup file is complete and not corrupted. Check that the file was created on the same DashCaddy version.',
|
||||
},
|
||||
{
|
||||
issue: 'Forgotten encryption key',
|
||||
solution: 'Without the encryption key from backup, some credentials may not restore. Always keep backup files and encryption keys together.',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default function BackupRestorePage() {
|
||||
return (
|
||||
<TutorialLayout
|
||||
title={backupRestore.title}
|
||||
description={backupRestore.description}
|
||||
icon={backupRestore.icon}
|
||||
heroScreenshot={backupRestore.heroScreenshot}
|
||||
steps={backupRestore.steps}
|
||||
troubleshooting={backupRestore.troubleshooting}
|
||||
prevTutorial={{ slug: 'quick-search', title: 'Quick Search' }}
|
||||
nextTutorial={{ slug: 'stats-monitoring', title: 'Stats Monitoring' }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Actual Budget — DashCaddy Docs',
|
||||
description: 'Install and configure Actual Budget via DashCaddy. Privacy-focused budgeting app with envelope budgeting',
|
||||
};
|
||||
|
||||
export default function actualBudgetDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Actual Budget"
|
||||
intro="Privacy-focused budgeting app with envelope budgeting"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Productivity</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">actualbudget/actual-server:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Actual Budget?</h2>
|
||||
<p>Privacy-focused budgeting app with envelope budgeting</p>
|
||||
<p>Actual Budget ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Actual Budget, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Actual Budget</strong> from the Productivity category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>budget</code>), host port (default: <code>5006</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "actual-budget",
|
||||
"config": {
|
||||
"subdomain": "budget",
|
||||
"port": 5006
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Actual Budget on my home host and expose it at budget.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull actualbudget/actual-server:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Create your first budget in the web interface</li>
|
||||
<li>Import transactions from your bank (OFX, QFX, CSV)</li>
|
||||
<li>Set up envelope categories for spending control</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/actual-budget/data:/data</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<p>None. The container runs with its upstream defaults.</p><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull actualbudget/actual-server:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Actual Budget:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>actual-budget</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Adminer — DashCaddy Docs',
|
||||
description: 'Install and configure Adminer via DashCaddy. Lightweight database management in single PHP file',
|
||||
};
|
||||
|
||||
export default function adminerDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Adminer"
|
||||
intro="Lightweight database management in single PHP file"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Database</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">adminer:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Adminer?</h2>
|
||||
<p>Lightweight database management in single PHP file</p>
|
||||
<p>Adminer ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Adminer, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Adminer</strong> from the Database category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>adminer</code>), host port (default: <code>8087</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "adminer",
|
||||
"config": {
|
||||
"subdomain": "adminer",
|
||||
"port": 8087
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Adminer on my home host and expose it at adminer.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull adminer:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Connect to your database servers</li>
|
||||
<li>Supports MySQL, PostgreSQL, SQLite, etc.</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/adminer:/var/www/html</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>ADMINER_DEFAULT_SERVER</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull adminer:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Adminer:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>adminer</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Airsonic Advanced — DashCaddy Docs',
|
||||
description: 'Install and configure Airsonic Advanced via DashCaddy. Free web-based media streamer',
|
||||
};
|
||||
|
||||
export default function airsonicDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Airsonic Advanced"
|
||||
intro="Free web-based media streamer"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Media</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">linuxserver/airsonic-advanced:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Airsonic Advanced?</h2>
|
||||
<p>Free web-based media streamer</p>
|
||||
<p>Airsonic Advanced ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Airsonic Advanced, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Airsonic Advanced</strong> from the Media category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>airsonic</code>), host port (default: <code>4040</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "airsonic",
|
||||
"config": {
|
||||
"subdomain": "airsonic",
|
||||
"port": 4040
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Airsonic Advanced on my home host and expose it at airsonic.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull linuxserver/airsonic-advanced:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Default login: admin/admin</li>
|
||||
<li>Configure media folders</li>
|
||||
<li>Set up transcoding</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/airsonic/config:/config</code></li>
|
||||
<li><code>/music:/music</code></li>
|
||||
<li><code>/podcasts:/podcasts</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>PUID</code></li>
|
||||
<li><code>PGID</code></li>
|
||||
<li><code>TZ</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull linuxserver/airsonic-advanced:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Airsonic Advanced:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>airsonic</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Audiobookshelf — DashCaddy Docs',
|
||||
description: 'Install and configure Audiobookshelf via DashCaddy. Self-hosted audiobook and podcast server',
|
||||
};
|
||||
|
||||
export default function audiobookshelfDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Audiobookshelf"
|
||||
intro="Self-hosted audiobook and podcast server"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Media</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">ghcr.io/advplyr/audiobookshelf:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Audiobookshelf?</h2>
|
||||
<p>Self-hosted audiobook and podcast server</p>
|
||||
<p>Audiobookshelf ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Audiobookshelf, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>A host path containing your media. Default suggestion: <code>/media/audiobooks</code>. The deploy form / API payload <code>config.mediaPath</code> must be readable by the container UID (usually <code>1000</code>).</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Audiobookshelf</strong> from the Media category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>audiobooks</code>), host port (default: <code>13378</code>), and the media library path.</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "audiobookshelf",
|
||||
"config": {
|
||||
"subdomain": "audiobooks",
|
||||
"port": 13378,
|
||||
"mediaPath": "/media/audiobooks"
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Audiobookshelf on my home host and expose it at audiobooks.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull ghcr.io/advplyr/audiobookshelf:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Create your account on first access</li>
|
||||
<li>Add your audiobook library folders</li>
|
||||
<li>Download the mobile app for offline listening</li>
|
||||
</ol>
|
||||
<h2>Media library path notes</h2>
|
||||
<p>The media mount path you pass as <code>mediaPath</code> in the deploy payload is mounted as <code>/audiobooks</code> inside the container. Bind a host directory containing your media library (movies, TV shows, music, etc.).</p>
|
||||
<ul>
|
||||
<li><strong>UID/GID:</strong> Audiobookshelf runs as a non-root user. If you see permission errors in the dashboard Logs tab, run <code>chown -R 1000:1000 /media/audiobooks</code> on the host.</li>
|
||||
<li><strong>Multi-library:</strong> bind the parent folder and let Audiobookshelf discover subfolders.</li>
|
||||
</ul>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/audiobookshelf/config:/config</code></li>
|
||||
<li><code>/opt/audiobookshelf/metadata:/metadata</code></li>
|
||||
<li><code>MEDIA_PATH:/audiobooks</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<p>None. The container runs with its upstream defaults.</p><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull ghcr.io/advplyr/audiobookshelf:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Audiobookshelf:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>Library shows empty:</strong> confirm <code>mediaPath</code> is readable by the container UID and that the directory contains the file extensions Audiobookshelf indexes.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>audiobookshelf</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Authentik — DashCaddy Docs',
|
||||
description: 'Install and configure Authentik via DashCaddy. Identity provider and single sign-on platform',
|
||||
};
|
||||
|
||||
export default function authentikDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Authentik"
|
||||
intro="Identity provider and single sign-on platform"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Security</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#f5a62322', color: '#f5a623' }}>Difficulty: Advanced</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">ghcr.io/goauthentik/server:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Authentik?</h2>
|
||||
<p>Identity provider and single sign-on platform</p>
|
||||
<p>Authentik ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Authentik, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Authentik</strong> from the Security category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>auth</code>), host port (default: <code>9010</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/-/health/live/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "authentik",
|
||||
"config": {
|
||||
"subdomain": "auth",
|
||||
"port": 9010
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Authentik on my home host and expose it at auth.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull ghcr.io/goauthentik/server:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Requires a PostgreSQL database and Redis instance</li>
|
||||
<li>Consider deploying via the Dev Environment recipe for full stack</li>
|
||||
<li>Set up flows for authentication, enrollment, and recovery</li>
|
||||
<li>Configure OAuth2/OIDC providers for SSO with other apps</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/authentik/media:/media</code></li>
|
||||
<li><code>/opt/authentik/templates:/templates</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>AUTHENTIK_SECRET_KEY</code></li>
|
||||
<li><code>AUTHENTIK_ERROR_REPORTING__ENABLED</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull ghcr.io/goauthentik/server:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Authentik:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/-/health/live/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>authentik</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Bazarr — DashCaddy Docs',
|
||||
description: 'Install and configure Bazarr via DashCaddy. Automatic subtitle downloader for Sonarr and Radarr',
|
||||
};
|
||||
|
||||
export default function bazarrDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Bazarr"
|
||||
intro="Automatic subtitle downloader for Sonarr and Radarr"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Media Management</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">linuxserver/bazarr:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Bazarr?</h2>
|
||||
<p>Automatic subtitle downloader for Sonarr and Radarr</p>
|
||||
<p>Bazarr ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Bazarr, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Bazarr</strong> from the Media Management category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>bazarr</code>), host port (default: <code>6767</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "bazarr",
|
||||
"config": {
|
||||
"subdomain": "bazarr",
|
||||
"port": 6767
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Bazarr on my home host and expose it at bazarr.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull linuxserver/bazarr:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Connect to Sonarr and Radarr</li>
|
||||
<li>Configure subtitle providers</li>
|
||||
<li>Set language preferences</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/bazarr/config:/config</code></li>
|
||||
<li><code>/movies:/movies</code></li>
|
||||
<li><code>/tv:/tv</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>PUID</code></li>
|
||||
<li><code>PGID</code></li>
|
||||
<li><code>TZ</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull linuxserver/bazarr:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Bazarr:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>bazarr</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install BIND9 DNS Server — DashCaddy Docs',
|
||||
description: 'Install and configure BIND9 DNS Server via DashCaddy. Industry-standard DNS server - powerful and flexible',
|
||||
};
|
||||
|
||||
export default function bind9DocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install BIND9 DNS Server"
|
||||
intro="Industry-standard DNS server - powerful and flexible"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: DNS</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#f5a62322', color: '#f5a623' }}>Difficulty: Advanced</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">ubuntu/bind9:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is BIND9 DNS Server?</h2>
|
||||
<p>Industry-standard DNS server - powerful and flexible</p>
|
||||
<p>BIND9 DNS Server ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using BIND9 DNS Server, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>BIND9 DNS Server</strong> from the DNS category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>dns2</code>), host port (default: <code>953</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>tcp://localhost:53</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "bind9",
|
||||
"config": {
|
||||
"subdomain": "dns2",
|
||||
"port": 953
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy BIND9 DNS Server on my home host and expose it at dns2.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull ubuntu/bind9:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Configure zone files in /opt/bind9/config/</li>
|
||||
<li>Create named.conf.local for your .sami zone</li>
|
||||
<li>Add zone file: /opt/bind9/records/db.sami</li>
|
||||
<li>Restart container to apply changes</li>
|
||||
<li>Test with: dig @localhost sami</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/bind9/config:/etc/bind</code></li>
|
||||
<li><code>/opt/bind9/cache:/var/cache/bind</code></li>
|
||||
<li><code>/opt/bind9/records:/var/lib/bind</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>BIND9_USER</code></li>
|
||||
<li><code>TZ</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull ubuntu/bind9:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with BIND9 DNS Server:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>tcp://localhost:53</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>bind9</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install BookStack — DashCaddy Docs',
|
||||
description: 'Install and configure BookStack via DashCaddy. Simple wiki and documentation platform',
|
||||
};
|
||||
|
||||
export default function bookstackDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install BookStack"
|
||||
intro="Simple wiki and documentation platform"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Productivity</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Difficulty: Intermediate</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">linuxserver/bookstack:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is BookStack?</h2>
|
||||
<p>Simple wiki and documentation platform</p>
|
||||
<p>BookStack ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using BookStack, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>BookStack</strong> from the Productivity category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>wiki</code>), host port (default: <code>8091</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "bookstack",
|
||||
"config": {
|
||||
"subdomain": "wiki",
|
||||
"port": 8091
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy BookStack on my home host and expose it at wiki.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull linuxserver/bookstack:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Requires MariaDB/MySQL database</li>
|
||||
<li>Default login: admin@admin.com / password</li>
|
||||
<li>Change default credentials</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/bookstack/config:/config</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>PUID</code></li>
|
||||
<li><code>PGID</code></li>
|
||||
<li><code>APP_URL</code></li>
|
||||
<li><code>DB_HOST</code></li>
|
||||
<li><code>DB_DATABASE</code></li>
|
||||
<li><code>DB_USERNAME</code></li>
|
||||
<li><code>DB_PASSWORD</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull linuxserver/bookstack:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with BookStack:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>bookstack</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Calibre-Web — DashCaddy Docs',
|
||||
description: 'Install and configure Calibre-Web via DashCaddy. Web-based ebook manager and reader',
|
||||
};
|
||||
|
||||
export default function calibreWebDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Calibre-Web"
|
||||
intro="Web-based ebook manager and reader"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Media</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Difficulty: Intermediate</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">lscr.io/linuxserver/calibre-web:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Calibre-Web?</h2>
|
||||
<p>Web-based ebook manager and reader</p>
|
||||
<p>Calibre-Web ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Calibre-Web, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>A host path containing your media. Default suggestion: <code>/media/books</code>. The deploy form / API payload <code>config.mediaPath</code> must be readable by the container UID (usually <code>1000</code>).</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Calibre-Web</strong> from the Media category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>books</code>), host port (default: <code>8083</code>), and the media library path.</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "calibre-web",
|
||||
"config": {
|
||||
"subdomain": "books",
|
||||
"port": 8083,
|
||||
"mediaPath": "/media/books"
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Calibre-Web on my home host and expose it at books.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull lscr.io/linuxserver/calibre-web:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Default login: admin / admin123</li>
|
||||
<li>Point to your Calibre database location on first setup</li>
|
||||
<li>Supports EPUB, PDF, MOBI, and more formats</li>
|
||||
</ol>
|
||||
<h2>Media library path notes</h2>
|
||||
<p>The media mount path you pass as <code>mediaPath</code> in the deploy payload is mounted as <code>/books</code> inside the container. Bind a host directory containing your media library (movies, TV shows, music, etc.).</p>
|
||||
<ul>
|
||||
<li><strong>UID/GID:</strong> Calibre-Web runs as a non-root user. If you see permission errors in the dashboard Logs tab, run <code>chown -R 1000:1000 /media/books</code> on the host.</li>
|
||||
<li><strong>Multi-library:</strong> bind the parent folder and let Calibre-Web discover subfolders.</li>
|
||||
</ul>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/calibre-web/config:/config</code></li>
|
||||
<li><code>MEDIA_PATH:/books</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>PUID</code></li>
|
||||
<li><code>PGID</code></li>
|
||||
<li><code>TZ</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull lscr.io/linuxserver/calibre-web:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Calibre-Web:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>Library shows empty:</strong> confirm <code>mediaPath</code> is readable by the container UID and that the directory contains the file extensions Calibre-Web indexes.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>calibre-web</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Change Detection — DashCaddy Docs',
|
||||
description: 'Install and configure Change Detection via DashCaddy. Monitor websites for changes',
|
||||
};
|
||||
|
||||
export default function changedetectionDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Change Detection"
|
||||
intro="Monitor websites for changes"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Utilities</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">ghcr.io/dgtlmoon/changedetection.io:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Change Detection?</h2>
|
||||
<p>Monitor websites for changes</p>
|
||||
<p>Change Detection ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Change Detection, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Change Detection</strong> from the Utilities category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>watch</code>), host port (default: <code>5001</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "changedetection",
|
||||
"config": {
|
||||
"subdomain": "watch",
|
||||
"port": 5001
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Change Detection on my home host and expose it at watch.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull ghcr.io/dgtlmoon/changedetection.io:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Add URLs to monitor</li>
|
||||
<li>Configure check frequency</li>
|
||||
<li>Set up notifications</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/changedetection/data:/datastore</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<p>None. The container runs with its upstream defaults.</p><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull ghcr.io/dgtlmoon/changedetection.io:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Change Detection:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>changedetection</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install CoreDNS — DashCaddy Docs',
|
||||
description: 'Install and configure CoreDNS via DashCaddy. Cloud-native DNS server - lightweight and flexible',
|
||||
};
|
||||
|
||||
export default function corednsDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install CoreDNS"
|
||||
intro="Cloud-native DNS server - lightweight and flexible"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: DNS</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Difficulty: Intermediate</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">coredns/coredns:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is CoreDNS?</h2>
|
||||
<p>Cloud-native DNS server - lightweight and flexible</p>
|
||||
<p>CoreDNS ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using CoreDNS, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>CoreDNS</strong> from the DNS category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>dns4</code>), host port (default: <code>53</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>tcp://localhost:53</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "coredns",
|
||||
"config": {
|
||||
"subdomain": "dns4",
|
||||
"port": 53
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy CoreDNS on my home host and expose it at dns4.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull coredns/coredns:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Create Corefile in /opt/coredns/config/</li>
|
||||
<li>Define .sami zone with file plugin</li>
|
||||
<li>Create zone file with your records</li>
|
||||
<li>Restart container to load config</li>
|
||||
<li>Test with: dig @localhost test.sami</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/coredns/config:/etc/coredns</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<p>None. The container runs with its upstream defaults.</p><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull coredns/coredns:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with CoreDNS:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>tcp://localhost:53</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>coredns</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install CrowdSec — DashCaddy Docs',
|
||||
description: 'Install and configure CrowdSec via DashCaddy. Collaborative intrusion prevention system',
|
||||
};
|
||||
|
||||
export default function crowdsecDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install CrowdSec"
|
||||
intro="Collaborative intrusion prevention system"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Security</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Difficulty: Intermediate</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">crowdsecurity/crowdsec:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is CrowdSec?</h2>
|
||||
<p>Collaborative intrusion prevention system</p>
|
||||
<p>CrowdSec ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using CrowdSec, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>CrowdSec</strong> from the Security category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>crowdsec</code>), host port (default: <code>8091</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/health</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "crowdsec",
|
||||
"config": {
|
||||
"subdomain": "crowdsec",
|
||||
"port": 8091
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy CrowdSec on my home host and expose it at crowdsec.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull crowdsecurity/crowdsec:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Register at app.crowdsec.net for community threat intelligence</li>
|
||||
<li>Install bouncers on your reverse proxy for active blocking</li>
|
||||
<li>CrowdSec analyzes logs and shares threat data with the community</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/crowdsec/config:/etc/crowdsec</code></li>
|
||||
<li><code>/opt/crowdsec/data:/var/lib/crowdsec/data</code></li>
|
||||
<li><code>/var/log:/var/log:ro</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<p>None. The container runs with its upstream defaults.</p><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull crowdsecurity/crowdsec:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with CrowdSec:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/health</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>crowdsec</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install DashCA — DashCaddy Docs',
|
||||
description: 'Install and configure DashCA via DashCaddy. One-click root CA certificate installer for your network',
|
||||
};
|
||||
|
||||
export default function dashcaDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install DashCA"
|
||||
intro="One-click root CA certificate installer for your network"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Security</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">N/A</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is DashCA?</h2>
|
||||
<p>One-click root CA certificate installer for your network</p>
|
||||
<p>DashCA ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using DashCA, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>DashCA</strong> from the Security category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>ca</code>), host port (default: <code>32400</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/healthz</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "dashca",
|
||||
"config": {
|
||||
"subdomain": "ca",
|
||||
"port": 32400
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy DashCA on my home host and expose it at ca.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull N/A</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>New devices: visit http://ca.sami (HTTP, no certificate needed)</li>
|
||||
<li>Click the 'Install Certificate' button for your platform</li>
|
||||
<li>Follow platform-specific instructions</li>
|
||||
<li>Verify all *.sami domains now show secure connections</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<p>None. The container runs with its upstream defaults.</p><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull N/A</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with DashCA:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/healthz</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>dashca</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Digital Clock — DashCaddy Docs',
|
||||
description: 'Install and configure Digital Clock via DashCaddy. Live digital clock with time, date, and day of week',
|
||||
};
|
||||
|
||||
export default function digitalClockDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Digital Clock"
|
||||
intro="Live digital clock with time, date, and day of week"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Utilities</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">N/A</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Digital Clock?</h2>
|
||||
<p>Live digital clock with time, date, and day of week</p>
|
||||
<p>Digital Clock ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Digital Clock, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Digital Clock</strong> from the Utilities category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>digital-clock</code>), host port (default: <code>32400</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/healthz</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "digital-clock",
|
||||
"config": {
|
||||
"subdomain": "digital-clock",
|
||||
"port": 32400
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Digital Clock on my home host and expose it at digital-clock.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull N/A</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Clock appears in the top bar to the right of the weather widget</li>
|
||||
<li>No configuration needed — runs automatically</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<p>None. The container runs with its upstream defaults.</p><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull N/A</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Digital Clock:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/healthz</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>digital-clock</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Dozzle — DashCaddy Docs',
|
||||
description: 'Install and configure Dozzle via DashCaddy. Real-time Docker container log viewer',
|
||||
};
|
||||
|
||||
export default function dozzleDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Dozzle"
|
||||
intro="Real-time Docker container log viewer"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Monitoring</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">amir20/dozzle:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Dozzle?</h2>
|
||||
<p>Real-time Docker container log viewer</p>
|
||||
<p>Dozzle ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Dozzle, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Dozzle</strong> from the Monitoring category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>logs</code>), host port (default: <code>8088</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "dozzle",
|
||||
"config": {
|
||||
"subdomain": "logs",
|
||||
"port": 8088
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Dozzle on my home host and expose it at logs.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull amir20/dozzle:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>View real-time logs from all running containers</li>
|
||||
<li>Filter and search across container logs</li>
|
||||
<li>No configuration needed - auto-discovers containers</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/var/run/docker.sock:/var/run/docker.sock:ro</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<p>None. The container runs with its upstream defaults.</p><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull amir20/dozzle:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Dozzle:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>dozzle</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Drone CI — DashCaddy Docs',
|
||||
description: 'Install and configure Drone CI via DashCaddy. Container-native continuous delivery platform',
|
||||
};
|
||||
|
||||
export default function droneDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Drone CI"
|
||||
intro="Container-native continuous delivery platform"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Development</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Difficulty: Intermediate</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">drone/drone:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Drone CI?</h2>
|
||||
<p>Container-native continuous delivery platform</p>
|
||||
<p>Drone CI ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Drone CI, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Drone CI</strong> from the Development category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>drone</code>), host port (default: <code>8090</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "drone",
|
||||
"config": {
|
||||
"subdomain": "drone",
|
||||
"port": 8090
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Drone CI on my home host and expose it at drone.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull drone/drone:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Configure Git provider integration</li>
|
||||
<li>Set up shared secret</li>
|
||||
<li>Deploy Drone runners</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/drone/data:/data</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>DRONE_GITEA_SERVER</code></li>
|
||||
<li><code>DRONE_RPC_SECRET</code></li>
|
||||
<li><code>DRONE_SERVER_HOST</code></li>
|
||||
<li><code>DRONE_SERVER_PROTO</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull drone/drone:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Drone CI:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>drone</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Emby — DashCaddy Docs',
|
||||
description: 'Install and configure Emby via DashCaddy. Personal media server with apps for all devices',
|
||||
};
|
||||
|
||||
export default function embyDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Emby"
|
||||
intro="Personal media server with apps for all devices"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Media</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">emby/embyserver:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Emby?</h2>
|
||||
<p>Personal media server with apps for all devices</p>
|
||||
<p>Emby ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Emby, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>A host path containing your media. Default suggestion: <code>/media</code>. The deploy form / API payload <code>config.mediaPath</code> must be readable by the container UID (usually <code>1000</code>).</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Emby</strong> from the Media category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>emby</code>), host port (default: <code>8096</code>), and the media library path.</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/emby/web/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "emby",
|
||||
"config": {
|
||||
"subdomain": "emby",
|
||||
"port": 8096,
|
||||
"mediaPath": "/media"
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Emby on my home host and expose it at emby.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull emby/embyserver:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Complete the initial setup wizard at the web interface</li>
|
||||
<li>Add your media libraries (Movies, TV Shows, Music)</li>
|
||||
<li>Configure user accounts and permissions</li>
|
||||
<li>Install Emby apps on your devices for remote access</li>
|
||||
</ol>
|
||||
<h2>Media library path notes</h2>
|
||||
<p>The media mount path you pass as <code>mediaPath</code> in the deploy payload is mounted as <code>/media</code> inside the container. Bind a host directory containing your media library (movies, TV shows, music, etc.).</p>
|
||||
<ul>
|
||||
<li><strong>UID/GID:</strong> Emby runs as a non-root user. If you see permission errors in the dashboard Logs tab, run <code>chown -R 1000:1000 /media</code> on the host.</li>
|
||||
<li><strong>Multi-library:</strong> bind the parent folder and let Emby discover subfolders.</li>
|
||||
</ul>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/emby/config:/config</code></li>
|
||||
<li><code>/opt/emby/cache:/cache</code></li>
|
||||
<li><code>MEDIA_PATH:/media</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>UID</code></li>
|
||||
<li><code>GID</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull emby/embyserver:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Emby:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>Library shows empty:</strong> confirm <code>mediaPath</code> is readable by the container UID and that the directory contains the file extensions Emby indexes.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/emby/web/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>emby</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Excalidraw — DashCaddy Docs',
|
||||
description: 'Install and configure Excalidraw via DashCaddy. Collaborative virtual whiteboard for sketching and diagrams',
|
||||
};
|
||||
|
||||
export default function excalidrawDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Excalidraw"
|
||||
intro="Collaborative virtual whiteboard for sketching and diagrams"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Productivity</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">excalidraw/excalidraw:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Excalidraw?</h2>
|
||||
<p>Collaborative virtual whiteboard for sketching and diagrams</p>
|
||||
<p>Excalidraw ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Excalidraw, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Excalidraw</strong> from the Productivity category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>draw</code>), host port (default: <code>8086</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "excalidraw",
|
||||
"config": {
|
||||
"subdomain": "draw",
|
||||
"port": 8086
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Excalidraw on my home host and expose it at draw.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull excalidraw/excalidraw:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Start drawing immediately - no account needed</li>
|
||||
<li>Share drawings via link for real-time collaboration</li>
|
||||
<li>Export as PNG, SVG, or Excalidraw file</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/excalidraw/data:/var/lib/excalidraw</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<p>None. The container runs with its upstream defaults.</p><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull excalidraw/excalidraw:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Excalidraw:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>excalidraw</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install FileBrowser — DashCaddy Docs',
|
||||
description: 'Install and configure FileBrowser via DashCaddy. Web-based file manager with sharing capabilities',
|
||||
};
|
||||
|
||||
export default function filebrowserDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install FileBrowser"
|
||||
intro="Web-based file manager with sharing capabilities"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Files</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">filebrowser/filebrowser:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is FileBrowser?</h2>
|
||||
<p>Web-based file manager with sharing capabilities</p>
|
||||
<p>FileBrowser ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using FileBrowser, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>FileBrowser</strong> from the Files category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>files</code>), host port (default: <code>8085</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "filebrowser",
|
||||
"config": {
|
||||
"subdomain": "files",
|
||||
"port": 8085
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy FileBrowser on my home host and expose it at files.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull filebrowser/filebrowser:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Default login: admin/admin</li>
|
||||
<li>Change default password immediately</li>
|
||||
<li>Configure user permissions and shares</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/filebrowser/data:/srv</code></li>
|
||||
<li><code>/opt/filebrowser/database:/database</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<p>None. The container runs with its upstream defaults.</p><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull filebrowser/filebrowser:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with FileBrowser:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>filebrowser</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Gitea — DashCaddy Docs',
|
||||
description: 'Install and configure Gitea via DashCaddy. Lightweight self-hosted Git service',
|
||||
};
|
||||
|
||||
export default function giteaDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Gitea"
|
||||
intro="Lightweight self-hosted Git service"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Development</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">gitea/gitea:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Gitea?</h2>
|
||||
<p>Lightweight self-hosted Git service</p>
|
||||
<p>Gitea ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Gitea, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Gitea</strong> from the Development category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>gitea</code>), host port (default: <code>3005</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "gitea",
|
||||
"config": {
|
||||
"subdomain": "gitea",
|
||||
"port": 3005
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Gitea on my home host and expose it at gitea.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull gitea/gitea:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Complete initial setup wizard</li>
|
||||
<li>Create admin account</li>
|
||||
<li>Configure SSH access</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/gitea/data:/data</code></li>
|
||||
<li><code>/etc/timezone:/etc/timezone:ro</code></li>
|
||||
<li><code>/etc/localtime:/etc/localtime:ro</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>USER_UID</code></li>
|
||||
<li><code>USER_GID</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull gitea/gitea:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Gitea:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>gitea</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Grafana — DashCaddy Docs',
|
||||
description: 'Install and configure Grafana via DashCaddy. Analytics and interactive visualization platform',
|
||||
};
|
||||
|
||||
export default function grafanaDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Grafana"
|
||||
intro="Analytics and interactive visualization platform"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Monitoring</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#f5a62322', color: '#f5a623' }}>Difficulty: Advanced</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">grafana/grafana:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Grafana?</h2>
|
||||
<p>Analytics and interactive visualization platform</p>
|
||||
<p>Grafana ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Grafana, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Grafana</strong> from the Monitoring category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>grafana</code>), host port (default: <code>3000</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/api/health</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "grafana",
|
||||
"config": {
|
||||
"subdomain": "grafana",
|
||||
"port": 3000
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Grafana on my home host and expose it at grafana.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull grafana/grafana:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Open the deployed URL (returned in the response as <code>url</code>, or visible in the dashboard).</li>
|
||||
<li>Complete the upstream Grafana setup wizard (admin account, library paths, EULA).</li>
|
||||
<li>Restore from a backup if one exists: <code>POST /api/v1/apps/{appId}/restore</code> with the backup ID from <code>GET /api/v1/backups/history</code>.</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/grafana/data:/var/lib/grafana</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>GF_SECURITY_ADMIN_PASSWORD</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull grafana/grafana:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Grafana:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/api/health</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>grafana</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Homarr — DashCaddy Docs',
|
||||
description: 'Install and configure Homarr via DashCaddy. Sleek dashboard for all your services',
|
||||
};
|
||||
|
||||
export default function homarrDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Homarr"
|
||||
intro="Sleek dashboard for all your services"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Utilities</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">ghcr.io/ajnart/homarr:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Homarr?</h2>
|
||||
<p>Sleek dashboard for all your services</p>
|
||||
<p>Homarr ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Homarr, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Homarr</strong> from the Utilities category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>homarr</code>), host port (default: <code>7575</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "homarr",
|
||||
"config": {
|
||||
"subdomain": "homarr",
|
||||
"port": 7575
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Homarr on my home host and expose it at homarr.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull ghcr.io/ajnart/homarr:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Add your services via UI</li>
|
||||
<li>Configure integrations</li>
|
||||
<li>Customize layout and appearance</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/homarr/configs:/app/data/configs</code></li>
|
||||
<li><code>/opt/homarr/icons:/app/public/icons</code></li>
|
||||
<li><code>/var/run/docker.sock:/var/run/docker.sock:ro</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<p>None. The container runs with its upstream defaults.</p><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull ghcr.io/ajnart/homarr:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Homarr:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>homarr</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,131 +0,0 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Home Assistant — DashCaddy Docs',
|
||||
description: 'Install and configure Home Assistant via DashCaddy. Open source home automation platform',
|
||||
};
|
||||
|
||||
export default function homeassistantDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Home Assistant"
|
||||
intro="Open source home automation platform"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Home Automation</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Difficulty: Intermediate</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">homeassistant/home-assistant:stable</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Home Assistant?</h2>
|
||||
<p>Open source home automation platform</p>
|
||||
<p>Home Assistant ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Home Assistant, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Home Assistant</strong> from the Home Automation category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>home</code>), host port (default: <code>8123</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/api/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "homeassistant",
|
||||
"config": {
|
||||
"subdomain": "home",
|
||||
"port": 8123
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Home Assistant on my home host and expose it at home.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull homeassistant/home-assistant:stable</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Complete onboarding wizard</li>
|
||||
<li>Add integrations for your smart devices</li>
|
||||
<li>Create automations and dashboards</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/homeassistant/config:/config</code></li>
|
||||
<li><code>/etc/localtime:/etc/localtime:ro</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>TZ</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull homeassistant/home-assistant:stable</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Home Assistant:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/api/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>homeassistant</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Homepage — DashCaddy Docs',
|
||||
description: 'Install and configure Homepage via DashCaddy. Highly customizable application dashboard',
|
||||
};
|
||||
|
||||
export default function homepageDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Homepage"
|
||||
intro="Highly customizable application dashboard"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Utilities</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">ghcr.io/gethomepage/homepage:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Homepage?</h2>
|
||||
<p>Highly customizable application dashboard</p>
|
||||
<p>Homepage ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Homepage, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Homepage</strong> from the Utilities category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>dashboard</code>), host port (default: <code>3008</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "homepage",
|
||||
"config": {
|
||||
"subdomain": "dashboard",
|
||||
"port": 3008
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Homepage on my home host and expose it at dashboard.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull ghcr.io/gethomepage/homepage:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Edit config files to add services</li>
|
||||
<li>Configure widgets</li>
|
||||
<li>Customize appearance</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/homepage/config:/app/config</code></li>
|
||||
<li><code>/var/run/docker.sock:/var/run/docker.sock:ro</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<p>None. The container runs with its upstream defaults.</p><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull ghcr.io/gethomepage/homepage:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Homepage:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>homepage</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Immich — DashCaddy Docs',
|
||||
description: 'Install and configure Immich via DashCaddy. Self-hosted Google Photos alternative',
|
||||
};
|
||||
|
||||
export default function immichDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Immich"
|
||||
intro="Self-hosted Google Photos alternative"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Photos</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Difficulty: Intermediate</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">ghcr.io/immich-app/immich-server:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Immich?</h2>
|
||||
<p>Self-hosted Google Photos alternative</p>
|
||||
<p>Immich ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Immich, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Immich</strong> from the Photos category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>photos</code>), host port (default: <code>2283</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/api/server-info/ping</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "immich",
|
||||
"config": {
|
||||
"subdomain": "photos",
|
||||
"port": 2283
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Immich on my home host and expose it at photos.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull ghcr.io/immich-app/immich-server:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Requires PostgreSQL and Redis</li>
|
||||
<li>Install mobile apps for backup</li>
|
||||
<li>Configure machine learning for face detection</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/immich/upload:/usr/src/app/upload</code></li>
|
||||
<li><code>/opt/immich/library:/usr/src/app/library</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>DB_HOSTNAME</code></li>
|
||||
<li><code>DB_USERNAME</code></li>
|
||||
<li><code>DB_PASSWORD</code></li>
|
||||
<li><code>DB_DATABASE_NAME</code></li>
|
||||
<li><code>REDIS_HOSTNAME</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull ghcr.io/immich-app/immich-server:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Immich:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/api/server-info/ping</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>immich</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install IT Tools — DashCaddy Docs',
|
||||
description: 'Install and configure IT Tools via DashCaddy. Collection of handy developer and IT tools in one place',
|
||||
};
|
||||
|
||||
export default function itToolsDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install IT Tools"
|
||||
intro="Collection of handy developer and IT tools in one place"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Utilities</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">corentinth/it-tools:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is IT Tools?</h2>
|
||||
<p>Collection of handy developer and IT tools in one place</p>
|
||||
<p>IT Tools ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using IT Tools, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>IT Tools</strong> from the Utilities category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>tools</code>), host port (default: <code>8087</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "it-tools",
|
||||
"config": {
|
||||
"subdomain": "tools",
|
||||
"port": 8087
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy IT Tools on my home host and expose it at tools.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull corentinth/it-tools:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Access the web interface for instant tools access</li>
|
||||
<li>Includes: hash generators, UUID, JWT decoder, base64, regex tester, and 70+ more</li>
|
||||
<li>No configuration needed</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/it-tools/config:/config</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<p>None. The container runs with its upstream defaults.</p><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull corentinth/it-tools:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with IT Tools:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>it-tools</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install JDownloader 2 — DashCaddy Docs',
|
||||
description: 'Install and configure JDownloader 2 via DashCaddy. Download manager for file hosting sites',
|
||||
};
|
||||
|
||||
export default function jdownloaderDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install JDownloader 2"
|
||||
intro="Download manager for file hosting sites"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Downloads</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">jlesage/jdownloader-2:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is JDownloader 2?</h2>
|
||||
<p>Download manager for file hosting sites</p>
|
||||
<p>JDownloader 2 ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using JDownloader 2, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>JDownloader 2</strong> from the Downloads category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>jdownloader</code>), host port (default: <code>5800</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "jdownloader",
|
||||
"config": {
|
||||
"subdomain": "jdownloader",
|
||||
"port": 5800
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy JDownloader 2 on my home host and expose it at jdownloader.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull jlesage/jdownloader-2:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Access web interface to configure</li>
|
||||
<li>Link to MyJDownloader account</li>
|
||||
<li>Configure download paths</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/jdownloader/config:/config</code></li>
|
||||
<li><code>/downloads:/output</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<p>None. The container runs with its upstream defaults.</p><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull jlesage/jdownloader-2:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with JDownloader 2:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>jdownloader</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,140 +0,0 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Jellyfin — DashCaddy Docs',
|
||||
description: 'Install and configure Jellyfin via DashCaddy. Free software media system - alternative to Plex',
|
||||
};
|
||||
|
||||
export default function jellyfinDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Jellyfin"
|
||||
intro="Free software media system - alternative to Plex"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Media</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">jellyfin/jellyfin:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Jellyfin?</h2>
|
||||
<p>Free software media system - alternative to Plex</p>
|
||||
<p>Jellyfin ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Jellyfin, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>A host path containing your media. Default suggestion: <code>/media</code>. The deploy form / API payload <code>config.mediaPath</code> must be readable by the container UID (usually <code>1000</code>).</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Jellyfin</strong> from the Media category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>jellyfin</code>), host port (default: <code>8096</code>), and the media library path.</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/health</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "jellyfin",
|
||||
"config": {
|
||||
"subdomain": "jellyfin",
|
||||
"port": 8096,
|
||||
"mediaPath": "/media"
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Jellyfin on my home host and expose it at jellyfin.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull jellyfin/jellyfin:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Complete the initial setup wizard</li>
|
||||
<li>Add your media libraries</li>
|
||||
<li>Configure user accounts and permissions</li>
|
||||
</ol>
|
||||
<h2>Media library path notes</h2>
|
||||
<p>The media mount path you pass as <code>mediaPath</code> in the deploy payload is mounted as <code>/media</code> inside the container. Bind a host directory containing your media library (movies, TV shows, music, etc.).</p>
|
||||
<ul>
|
||||
<li><strong>UID/GID:</strong> Jellyfin runs as a non-root user. If you see permission errors in the dashboard Logs tab, run <code>chown -R 1000:1000 /media</code> on the host.</li>
|
||||
<li><strong>Multi-library:</strong> bind the parent folder and let Jellyfin discover subfolders.</li>
|
||||
</ul>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/jellyfin/config:/config</code></li>
|
||||
<li><code>/opt/jellyfin/cache:/cache</code></li>
|
||||
<li><code>MEDIA_PATH:/media</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>JELLYFIN_PublishedServerUrl</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull jellyfin/jellyfin:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Jellyfin:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>Library shows empty:</strong> confirm <code>mediaPath</code> is readable by the container UID and that the directory contains the file extensions Jellyfin indexes.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/health</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>jellyfin</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Jenkins — DashCaddy Docs',
|
||||
description: 'Install and configure Jenkins via DashCaddy. Automation server for CI/CD pipelines',
|
||||
};
|
||||
|
||||
export default function jenkinsDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Jenkins"
|
||||
intro="Automation server for CI/CD pipelines"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Development</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#f5a62322', color: '#f5a623' }}>Difficulty: Advanced</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">jenkins/jenkins:lts</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Jenkins?</h2>
|
||||
<p>Automation server for CI/CD pipelines</p>
|
||||
<p>Jenkins ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Jenkins, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Jenkins</strong> from the Development category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>jenkins</code>), host port (default: <code>8089</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/login</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "jenkins",
|
||||
"config": {
|
||||
"subdomain": "jenkins",
|
||||
"port": 8089
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Jenkins on my home host and expose it at jenkins.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull jenkins/jenkins:lts</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Get initial admin password from logs</li>
|
||||
<li>Install suggested plugins</li>
|
||||
<li>Create admin user</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/jenkins/data:/var/jenkins_home</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<p>None. The container runs with its upstream defaults.</p><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull jenkins/jenkins:lts</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Jenkins:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/login</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>jenkins</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Kavita — DashCaddy Docs',
|
||||
description: 'Install and configure Kavita via DashCaddy. Digital reading platform for manga, comics, and books',
|
||||
};
|
||||
|
||||
export default function kavitaDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Kavita"
|
||||
intro="Digital reading platform for manga, comics, and books"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Media</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">jvmilazz0/kavita:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Kavita?</h2>
|
||||
<p>Digital reading platform for manga, comics, and books</p>
|
||||
<p>Kavita ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Kavita, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>A host path containing your media. Default suggestion: <code>/media/reading</code>. The deploy form / API payload <code>config.mediaPath</code> must be readable by the container UID (usually <code>1000</code>).</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Kavita</strong> from the Media category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>kavita</code>), host port (default: <code>5004</code>), and the media library path.</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "kavita",
|
||||
"config": {
|
||||
"subdomain": "kavita",
|
||||
"port": 5004,
|
||||
"mediaPath": "/media/reading"
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Kavita on my home host and expose it at kavita.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull jvmilazz0/kavita:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Create admin account on first access</li>
|
||||
<li>Add library folders for manga, comics, or books</li>
|
||||
<li>Supports EPUB, PDF, CBZ, CBR formats</li>
|
||||
</ol>
|
||||
<h2>Media library path notes</h2>
|
||||
<p>The media mount path you pass as <code>mediaPath</code> in the deploy payload is mounted as <code>/data</code> inside the container. Bind a host directory containing your media library (movies, TV shows, music, etc.).</p>
|
||||
<ul>
|
||||
<li><strong>UID/GID:</strong> Kavita runs as a non-root user. If you see permission errors in the dashboard Logs tab, run <code>chown -R 1000:1000 /media/reading</code> on the host.</li>
|
||||
<li><strong>Multi-library:</strong> bind the parent folder and let Kavita discover subfolders.</li>
|
||||
</ul>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/kavita/config:/kavita/config</code></li>
|
||||
<li><code>MEDIA_PATH:/data</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<p>None. The container runs with its upstream defaults.</p><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull jvmilazz0/kavita:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Kavita:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>Library shows empty:</strong> confirm <code>mediaPath</code> is readable by the container UID and that the directory contains the file extensions Kavita indexes.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>kavita</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Komga — DashCaddy Docs',
|
||||
description: 'Install and configure Komga via DashCaddy. Comic and manga media server with web reader',
|
||||
};
|
||||
|
||||
export default function komgaDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Komga"
|
||||
intro="Comic and manga media server with web reader"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Media</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">gotson/komga:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Komga?</h2>
|
||||
<p>Comic and manga media server with web reader</p>
|
||||
<p>Komga ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Komga, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>A host path containing your media. Default suggestion: <code>/media/comics</code>. The deploy form / API payload <code>config.mediaPath</code> must be readable by the container UID (usually <code>1000</code>).</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Komga</strong> from the Media category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>komga</code>), host port (default: <code>25600</code>), and the media library path.</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "komga",
|
||||
"config": {
|
||||
"subdomain": "komga",
|
||||
"port": 25600,
|
||||
"mediaPath": "/media/comics"
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Komga on my home host and expose it at komga.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull gotson/komga:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Create admin account on first access</li>
|
||||
<li>Add your comic libraries (CBZ, CBR, PDF supported)</li>
|
||||
<li>Use OPDS for third-party reader apps</li>
|
||||
</ol>
|
||||
<h2>Media library path notes</h2>
|
||||
<p>The media mount path you pass as <code>mediaPath</code> in the deploy payload is mounted as <code>/data</code> inside the container. Bind a host directory containing your media library (movies, TV shows, music, etc.).</p>
|
||||
<ul>
|
||||
<li><strong>UID/GID:</strong> Komga runs as a non-root user. If you see permission errors in the dashboard Logs tab, run <code>chown -R 1000:1000 /media/comics</code> on the host.</li>
|
||||
<li><strong>Multi-library:</strong> bind the parent folder and let Komga discover subfolders.</li>
|
||||
</ul>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/komga/config:/config</code></li>
|
||||
<li><code>MEDIA_PATH:/data</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>TZ</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull gotson/komga:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Komga:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>Library shows empty:</strong> confirm <code>mediaPath</code> is readable by the container UID and that the directory contains the file extensions Komga indexes.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>komga</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Lidarr — DashCaddy Docs',
|
||||
description: 'Install and configure Lidarr via DashCaddy. Music collection manager for Usenet and BitTorrent',
|
||||
};
|
||||
|
||||
export default function lidarrDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Lidarr"
|
||||
intro="Music collection manager for Usenet and BitTorrent"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Media Management</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Difficulty: Intermediate</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">linuxserver/lidarr:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Lidarr?</h2>
|
||||
<p>Music collection manager for Usenet and BitTorrent</p>
|
||||
<p>Lidarr ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Lidarr, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Lidarr</strong> from the Media Management category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>lidarr</code>), host port (default: <code>8686</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/api/v1/system/status</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "lidarr",
|
||||
"config": {
|
||||
"subdomain": "lidarr",
|
||||
"port": 8686
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Lidarr on my home host and expose it at lidarr.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull linuxserver/lidarr:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Configure download clients</li>
|
||||
<li>Add indexers</li>
|
||||
<li>Set up root folders for music</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/lidarr/config:/config</code></li>
|
||||
<li><code>/downloads:/downloads</code></li>
|
||||
<li><code>/music:/music</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>PUID</code></li>
|
||||
<li><code>PGID</code></li>
|
||||
<li><code>TZ</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull linuxserver/lidarr:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Lidarr:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/api/v1/system/status</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>lidarr</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Docker Mailserver — DashCaddy Docs',
|
||||
description: 'Install and configure Docker Mailserver via DashCaddy. Full-featured email server with SMTP, IMAP, spam filtering',
|
||||
};
|
||||
|
||||
export default function mailserverDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Docker Mailserver"
|
||||
intro="Full-featured email server with SMTP, IMAP, spam filtering"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Communication</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#f5a62322', color: '#f5a623' }}>Difficulty: Advanced</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">mailserver/docker-mailserver:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Docker Mailserver?</h2>
|
||||
<p>Full-featured email server with SMTP, IMAP, spam filtering</p>
|
||||
<p>Docker Mailserver ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Docker Mailserver, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Docker Mailserver</strong> from the Communication category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>mail</code>), host port (default: <code>25</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "mailserver",
|
||||
"config": {
|
||||
"subdomain": "mail",
|
||||
"port": 25
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Docker Mailserver on my home host and expose it at mail.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull mailserver/docker-mailserver:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Configure DNS records (MX, SPF, DKIM, DMARC)</li>
|
||||
<li>Create email accounts using setup.sh</li>
|
||||
<li>Set up SSL certificates for secure connections</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/mailserver/data:/var/mail</code></li>
|
||||
<li><code>/opt/mailserver/state:/var/mail-state</code></li>
|
||||
<li><code>/opt/mailserver/logs:/var/log/mail</code></li>
|
||||
<li><code>/opt/mailserver/config:/tmp/docker-mailserver</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>ENABLE_SPAMASSASSIN</code></li>
|
||||
<li><code>ENABLE_CLAMAV</code></li>
|
||||
<li><code>ENABLE_FAIL2BAN</code></li>
|
||||
<li><code>ONE_DIR</code></li>
|
||||
<li><code>TZ</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull mailserver/docker-mailserver:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Docker Mailserver:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>mailserver</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,131 +0,0 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Matrix Synapse — DashCaddy Docs',
|
||||
description: 'Install and configure Matrix Synapse via DashCaddy. Decentralized, secure messaging and collaboration',
|
||||
};
|
||||
|
||||
export default function matrixDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Matrix Synapse"
|
||||
intro="Decentralized, secure messaging and collaboration"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Communication</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#f5a62322', color: '#f5a623' }}>Difficulty: Advanced</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">matrixdotorg/synapse:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Matrix Synapse?</h2>
|
||||
<p>Decentralized, secure messaging and collaboration</p>
|
||||
<p>Matrix Synapse ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Matrix Synapse, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Matrix Synapse</strong> from the Communication category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>matrix</code>), host port (default: <code>8008</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/_matrix/client/versions</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "matrix",
|
||||
"config": {
|
||||
"subdomain": "matrix",
|
||||
"port": 8008
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Matrix Synapse on my home host and expose it at matrix.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull matrixdotorg/synapse:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Generate initial config with --generate</li>
|
||||
<li>Configure homeserver.yaml</li>
|
||||
<li>Set up federation if needed</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/matrix/data:/data</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>SYNAPSE_SERVER_NAME</code></li>
|
||||
<li><code>SYNAPSE_REPORT_STATS</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull matrixdotorg/synapse:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Matrix Synapse:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/_matrix/client/versions</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>matrix</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Mealie — DashCaddy Docs',
|
||||
description: 'Install and configure Mealie via DashCaddy. Recipe manager and meal planner with grocery lists',
|
||||
};
|
||||
|
||||
export default function mealieDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Mealie"
|
||||
intro="Recipe manager and meal planner with grocery lists"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Productivity</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">ghcr.io/mealie-recipes/mealie:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Mealie?</h2>
|
||||
<p>Recipe manager and meal planner with grocery lists</p>
|
||||
<p>Mealie ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Mealie, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Mealie</strong> from the Productivity category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>mealie</code>), host port (default: <code>9925</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "mealie",
|
||||
"config": {
|
||||
"subdomain": "mealie",
|
||||
"port": 9925
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Mealie on my home host and expose it at mealie.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull ghcr.io/mealie-recipes/mealie:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Default login: changeme@example.com / MyPassword</li>
|
||||
<li>Import recipes from URLs or add them manually</li>
|
||||
<li>Create meal plans and generate shopping lists</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/mealie/data:/app/data</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>ALLOW_SIGNUP</code></li>
|
||||
<li><code>MAX_WORKERS</code></li>
|
||||
<li><code>WEB_CONCURRENCY</code></li>
|
||||
<li><code>BASE_URL</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull ghcr.io/mealie-recipes/mealie:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Mealie:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>mealie</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Minecraft Server — DashCaddy Docs',
|
||||
description: 'Install and configure Minecraft Server via DashCaddy. Minecraft Java Edition dedicated server',
|
||||
};
|
||||
|
||||
export default function minecraftDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Minecraft Server"
|
||||
intro="Minecraft Java Edition dedicated server"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Gaming</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">itzg/minecraft-server:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Minecraft Server?</h2>
|
||||
<p>Minecraft Java Edition dedicated server</p>
|
||||
<p>Minecraft Server ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Minecraft Server, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Minecraft Server</strong> from the Gaming category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>mc</code>), host port (default: <code>25565</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>tcp://localhost:25565</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "minecraft",
|
||||
"config": {
|
||||
"subdomain": "mc",
|
||||
"port": 25565
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Minecraft Server on my home host and expose it at mc.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull itzg/minecraft-server:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Server accepts the Minecraft EULA automatically</li>
|
||||
<li>Connect with your Minecraft client to the server IP:port</li>
|
||||
<li>Configure server.properties in the data volume for customization</li>
|
||||
<li>Supports Vanilla, Paper, Forge, Fabric via TYPE environment variable</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/minecraft/data:/data</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>EULA</code></li>
|
||||
<li><code>TYPE</code></li>
|
||||
<li><code>VERSION</code></li>
|
||||
<li><code>MEMORY</code></li>
|
||||
<li><code>MAX_PLAYERS</code></li>
|
||||
<li><code>MOTD</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull itzg/minecraft-server:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Minecraft Server:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>tcp://localhost:25565</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>minecraft</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,131 +0,0 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install MongoDB — DashCaddy Docs',
|
||||
description: 'Install and configure MongoDB via DashCaddy. Document-oriented NoSQL database',
|
||||
};
|
||||
|
||||
export default function mongodbDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install MongoDB"
|
||||
intro="Document-oriented NoSQL database"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Database</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Difficulty: Intermediate</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">mongo:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is MongoDB?</h2>
|
||||
<p>Document-oriented NoSQL database</p>
|
||||
<p>MongoDB ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using MongoDB, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>MongoDB</strong> from the Database category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>mongo</code>), host port (default: <code>27017</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "mongodb",
|
||||
"config": {
|
||||
"subdomain": "mongo",
|
||||
"port": 27017
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy MongoDB on my home host and expose it at mongo.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull mongo:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Change default admin password</li>
|
||||
<li>Create application databases and users</li>
|
||||
<li>Configure replica set if needed</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/mongodb/data:/data/db</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>MONGO_INITDB_ROOT_USERNAME</code></li>
|
||||
<li><code>MONGO_INITDB_ROOT_PASSWORD</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull mongo:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with MongoDB:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>mongodb</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Navidrome — DashCaddy Docs',
|
||||
description: 'Install and configure Navidrome via DashCaddy. Modern music server and streamer',
|
||||
};
|
||||
|
||||
export default function navidromeDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Navidrome"
|
||||
intro="Modern music server and streamer"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Media</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Difficulty: Easy</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">deluan/navidrome:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Navidrome?</h2>
|
||||
<p>Modern music server and streamer</p>
|
||||
<p>Navidrome ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Navidrome, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Navidrome</strong> from the Media category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>music</code>), host port (default: <code>4533</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "navidrome",
|
||||
"config": {
|
||||
"subdomain": "music",
|
||||
"port": 4533
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Navidrome on my home host and expose it at music.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull deluan/navidrome:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Point to your music library</li>
|
||||
<li>Create user accounts</li>
|
||||
<li>Install Subsonic-compatible apps</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/navidrome/data:/data</code></li>
|
||||
<li><code>/music:/music:ro</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>ND_SCANSCHEDULE</code></li>
|
||||
<li><code>ND_LOGLEVEL</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull deluan/navidrome:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Navidrome:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>navidrome</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Nextcloud — DashCaddy Docs',
|
||||
description: 'Install and configure Nextcloud via DashCaddy. Self-hosted productivity platform and file sync',
|
||||
};
|
||||
|
||||
export default function nextcloudDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Nextcloud"
|
||||
intro="Self-hosted productivity platform and file sync"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Productivity</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Difficulty: Intermediate</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">nextcloud:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Nextcloud?</h2>
|
||||
<p>Self-hosted productivity platform and file sync</p>
|
||||
<p>Nextcloud ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Nextcloud, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Nextcloud</strong> from the Productivity category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>cloud</code>), host port (default: <code>8080</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/status.php</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "nextcloud",
|
||||
"config": {
|
||||
"subdomain": "cloud",
|
||||
"port": 8080
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Nextcloud on my home host and expose it at cloud.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull nextcloud:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Change the default admin password</li>
|
||||
<li>Configure trusted domains</li>
|
||||
<li>Install recommended apps</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/nextcloud/html:/var/www/html</code></li>
|
||||
<li><code>/opt/nextcloud/data:/var/www/html/data</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>NEXTCLOUD_ADMIN_USER</code></li>
|
||||
<li><code>NEXTCLOUD_ADMIN_PASSWORD</code></li>
|
||||
<li><code>NEXTCLOUD_TRUSTED_DOMAINS</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull nextcloud:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Nextcloud:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/status.php</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>nextcloud</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Node-RED — DashCaddy Docs',
|
||||
description: 'Install and configure Node-RED via DashCaddy. Flow-based programming for IoT and automation',
|
||||
};
|
||||
|
||||
export default function noderedDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Node-RED"
|
||||
intro="Flow-based programming for IoT and automation"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Home Automation</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Difficulty: Intermediate</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">nodered/node-red:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Node-RED?</h2>
|
||||
<p>Flow-based programming for IoT and automation</p>
|
||||
<p>Node-RED ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Node-RED, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Node-RED</strong> from the Home Automation category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>nodered</code>), host port (default: <code>1880</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "nodered",
|
||||
"config": {
|
||||
"subdomain": "nodered",
|
||||
"port": 1880
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Node-RED on my home host and expose it at nodered.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull nodered/node-red:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Install additional nodes from palette</li>
|
||||
<li>Create flows for automation</li>
|
||||
<li>Connect to Home Assistant or MQTT</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/nodered/data:/data</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>TZ</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull nodered/node-red:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Node-RED:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>nodered</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install NZBGet — DashCaddy Docs',
|
||||
description: 'Install and configure NZBGet via DashCaddy. Efficient Usenet downloader',
|
||||
};
|
||||
|
||||
export default function nzbgetDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install NZBGet"
|
||||
intro="Efficient Usenet downloader"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Downloads</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Difficulty: Intermediate</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">linuxserver/nzbget:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is NZBGet?</h2>
|
||||
<p>Efficient Usenet downloader</p>
|
||||
<p>NZBGet ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using NZBGet, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>NZBGet</strong> from the Downloads category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>nzbget</code>), host port (default: <code>6789</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "nzbget",
|
||||
"config": {
|
||||
"subdomain": "nzbget",
|
||||
"port": 6789
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy NZBGet on my home host and expose it at nzbget.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull linuxserver/nzbget:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Default login: nzbget/tegbzn6789</li>
|
||||
<li>Configure news servers</li>
|
||||
<li>Set up categories and paths</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/nzbget/config:/config</code></li>
|
||||
<li><code>/downloads:/downloads</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>PUID</code></li>
|
||||
<li><code>PGID</code></li>
|
||||
<li><code>TZ</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull linuxserver/nzbget:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with NZBGet:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>nzbget</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Outline — DashCaddy Docs',
|
||||
description: 'Install and configure Outline via DashCaddy. Modern team knowledge base and wiki',
|
||||
};
|
||||
|
||||
export default function outlineDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Outline"
|
||||
intro="Modern team knowledge base and wiki"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Productivity</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#f5a62322', color: '#f5a623' }}>Difficulty: Advanced</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">outlinewiki/outline:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Outline?</h2>
|
||||
<p>Modern team knowledge base and wiki</p>
|
||||
<p>Outline ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Outline, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Outline</strong> from the Productivity category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>outline</code>), host port (default: <code>3006</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "outline",
|
||||
"config": {
|
||||
"subdomain": "outline",
|
||||
"port": 3006
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Outline on my home host and expose it at outline.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull outlinewiki/outline:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Requires PostgreSQL and Redis</li>
|
||||
<li>Configure OAuth provider</li>
|
||||
<li>Set up S3-compatible storage</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/outline/data:/var/lib/outline/data</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>URL</code></li>
|
||||
<li><code>SECRET_KEY</code></li>
|
||||
<li><code>DATABASE_URL</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull outlinewiki/outline:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Outline:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>outline</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,638 +0,0 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'App Catalog — DashCaddy Docs',
|
||||
description: 'Browse all 77 one-click installable apps supported by DashCaddy, organized by category with install instructions for each.',
|
||||
};
|
||||
|
||||
export default function DocsCatalogPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="App Catalog"
|
||||
intro="DashCaddy ships with 77 pre-configured application templates. Every template can be deployed from the dashboard, called via the REST API, or invoked through the MCP server. This page is the index — click any app for its dedicated install guide with prerequisites, the exact API payload shape, and post-install verification steps."
|
||||
>
|
||||
<div className="mb-8 rounded-xl border border-brand-500/30 bg-brand-500/5 p-5">
|
||||
<p className="text-sm text-surface-200">
|
||||
<strong className="text-brand-400">77 apps</strong> across <strong>17 categories</strong>. The canonical source is <code>dashcaddy-api/src/docker/app-templates.js</code>; regenerate these pages with <code>scripts/regenerate-catalog-docs.sh</code> after editing.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<h2>Media</h2>
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<a href="/docs/catalog/plex" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Plex</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Stream your personal media collection anywhere</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/jellyfin" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Jellyfin</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Free software media system - alternative to Plex</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/emby" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Emby</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Personal media server with apps for all devices</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/audiobookshelf" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Audiobookshelf</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Self-hosted audiobook and podcast server</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/navidrome" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Navidrome</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Modern music server and streamer</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/calibre-web" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Calibre-Web</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Intermediate</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Web-based ebook manager and reader</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/kavita" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Kavita</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Digital reading platform for manga, comics, and books</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/vintage-radio" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Vintage Stereo</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Glass-front console stereo that tunes curated real internet stations (SomaFM, KEXP, Radio Paradise, and more) through a beautiful analog UI</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/komga" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Komga</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Comic and manga media server with web reader</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/airsonic" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Airsonic Advanced</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Free web-based media streamer</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h2>Media Management</h2>
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<a href="/docs/catalog/seerr" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Seerr</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Media request and discovery manager for Plex, Jellyfin, and Emby</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/sonarr" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Sonarr</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Intermediate</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Smart PVR for newsgroup and bittorrent users</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/radarr" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Radarr</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Intermediate</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Movie collection manager for Usenet and BitTorrent</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/tautulli" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Tautulli</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Plex media server monitoring and statistics</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/prowlarr" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Prowlarr</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#f5a62322', color: '#f5a623' }}>Advanced</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Indexer manager/proxy for *arr applications</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/bazarr" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Bazarr</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Automatic subtitle downloader for Sonarr and Radarr</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/lidarr" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Lidarr</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Intermediate</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Music collection manager for Usenet and BitTorrent</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/readarr" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Readarr</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Intermediate</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Book and audiobook collection manager</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h2>Downloads</h2>
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<a href="/docs/catalog/qbittorrent" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">qBittorrent</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Lightweight BitTorrent client with web UI</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/transmission" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Transmission</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Lightweight BitTorrent client</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/sabnzbd" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">SABnzbd</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Intermediate</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Binary newsreader for Usenet downloads</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/jdownloader" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">JDownloader 2</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Download manager for file hosting sites</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/nzbget" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">NZBGet</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Intermediate</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Efficient Usenet downloader</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h2>Productivity</h2>
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<a href="/docs/catalog/nextcloud" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Nextcloud</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Intermediate</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Self-hosted productivity platform and file sync</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/paperless-ngx" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Paperless-ngx</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Intermediate</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Document management system - scan, organize, and search documents</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/bookstack" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">BookStack</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Intermediate</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Simple wiki and documentation platform</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/actual-budget" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Actual Budget</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Privacy-focused budgeting app with envelope budgeting</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/mealie" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Mealie</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Recipe manager and meal planner with grocery lists</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/outline" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Outline</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#f5a62322', color: '#f5a623' }}>Advanced</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Modern team knowledge base and wiki</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/trilium" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Trilium Notes</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Hierarchical knowledge base and note-taking app</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/excalidraw" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Excalidraw</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Collaborative virtual whiteboard for sketching and diagrams</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/standardnotes" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Standard Notes</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Intermediate</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">End-to-end encrypted notes app</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h2>Development</h2>
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<a href="/docs/catalog/gitea" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Gitea</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Lightweight self-hosted Git service</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/vscode-server" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">VS Code Server</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Visual Studio Code in your browser</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/jenkins" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Jenkins</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#f5a62322', color: '#f5a623' }}>Advanced</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Automation server for CI/CD pipelines</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/drone" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Drone CI</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Intermediate</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Container-native continuous delivery platform</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h2>Management</h2>
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<a href="/docs/catalog/portainer" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Portainer</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Docker container management UI</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/watchtower" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Watchtower</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Automatic Docker container image updates</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h2>Monitoring</h2>
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<a href="/docs/catalog/uptime-kuma" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Uptime Kuma</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Self-hosted monitoring tool like Uptime Robot</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/grafana" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Grafana</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#f5a62322', color: '#f5a623' }}>Advanced</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Analytics and interactive visualization platform</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/dozzle" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Dozzle</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Real-time Docker container log viewer</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/speedtest" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Speedtest Tracker</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Internet speed monitoring over time</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h2>Networking</h2>
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<a href="/docs/catalog/pihole" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Pi-hole</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Intermediate</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Network-wide ad blocker and DNS sinkhole</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/wireguard" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">WireGuard VPN</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#f5a62322', color: '#f5a623' }}>Advanced</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Fast, modern, secure VPN tunnel</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h2>DNS</h2>
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<a href="/docs/catalog/technitium" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Technitium DNS Server</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Modern DNS server with web UI for managing private zones</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/bind9" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">BIND9 DNS Server</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#f5a62322', color: '#f5a623' }}>Advanced</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Industry-standard DNS server - powerful and flexible</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/powerdns" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">PowerDNS</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Intermediate</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">High-performance DNS server with SQL backend</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/coredns" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">CoreDNS</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Intermediate</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Cloud-native DNS server - lightweight and flexible</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h2>Files</h2>
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<a href="/docs/catalog/filebrowser" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">FileBrowser</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Web-based file manager with sharing capabilities</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/syncthing" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Syncthing</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Continuous file synchronization between devices</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/sami-files" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Sami Files</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Intermediate</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Multi-server SSH file manager — browse, edit, upload, and exec across all your machines from one browser tab</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h2>Communication</h2>
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<a href="/docs/catalog/rocketchat" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Rocket.Chat</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Intermediate</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Team collaboration platform like Slack</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/matrix" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Matrix Synapse</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#f5a62322', color: '#f5a623' }}>Advanced</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Decentralized, secure messaging and collaboration</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/roundcube" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Roundcube</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Intermediate</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Modern webmail client with rich features</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/mailserver" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Docker Mailserver</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#f5a62322', color: '#f5a623' }}>Advanced</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Full-featured email server with SMTP, IMAP, spam filtering</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h2>Home Automation</h2>
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<a href="/docs/catalog/homeassistant" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Home Assistant</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Intermediate</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Open source home automation platform</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/nodered" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Node-RED</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Intermediate</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Flow-based programming for IoT and automation</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h2>Database</h2>
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<a href="/docs/catalog/postgres" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">PostgreSQL</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Intermediate</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Advanced open-source relational database</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/redis" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Redis</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">In-memory data structure store and cache</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/mongodb" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">MongoDB</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Intermediate</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Document-oriented NoSQL database</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/adminer" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Adminer</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Lightweight database management in single PHP file</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h2>Security</h2>
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<a href="/docs/catalog/dashca" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">DashCA</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">One-click root CA certificate installer for your network</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/vaultwarden" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Vaultwarden</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Lightweight Bitwarden-compatible password manager</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/authentik" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Authentik</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#f5a62322', color: '#f5a623' }}>Advanced</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Identity provider and single sign-on platform</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/crowdsec" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">CrowdSec</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Intermediate</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Collaborative intrusion prevention system</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h2>Photos</h2>
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<a href="/docs/catalog/immich" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Immich</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Intermediate</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Self-hosted Google Photos alternative</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/photoprism" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">PhotoPrism</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Intermediate</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">AI-powered photo management</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h2>Utilities</h2>
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<a href="/docs/catalog/homepage" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Homepage</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Highly customizable application dashboard</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/stirling-pdf" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Stirling PDF</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Self-hosted PDF manipulation tool - merge, split, convert, and more</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/weather" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Weather</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Live weather widget with temperature, conditions, and wind</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/homarr" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Homarr</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Sleek dashboard for all your services</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/digital-clock" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Digital Clock</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Live digital clock with time, date, and day of week</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/it-tools" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">IT Tools</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Collection of handy developer and IT tools in one place</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/changedetection" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Change Detection</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Monitor websites for changes</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/whoami" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Whoami</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Simple HTTP request debugging service</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h2>Gaming</h2>
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<a href="/docs/catalog/minecraft" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Minecraft Server</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Minecraft Java Edition dedicated server</p>
|
||||
</a>
|
||||
<a href="/docs/catalog/valheim" className="block rounded-xl border border-surface-700/50 bg-surface-900/40 p-4 transition-colors hover:border-brand-500/50 hover:bg-surface-800/50">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<h3 className="text-base font-semibold text-surface-50">Valheim Server</h3>
|
||||
<span className="shrink-0 rounded-full px-2 py-0.5 text-xs font-medium" style={{ backgroundColor: '#7cf2c022', color: '#7cf2c0' }}>Easy</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-surface-300">Valheim dedicated server for multiplayer Viking adventures</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<hr className="my-10 border-surface-700" />
|
||||
<h2>Adding your own template</h2>
|
||||
<p>Add an entry to <code>APP_TEMPLATES</code> in <code>dashcaddy-api/src/docker/app-templates.js</code> with the required fields (name, description, category, docker.image, ports, volumes), then re-run <code>scripts/regenerate-catalog-docs.sh</code>. The template will appear in the dashboard App Selector automatically.</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Paperless-ngx — DashCaddy Docs',
|
||||
description: 'Install and configure Paperless-ngx via DashCaddy. Document management system - scan, organize, and search documents',
|
||||
};
|
||||
|
||||
export default function paperlessNgxDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Paperless-ngx"
|
||||
intro="Document management system - scan, organize, and search documents"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Productivity</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Difficulty: Intermediate</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">ghcr.io/paperless-ngx/paperless-ngx:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Paperless-ngx?</h2>
|
||||
<p>Document management system - scan, organize, and search documents</p>
|
||||
<p>Paperless-ngx ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Paperless-ngx, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Paperless-ngx</strong> from the Productivity category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>paperless</code>), host port (default: <code>8095</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "paperless-ngx",
|
||||
"config": {
|
||||
"subdomain": "paperless",
|
||||
"port": 8095
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Paperless-ngx on my home host and expose it at paperless.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull ghcr.io/paperless-ngx/paperless-ngx:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Create admin account via: docker exec -it <container> python3 manage.py createsuperuser</li>
|
||||
<li>Drop documents into the consume folder for automatic import</li>
|
||||
<li>Configure tags and correspondents for organization</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/paperless/data:/usr/src/paperless/data</code></li>
|
||||
<li><code>/opt/paperless/media:/usr/src/paperless/media</code></li>
|
||||
<li><code>/opt/paperless/consume:/usr/src/paperless/consume</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>PAPERLESS_URL</code></li>
|
||||
<li><code>USERMAP_UID</code></li>
|
||||
<li><code>USERMAP_GID</code></li>
|
||||
<li><code>PAPERLESS_TIME_ZONE</code></li>
|
||||
<li><code>PAPERLESS_OCR_LANGUAGE</code></li>
|
||||
<li><code>PAPERLESS_SECRET_KEY</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull ghcr.io/paperless-ngx/paperless-ngx:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Paperless-ngx:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>paperless-ngx</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install PhotoPrism — DashCaddy Docs',
|
||||
description: 'Install and configure PhotoPrism via DashCaddy. AI-powered photo management',
|
||||
};
|
||||
|
||||
export default function photoprismDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install PhotoPrism"
|
||||
intro="AI-powered photo management"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Photos</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Difficulty: Intermediate</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">photoprism/photoprism:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is PhotoPrism?</h2>
|
||||
<p>AI-powered photo management</p>
|
||||
<p>PhotoPrism ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using PhotoPrism, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>PhotoPrism</strong> from the Photos category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>gallery</code>), host port (default: <code>2342</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/api/v1/status</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "photoprism",
|
||||
"config": {
|
||||
"subdomain": "gallery",
|
||||
"port": 2342
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy PhotoPrism on my home host and expose it at gallery.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull photoprism/photoprism:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Change admin password</li>
|
||||
<li>Import your photos</li>
|
||||
<li>Run indexing for AI features</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/photoprism/storage:/photoprism/storage</code></li>
|
||||
<li><code>/opt/photoprism/originals:/photoprism/originals</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>PHOTOPRISM_ADMIN_PASSWORD</code></li>
|
||||
<li><code>PHOTOPRISM_SITE_URL</code></li>
|
||||
<li><code>PHOTOPRISM_DATABASE_DRIVER</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull photoprism/photoprism:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with PhotoPrism:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/api/v1/status</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>photoprism</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import DocsLayout from '@/components/docs/DocsLayout';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Install Pi-hole — DashCaddy Docs',
|
||||
description: 'Install and configure Pi-hole via DashCaddy. Network-wide ad blocker and DNS sinkhole',
|
||||
};
|
||||
|
||||
export default function piholeDocsPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
title="Install Pi-hole"
|
||||
intro="Network-wide ad blocker and DNS sinkhole"
|
||||
>
|
||||
<div className="mb-6 flex flex-wrap gap-3 text-xs font-semibold">
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Category: Networking</span>
|
||||
<span className="rounded-full px-3 py-1" style={{ backgroundColor: '#68a4ff22', color: '#68a4ff' }}>Difficulty: Intermediate</span>
|
||||
<span className="rounded-full bg-surface-800 px-3 py-1 text-surface-300">Docker image: <code className="text-brand-400">pihole/pihole:latest</code></span>
|
||||
</div>
|
||||
|
||||
<h2>What is Pi-hole?</h2>
|
||||
<p>Network-wide ad blocker and DNS sinkhole</p>
|
||||
<p>Pi-hole ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Pi-hole, not installing it.</p>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>A running DashCaddy host with the dashboard accessible (default URL: <code>https://status.sami</code>; configurable via the <code>dashboardHost</code> setting in <code>config.json</code>).</li>
|
||||
<li>You must be signed in to the dashboard with an admin session, or have an API key with admin scope (<code>POST /api/v1/auth/keys</code> to create one).</li>
|
||||
<li>No special host paths required.</li>
|
||||
<li>For HTTPS with a real certificate, Technitium DNS must be configured and the host must have port 80/443 reachable from the internet. Otherwise DashCaddy will fall back to direct-IP access or a self-signed certificate.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the DashCaddy dashboard</h2>
|
||||
<ol>
|
||||
<li>Sign in at <code>https://status.sami</code> (or your host's dashboard URL).</li>
|
||||
<li>Click the <strong>📱 App Selector</strong> button on the dashboard home page.</li>
|
||||
<li>Pick <strong>Pi-hole</strong> from the Networking category.</li>
|
||||
<li>Fill in the deployment form: subdomain (default suggestion: <code>pihole</code>), host port (default: <code>80</code>).</li>
|
||||
<li>Click <strong>Deploy</strong>. DashCaddy will pull the image, create persistent volumes, write a Caddy route, create the DNS record, and wait up to 30 seconds for the container's health check (<code>/admin/</code>) to pass.</li>
|
||||
<li>When the dashboard shows the service as <strong>Running</strong>, the URL (built from your subdomain and configured TLD) is clickable. The deploy API returns a synchronous response with <code>{success, containerId, url, message, setupInstructions}</code> — there is no separate status-poll endpoint; the dashboard updates live.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Install via the REST API</h2>
|
||||
<p>Authenticate with an API key (or a JWT minted via <code>POST /api/v1/auth/jwt</code>). Send as <code>X-API-Key: dk_...</code> or <code>Authorization: Bearer <jwt></code>.</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/apps/deploy \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"appId": "pihole",
|
||||
"config": {
|
||||
"subdomain": "pihole",
|
||||
"port": 80
|
||||
}
|
||||
}'</code></pre>
|
||||
<p>The full body schema is in <code>src/utilities/validate.js</code> (Joi schema <code>appDeploy</code>). All <code>config.*</code> fields except <code>subdomain</code> are optional. Notable options:</p>
|
||||
<ul>
|
||||
<li><code>config.port</code> — host port (1–65535). Defaults to the template's <code>defaultPort</code>.</li>
|
||||
<li><code>config.mediaPath</code> — host directory to mount as the media library.</li>
|
||||
<li><code>config.plexClaimToken</code> — Plex claim token (when the upstream service needs one).</li>
|
||||
<li><code>config.useExisting: true</code> + <code>existingContainerId</code> — attach DashCaddy metadata to an already-running container instead of pulling a new image.</li>
|
||||
<li><code>config.tailscaleOnly: true</code> — restrict the reverse-proxy entry to your Tailscale network.</li>
|
||||
<li><code>config.allowedIPs</code> — array of CIDR ranges allowed past the reverse proxy.</li>
|
||||
<li><code>config.createDns: false</code> — skip DNS record creation (use when the subdomain already resolves).</li>
|
||||
<li><code>config.resources</code> — <code>{memory, cpus}</code> limits applied to the container.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Install via the AI Intent Router (returns a structured intent)</h2>
|
||||
<p>The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>curl -X POST https://status.sami/api/v1/ai/intent \\
|
||||
-H "X-API-Key: dk_your_api_key" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{ "message": "Deploy Pi-hole on my home host and expose it at pihole.sami" }'</code></pre>
|
||||
<p>The response includes <code>intent</code>, <code>action</code>, <code>parameters</code>, and <code>followup</code> — your client (or the MCP server) must then call <code>POST /api/v1/apps/deploy</code> with those parameters to actually provision the container.</p>
|
||||
|
||||
<h2>Install via the MCP Server</h2>
|
||||
<p>For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (<code>src/mcp/mcp-server.js</code>) with:</p>
|
||||
<pre className="overflow-x-auto rounded-lg bg-surface-900 p-4 text-sm"><code>DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
|
||||
DASHCADDY_API_KEY=dk_your_api_key</code></pre>
|
||||
<p>The server exposes <code>dashcaddy_deploy_app</code>. Note: this tool writes the Caddy route and creates the <code>services.json</code> entry, but it does NOT pull the Docker image or start the container. You must run <code>docker pull pihole/pihole:latest</code> and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call <code>POST /api/v1/apps/deploy</code> directly from your agent.</p>
|
||||
|
||||
<h2>Post-install: first-run checklist</h2>
|
||||
<ol>
|
||||
<li>Open the deployed URL (returned in the response as <code>url</code>, or visible in the dashboard).</li>
|
||||
<li>Complete the upstream Pi-hole setup wizard (admin account, library paths, EULA).</li>
|
||||
<li>Restore from a backup if one exists: <code>POST /api/v1/apps/{appId}/restore</code> with the backup ID from <code>GET /api/v1/backups/history</code>.</li>
|
||||
</ol>
|
||||
<h2>Volumes and persistent data</h2>
|
||||
<p>DashCaddy creates these volume mounts in the container spec:</p>
|
||||
<ul>
|
||||
<li><code>/opt/pihole/etc:/etc/pihole</code></li>
|
||||
<li><code>/opt/pihole/dnsmasq:/etc/dnsmasq.d</code></li>
|
||||
</ul>
|
||||
<p>All paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass <code>config.useExisting: false</code> AND wipe the volume. Bind mounts use the host-path conventions above (e.g. <code>/opt/plex/config</code> becomes a bind mount to the host directory of the same path).</p>
|
||||
|
||||
<h2>Environment variables</h2>
|
||||
<ul>
|
||||
<li><code>WEBPASSWORD</code></li>
|
||||
<li><code>TZ</code></li>
|
||||
</ul><p>These are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in <code>dashcaddy-api/src/docker/app-templates.js</code>.</p>
|
||||
|
||||
<h2>Updating the image</h2>
|
||||
<p>There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:</p>
|
||||
<ol>
|
||||
<li>SSH into the DashCaddy host and run <code>docker pull pihole/pihole:latest</code>.</li>
|
||||
<li>Restart the container: <code>docker restart <containerId></code> (find the ID via <code>GET /api/v1/services</code> or the dashboard).</li>
|
||||
<li>Or deploy Watchtower from this catalog (separate template that polls Docker Hub and updates other containers automatically, default schedule <code>0 0 4 * * *</code> = 04:00 daily).</li>
|
||||
</ol>
|
||||
|
||||
<h2>Backups</h2>
|
||||
<p>The default backup policy includes the entire <code>/app/data/</code> directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via <code>backup-config.json</code> — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call <code>POST /api/v1/apps/{appId}/restore</code> with a backup ID from <code>GET /api/v1/backups/history</code>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
<p>Common issues with Pi-hole:</p>
|
||||
<ul>
|
||||
<li><strong>Container won't start:</strong> check the dashboard's Logs panel. Most startup failures are permission errors on the host-path volume.</li>
|
||||
<li><strong>URL not reachable after deploy:</strong> the Caddy route was written but DNS hasn't propagated, or port 80/443 is firewalled. Check <code>GET /api/v1/dns/records</code> and <code>systemctl status caddy</code> on the host.</li>
|
||||
<li><strong>Health check timeout (deploy returns 30s after start):</strong> the container is starting but <code>/admin/</code> is not returning 200. Inspect <code>docker logs <containerId></code> directly.</li>
|
||||
</ul>
|
||||
<p>For layer-by-layer diagnostics, see the <a href="/docs/troubleshooting" className="text-brand-400 underline">Troubleshooting guide</a>.</p>
|
||||
|
||||
<hr className="my-8 border-surface-700" />
|
||||
<p className="text-sm text-surface-400">
|
||||
Template ID: <code>pihole</code>. Source: <code>dashcaddy-api/src/docker/app-templates.js</code>.
|
||||
</p>
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||