Skip to content

Caddy UI

A modern, self-hosted web UI for Caddy — manage proxy hosts, redirections, SSL certificates, and advanced routes through a clean interface, without touching config files.

License: Apache 2.0 Docker Hub Docker Pulls Go 1.26

Documentation: the CaddyUI wiki covers installation, every Settings page, proxy hosts and the Caddyfile-to-form mapping, certificates, DNS providers, the fleet, observability, the REST API and troubleshooting. The same guide ships inside the app under Docs.

Lighthouse: Performance 99 · Accessibility 100 · Best Practices 100 · SEO 100 (measured on /login from Google's PageSpeed Insights against a residential-ISP install — :v2.12.53 and later)


Install

docker pull applegater/caddyui:latest

Current Docker images target linux/amd64 and include SBOM/provenance attestations. The CaddyUI container runs non-root (UID 10001). Native Linux release archives remain available for both amd64 and arm64.

Docker remains the recommended install path, but tagged releases also include native Linux binary archives for amd64 and arm64 hosts. See the non-Docker systemd install below if you want to run CaddyUI directly in an LXC, VM, or bare-metal host.


Screenshots

Dashboard Proxy Hosts
Dashboard Proxy Hosts
Edit Proxy Host Certificates
Edit Proxy Host Certificates
Caddy Servers Snapshots
Caddy Servers Snapshots
User Management Settings & Notifications
New User Settings

Features

Analytics, certificates and local services in v2.39 – v2.52

  • OIDC / SSO login (v2.52.0) — sign in through an external identity provider (Authelia, Authentik, Keycloak, Google, …) alongside local password + TOTP; configure under Settings → Security. Standard auth-code flow with state/nonce and go-oidc token verification; verified-email matching with opt-in auto-provisioning.
  • Scheduled off-host backups (v2.52.0) — automatic SQLite snapshots to a directory on an interval (keep the newest N), plus a Back up now button, under Settings → Backup. Point the directory at a mounted volume or share to keep copies off the host.
  • Per-host rate limiting (v2.51.0) — cap a host at N requests per M seconds, per client IP (429 on excess), via the caddy-ratelimit module now built into Dockerfile.caddy. Rebuild your custom Caddy image to use it.
  • Block an IP from analytics (v2.50.0) — a visitor's drill-down page gains one-click Block on this host and Block everywhere (fleet-wide) actions; the global blocklist is managed under Settings → Security. Turns "who's probing me?" into a one-click 403.
  • Configurable verification resolver (v2.49.0) — point the deploy/readiness DNS checks at trusted, internal, or split-horizon resolvers (plain DNS servers or a DoH URL) under Settings → DNS, instead of the default Cloudflare DNS-over-HTTPS — for networks where outbound DNS to Cloudflare is filtered.
  • Drill down by status class (v2.48.0) — click a class in the Status mix card or the HTTP status breakdown legend to see the exact status codes within it, the top paths that produced them and the top visitors that received them (paths and IPs link on into their own drill-downs).
  • Deeper analytics drill-down (v2.47.0) — click a visitor (client IP) to see their top paths, status mix, exact error detail and user-agents, or click a path to see which visitors requested it. An Error detail table shows errors by exact status code + path (404 /wp-login.php ×840) instead of just a "4xx" total — handy for spotting probing/fuzzing.
  • Internal CA (tls internal) for local services (v2.46.0) — a proxy host can now be served from Caddy's built-in self-signed CA instead of ACME, for internal or local-network services where a publicly trusted certificate isn't wanted or possible. Tick Internal CA (self-signed) in the host's TLS section (Auto TLS only); CaddyUI issues via Caddy's internal issuer and skips DNS-01 for that host. No special Caddy build required — clients just need to trust Caddy's root CA.
  • Live TLS probe for file-path certificates (v2.39.0) — a certificate stored as a file path inside the Caddy container now shows expiry, issuer and days left from what the node actually serves, instead of nothing; Inspect gains a Live check card and warns when a renewed file has not been reloaded by Caddy.
  • Fleet sync for every certificate (v2.41.0) — Sync configuration and Also configure on copy PEM and file-path certificates too (a readable file-path certificate travels as stored PEM), and hosts created on a target keep referencing the copied certificate instead of falling back to Auto TLS.
  • Export a managed certificate to a directory (v2.42.0) — mount the node's Caddy data volume, set its Data directory on the Caddy Fleet entry, and CaddyUI copies the chain and key out after every issuance and renewal for a mail server or any other service, with Export now on the certificate form.
  • Changes Caddy rejects are refused up front (v2.42.1) — proxy hosts, redirections and certificates are validated against Caddy before they are saved (for example a certificate file Caddy cannot open), and a sync that fails afterwards shows an amber banner on every page with the exact error and Retry sync now.
  • Advanced config understands reverse_proxy { … } (v2.40.0 – v2.42.2) — sub-directives such as flush_interval -1, header_up and transport http { … } are merged into the host's own handler; bare sub-directives, JSON-style option names and misplaced transport options are repaired instead of rejected.
  • Analytics retention that actually runs (v2.43.0 / v2.43.1) — the raw-event prune loop now starts (it never had), retention is a setting, and Settings → Analytics shows a Storage panel with file size, oldest event and reclaimable space plus Prune now and Reclaim space.
  • Settings is a set of pages (v2.44.0) — General, Notifications, DNS, Security, Analytics, Integrations, AI assistant and Backup, each saving only its own settings.
  • Gandi managed DNS (v2.45.0) — a LiveDNS Personal Access Token drives A-record management and DNS-01 issuance; Dockerfile.caddy builds the caddy-dns/gandi module.
  • Security headers really are sent (v2.45.2) — hosts with the Security Headers bundle enabled were serving none of them, because Caddy applies header deletes after sets; fixed on the next sync.

Post-apply checks, monitoring and hardening in v2.26 – v2.38

  • Expectations: post-apply checks with automatic rollback (v2.38.0) — declare the requests a host must keep answering (method, path, expected status or Location, maximum latency, valid TLS). CaddyUI runs them after every sync; if one fails it loads the previous live config back into Caddy, pauses automatic syncs for that server and shows a banner with Re-apply now / Keep the rolled-back config. Suggested by Caddy's maintainer.
  • Each node ships its logs to a target it can reach (v2.37.0) — a per-node Log ingest target on the Caddy Fleet entry, so remote nodes' Analytics, certificate status and Server Logs work beyond the Docker network.
  • Advanced routes on custom ports keep their listener (v2.36.1) — a :7070 { … } block becomes its own Caddy server instead of being folded into :443; the Certificates page also lists Advanced routes' Auto-TLS domains (v2.36.2).
  • Monitoring you can shape — health checks with any HTTP method (v2.36.0); Automatic / Custom / Off status monitoring per host, with Custom path, method, expected status, interval and timeout (v2.28.0); Off silences all three probes (v2.35.0); an always-present link to each host's public health history (v2.35.1).
  • Node-local hosts and routes (v2.33.0) — resources whose upstream only resolves on one node (a Docker service name, a VPN address) are excluded from fleet sync and Also deploy to, and counted rather than silently dropped.
  • Hardening — a CSRF token on every state-changing request (v2.29.0); a Content-Security-Policy on CaddyUI's own pages and byte-identical generated config (v2.30.0); no third-party scripts at runtime (v2.27.0).
  • Everyday improvements — live Caddyfile and JSON preview on the proxy host form (v2.26.0); Also deploy to for Advanced routes (v2.27.0); the Analytics page four times faster at scale (v2.31.0); full dates on Server Logs and Live Traffic with a Clear that sticks (v2.35.5, v2.37.1).

Fleet observability in v2.25

  • Actual node attribution — Live Traffic, Analytics, per-host drill-downs, and CSV exports identify the Caddy instance that handled each request, including shared hostnames behind a load balancer.
  • Ephemeral Server Logs — administrators can stream structured runtime logs from one managed node at a chosen level. Full capture is memory-only, preserves normal Caddy output, and stops automatically after 15 minutes.
  • Certificate lifecycle states — managed ACME certificates show obtaining, renewing, retrying, issued, revoked, and failure states with the latest Caddy message instead of an unconditional healthy badge.

What's new in v2.21

  • Fleet file access logs — independently write native JSON or console access logs to selected Caddy servers with HTTP/HTTPS scope and rotation controls while preserving CaddyUI Visitor Analytics.
  • CrowdSec request protection — configure the LAPI, masked bouncer key, streaming, refresh, hard-fail, selected environments, host/path exclusions, module validation, and live connection testing from Settings.
  • Correct client IPs — configure client_ip_headers alongside trusted proxy ranges so logging and CrowdSec evaluate Caddy's resolved client address behind Cloudflare or another load balancer.
  • Batteries-included custom Caddy — Dockerfile.caddy includes the CrowdSec HTTP bouncer module and the repository Compose stack persists /var/log/caddy.

Certificate cleanup in v2.20.1

  • Unused certificates are now named — the Operations recommendation identifies affected PEM/file-path certificates and opens a filtered cleanup view with an Unused marker on each row.
  • No false positives for managed ACME — standalone and wildcard certificates automated by Caddy are no longer classified as unused simply because they are not attached through a resource certificate ID.
  • Dismiss without permanent silence — administrators can hide the recommendation for the selected environment. It returns automatically when the set of unused certificates changes.

Enterprise workflows in v2.20

  • Guided first-run journey — create the administrator account, verify the Caddy control plane, secure access, optionally configure DNS automation, and publish the first service through one live readiness guide.
  • Publish-service wizard — new proxy hosts follow Hostname → Upstream → Policy → Review, with native validation and a deployment summary before anything is written to Caddy.
  • Enterprise semantic colors — blue is action/current context, green is healthy/enabled/completed, amber is pending/degraded/maintenance, red is failure/offline/destructive, violet is TLS/automation/managed policy, and gray is disabled/unknown/informational.
  • Professional fleet operations — Caddy environments are organized as an inventory with health, endpoint, management policy, last contact, and scoped actions; connecting a server follows Identify → Connect → Authorize.
  • Faster daily workflows — a global Create menu, persistent environment context, consistent Publish service language, and a one-click advanced editor keep both new and experienced operators moving.

The guide reads real configuration state rather than a one-time “completed” flag. DNS automation is optional and never prevents an otherwise operational environment from reaching 100% readiness.

Database and performance improvements in v2.19

  • MariaDB backend — keep embedded SQLite for simple installs or connect CaddyUI to MariaDB for concurrent enterprise workloads, replication, and platform-managed backups.
  • Safe database migration — copy an existing SQLite installation into an empty MariaDB database with a read-only, batched migration command. Large analytics histories can be intentionally skipped.
  • Faster Proxy Hosts — request counters now use domain-scoped index lookups instead of scanning the complete analytics history, and live probes wait until after the initial browser paint.
  • Database-aware operations — automatic schema migrations, portable sessions/retention/rollups, MariaDB-specific backup guidance, a Compose overlay, and dedicated MariaDB CI coverage.

Enterprise operations UI in v2.18

  • Enterprise operations center — the dashboard now leads with the active environment, route inventory, live system and upstream health, prioritized risks, fleet status, and recent configuration activity instead of a loose collection of cards.
  • Workflow-first application shell — navigation is regrouped around Traffic, Observe, Configuration, Developer, and Administration workflows. The current Caddy environment is always visible and switching servers is faster.
  • Visible command search and primary actions — search resources from the persistent top bar, create a proxy host, sync Caddy, or manage the fleet without hunting through menus. Read-only viewers never see the create action.
  • Shared enterprise design system — semantic surfaces, status treatments, panels, buttons, metrics, tables, focus states, and responsive rules now provide a durable foundation for the rest of the product.
  • First-class mobile operations — one compact mobile toolbar, responsive route/system metrics, stacked attention queues, fleet cards, and mobile host cards preserve the full operational picture without horizontal scrolling.
  • Theme and accessibility coverage — the redesign supports light, dark, Carbon Orange, Forest, Rose, and Indigo themes, with keyboard focus visibility and reduced-motion handling.

Certificate automation in v2.17

  • Visible Auto TLS certificate selection — the Caddy-managed (ACME) table now names the managed wildcard used by each covered domain and labels uncovered hosts Direct certificate, so the UI no longer implies every hostname received a separate certificate. (v2.17.3)
  • Enforced Auto wildcard reuse — Auto TLS suppresses redundant exact-host issuance when a managed wildcard covers the name. (v2.17.2)
  • Live per-server renewal status — edit a managed certificate to inspect each server's live expiry, issuer, deployment, and renewal health. (v2.17.2)
  • Multi-server managed wildcard reuse — copy managed certificate definitions to selected Caddy instances, or let proxy Also deploy to automatically discover and replicate a covering wildcard on each target. Every server keeps its own ACME order and private key. (v2.17.1)
  • Standalone managed wildcard certificates — Certificates → New → Managed ACME (DNS-01) obtains and renews *.example.com or *.sub.example.com without a proxy host, pasted PEM, or exported private key. (v2.17.0)

See the complete managed wildcard workflow below.

Earlier highlights

  • Import SSL from Porkbun — new button on the Certificates page (visible when Porkbun credentials are saved) that opens a domain picker and pulls the SSL bundle directly from your Porkbun account into CaddyUI as a stored PEM certificate. (v2.14.0)

  • Porkbun API v3 full compatibility — fixed JSON type mismatches where the Porkbun API returns integer fields (whoisPrivacy, autoRenew, securityLock, apiAccess, notLocal) as quoted strings; all domain and DNS decode paths now handle both forms transparently. SSL retrieve. (v2.14.1, v2.14.2)

  • ⌘K / Ctrl+K command palette — global search across every proxy host, redirection, raw route, and certificate. ↑/↓ Enter Esc. Color-coded type pills. (v2.11.5)

  • Bulk multi-select on every list page — checkbox + select-all + floating Enable / Disable / Delete bar on /proxy-hosts, /redirection-hosts, /raw-routes, and /certificates. (v2.11.6, .9, .10)

  • Drag-to-reorder rows — HTML5 ⠿ handle on /proxy-hosts and /redirection-hosts. (v2.11.11)

  • Live route preview on the proxy-host edit form — the exact Caddy route JSON and a Caddyfile excerpt your form would push, refreshing as you type. (v2.11.13, v2.26.0)

  • Multi-server health widget on the dashboard — one card per registered Caddy server with status, host count, version, last seen. (v2.11.16)

  • AI assistant — bring your own backend (v2.11.15, v2.12.36+) — opt-in floating chat button that answers Caddy / TLS / DNS questions and writes production-grade Caddyfile snippets on demand. Pick any backend in Settings → AI assistant: Ollama (local) for fully-offline on your GPU, Ollama Cloud for hosted MoE models that won't fit on a homelab card (qwen3-coder:480b, gpt-oss:120b, etc.), Anthropic Claude (Haiku 4.5 / Sonnet 4.6 / Opus 4.7), or any OpenAI-compatible API (also covers OpenRouter, Groq, Together, vLLM, LM Studio). Conversation memory (multi-turn), markdown rendering, and a custom system prompt setting are shared by every backend.

  • AI auto-fill — chat your hosts into existence (v2.12.11+) — describe what you want in plain English ("set up nextcloud at cloud.example.com pointing to nextcloud:80", "redirect old.example.com to new.example.com permanently") and the assistant emits a structured create_proxy_host(...) / create_redirection(...) tool call. The chat panel renders a confirmation card with the exact arguments — click Apply and the resource is created, the form is filled in for you, and Caddy is auto-synced. Every AI-driven exec writes an ai_tool_call activity-log entry so admins can audit what the AI did. Works on Claude 4.x, GPT-4, qwen2.5+, llama3.1+, and gemma2.

  • DNS-01 certificate issuance — select Managed DNS on a proxy, redirection, or advanced route and CaddyUI reuses that credential profile for Let's Encrypt DNS-01. Supports Cloudflare, Porkbun, Namecheap, GoDaddy, DigitalOcean, Hetzner, Amazon Route 53, and Gandi; the connected Caddy needs the matching caddy-dns plugin. Wildcards are supported, and public A-record creation can be disabled for internal/split-DNS services. (Route 53 added in v2.23.0; DNS-01-only mode in v2.23.1)

  • Per-hostname DNS-record pre-flight on multi-domain routes — checklist showing which records will be created vs already exist. (v2.12.1)

  • Managed DNS on redirections — closes the long-standing gap where redirects had no DNS plumbing. Now creates A records per hostname on save, deletes on row removal. (v2.12.2)

  • Settings pages — General, Notifications, DNS, Security, Analytics, Integrations, AI assistant and Backup, each with its own save. (v2.44.0; per-section save since v2.12.6)

  • Active-server-scoped dashboard cards — Requests / Visitors / Bandwidth Today now reflect only the server selected in the picker, including traffic to wildcard SAN hostnames. (v2.12.8, v2.12.9)

Managed wildcard certificates (v2.17+)

Use one DNS-01 wildcard across matching proxy hosts without exporting or copying its private key:

  1. Go to Settings → DNS and save credentials for your DNS provider.
  2. Go to Certificates → New, choose Managed ACME (DNS-01), select that credential profile, and enter a subject such as *.example.com.
  3. If you manage multiple Caddy servers, select them under Also configure on. Each server performs its own ACME order and keeps its own private key.
  4. Leave matching proxy hosts on Auto TLS. CaddyUI detects the covering wildcard and prevents redundant exact-host certificate orders.
  5. Edit the managed certificate later to see live deployment, issuer, expiry, and renewal health for every configured server.

Your Caddy build must include the matching caddy-dns provider module. The easiest path is the prebuilt applegater/caddyui-caddy image (all eight DNS providers, the CrowdSec bouncer and the rate-limit module); it's the published build of Dockerfile.caddy, which you can also build yourself. The stock caddy:2-alpine image does not include these modules.

Routing

  • Proxy Hosts — point domains at upstream services with one-click TLS via Caddy's automatic HTTPS
  • Redirections — 301/302/307/308 redirect rules across hostnames; path-based rules with redirect_rules; sunset-date 410 conversion; wildcard subdomain capture
  • Advanced Routes — import raw Caddyfile blocks or write JSON directly for anything the UI can't model; a block on a custom port (:7070 { … }) keeps its own listener, and routes can be deployed to other nodes with Also deploy to
  • Certificates — upload custom PEM / path-based certificates or create standalone Caddy-managed DNS-01 wildcard certificates; live TLS probe for expiry of file-path certificates, export of a managed certificate's chain and key to a directory after every renewal, fleet sync for every kind, expiry alerts via email or webhook
  • Managed DNS — optionally create the A record for a new proxy host, redirection, or advanced route without leaving CaddyUI; records created by CaddyUI are auto-deleted with the row. The same credentials drive DNS-01 certificate issuance even when public A-record creation is disabled. Providers: Cloudflare, DigitalOcean, Hetzner, Porkbun, GoDaddy, Namecheap, Amazon Route 53, Gandi. Zone lookup is automatic from the domain.
  • Paste Caddyfile import — convert a Caddyfile into managed proxy hosts / redirections / advanced routes; smart classifier auto-routes each block to the right table (v2.10.7+). Re-classify button rescues older imports
  • Import from Caddy — pull your existing live Caddy config into the DB on first run
  • Per-host options surface (~70 fields) — request / response headers (X-Forwarded-*, security headers, cache hints), path-based upstream overrides, multi-upstream routing, active health checks, upstream TLS settings, request blocking (deny by path / query / user-agent / method), plus an Advanced config box for extra Caddyfile directives and a reverse_proxy { … } block merged into the host's own handler
  • Post-apply checks — expectations per host, run after every sync, with automatic rollback of the live config when one fails
  • Validated before saving — changes Caddy would reject are refused with a plain explanation, and a sync that fails afterwards is shown on every page until it succeeds
  • Branded error pages — CaddyUI-styled 404 / 502 / 503 / 504 pages are injected into Caddy automatically

Multi-server

  • Remote Caddy management — manage multiple Caddy instances from a single UI; switch with a dropdown. Edge hosts only need Caddy — no CaddyUI container required (see Agent mode)
  • Per-server scoping — proxy hosts, redirections, routes, and certificates are all scoped to the active server; cross-server conflicts can't happen
  • Fleet sync and Also deploy to — copy a whole environment, or one host, redirection, route or certificate, onto other nodes; node-local resources stay where they belong, and each node has its own public IP, log ingest target and data directory

Access control

  • Three-role RBAC — admin (full control), user (manage their own resources), view (read-only). Admin-only pages: Users, Groups, Settings, Caddy Servers, Snapshots
  • Per-user ownership — proxy hosts, redirections, advanced routes, and certificates each belong to one user; only the owner, their group peers, and admins can edit or delete them. Admins can reassign ownership from any resource's edit form
  • Groups (v2.7.4, collaborative since v2.52.5) — admin bundles user-role accounts into a team; every member sees every other member's resources in their list views, with a Team chip so it's clear which rows are "mine" vs. "my teammate's". Since v2.52.5 a shared row is also manageable by its group peers, not just visible — global (admin-owned) rows stay admin-only
  • 2FA / TOTP — per-user time-based one-time passwords
  • Login CAPTCHA — optional Cloudflare Turnstile or reCAPTCHA v3 gate on the login form
  • Hardened by default — CSRF tokens on every state-changing request, a Content-Security-Policy on CaddyUI's own pages, no third-party scripts at runtime, admin IP allowlist, login attempt limits

Observability

  • Fleet observability (v2.25.0) — attribute requests to the node that actually handled them, stream temporary in-memory runtime logs from Observe → Server Logs, and surface per-node ACME progress/errors from structured Caddy TLS events
  • Prometheus metrics (v2.24.0) — enable base HTTP metrics, per-host labels, and Caddy 2.11+ catch-all host observation on selected fleet servers. CaddyUI validates each target, preserves metrics it does not own, and shows the protected Admin URL's /metrics scrape target
  • Visitor analytics (v2.7.0) — opt-in per-host traffic counters; top hosts, 24 h sparkline, status-code mix, unique visitors. Per-server filter for multi-Caddy fleets. The Caddy log writer uses soft-start failover so Caddy can boot while CaddyUI is offline. Raw events are kept for a configurable number of days (default 30) with per-day rollups for long-range charts, and a Storage panel reclaims space (v2.43.0)
  • Upstream health — live health check per proxy; polls Caddy's own admin API so Docker-internal hostnames work correctly
  • App health — detects whether the upstream actually responds, not just whether its TCP port is open
  • Activity log — every create / edit / delete / sync action is logged with actor, timestamp, and resource

Certificate lifecycle monitoring and Server Logs share the structured ingest target configured under Settings → Analytics, even when visitor analytics is disabled. Every managed Caddy node must be able to reach that private host and port (default caddyui:9019 on the repository Docker network); a node on another host gets its own Log ingest target on its Caddy Fleet entry (v2.37.0). Do not publish the ingest listener to the internet.

Operational

  • SQLite or MariaDB — embedded SQLite by default; optional MariaDB backend for concurrent enterprise workloads, replication, and managed backups
  • Snapshots — automatic and manual Caddy configuration snapshots on both backends; one-click full-file backup on SQLite
  • Email notifications — SMTP support (STARTTLS / TLS / plain) for cert-expiry and upstream-health alerts
  • Webhook notifications — generic JSON POST for cert-expiry (pair with any notifier that accepts webhooks)
  • Update notifications — the Operations dashboard announces a newer Docker Hub release
  • Settings pages — eight focused pages instead of one long form, each saving only its own settings
  • Dark mode — toggleable, remembers your choice; system preference respected on first visit
  • PWA — installable on desktop and mobile; offline-capable service worker

AI Assistant

CaddyUI ships an opt-in chat assistant that knows the entire CaddyUI surface area — every page, every Settings field, every proxy-host form section — and can both answer questions about Caddy / TLS / DNS and auto-fill new resources for you via tool calling.

Pick a backend

Go to Settings → AI assistant and choose one of:

Backend Best for What you need
Ollama (local) Fully-offline, runs on your GPU, no cloud calls An Ollama container reachable from CaddyUI; recommended models: qwen2.5:14b, qwen2.5-coder:14b, gemma2:9b, llama3.1:8b
Ollama Cloud Hosted MoE models that won't fit on a homelab GPU API key from ollama.com/settings/keys; models: qwen3-coder:480b-cloud, gpt-oss:120b, deepseek-v3.1:671b
Anthropic Claude Strongest reasoning + Caddy knowledge out of the box API key from console.anthropic.com; models: claude-haiku-4-5-20251001 (fast), claude-sonnet-4-6 (balanced), claude-opus-4-7 (max)
OpenAI-compatible OpenAI itself, OpenRouter, Groq, Together, self-hosted vLLM, LM Studio Base URL + API key + model name

Switching providers preserves the inactive providers' credentials, so you can flip back and forth without re-entering keys.

What you can ask

Knowledge questions — the assistant has the full CaddyUI app map in its system prompt, so it points you at the exact page → section → field for whatever you're trying to do:

"How do I strip the X-Powered-By header globally?"
→ Settings → General → Globally stripped response headers.

"Where do I configure SSE flushing?"
→ Proxy host form → Timeouts → Stream flush interval ms (use -1).

Caddyfile help — production-grade snippets, not 3-line stubs. The default system prompt steers the model toward encode zstd gzip + path blocking + security headers + the X-Forwarded-* trio. Strip back to minimum only when you ask for "just the basics".

AI auto-fill (tool calling)

Describe what you want and the assistant calls a tool to create it:

"Set up nextcloud at cloud.example.com pointing to nextcloud:80 with auto-SSL"
→ emits create_proxy_host(domains="cloud.example.com", forward_host="nextcloud", forward_port=80, ssl_enabled=true, ssl_forced=true).

The chat panel renders a confirmation card with the exact arguments. You click Apply and CaddyUI:

  1. Creates the proxy host (or redirection).
  2. Auto-syncs Caddy with the new config.
  3. Writes an ai_tool_call row to the activity log so admins can see exactly what happened.

Tools currently available:

  • create_proxy_host(domains, forward_scheme, forward_host, forward_port, ssl_enabled, ssl_forced)
  • create_redirection(domains, forward_scheme, forward_domain, forward_http_code, preserve_path)

Models with native tool support — Claude 4.x, GPT-4 / GPT-4o, qwen2.5+, llama3.1+, gemma2 — will use these. Older or smaller models silently ignore the tools field and just chat.

Custom system prompt

If you want a different persona, in-house naming conventions, or a leaner / chattier voice, paste your own steering text into Settings → AI assistant → Custom system prompt. Leaving it blank uses the built-in default that has the full CaddyUI knowledge map and the production-grade Caddyfile examples.

Privacy

API keys are stored in CaddyUI's selected database. The Settings UI never renders saved keys back to the form (a •••••••• placeholder shows when one is set; leave the field blank to keep the existing key). Local Ollama means the conversation never leaves your machine; cloud backends send the conversation to your chosen provider per their respective terms.

Community integrations

  • CaddyUI-MCP — a community-maintained MCP server that wraps CaddyUI's /api/v1 REST API for agent-driven automation. GET /api/v1/servers supplies the fleet ID/name/type mapping integrations need without scraping HTML. Use a dedicated CaddyUI user and the narrowest API token scope that fits your workflow.

Quick Start

Docker Compose (recommended)

services:
  caddy:
    # Batteries-included Caddy: every CaddyUI DNS provider (for ACME DNS-01),
    # the CrowdSec bouncer, and the rate-limit module. Published build of
    # Dockerfile.caddy. Swap for a stock/custom Caddy if you don't need those.
    image: applegater/caddyui-caddy:stable
    container_name: caddyui-caddy
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
      - "443:443/udp"
    volumes:
      - caddy_data:/data
      - caddy_config:/config
    environment:
      CADDY_ADMIN: 0.0.0.0:2019
    command: >-
      mkdir -p /config/caddy;
      [ -f /config/caddy/autosave.json ] || echo '{}' > /config/caddy/autosave.json;
      exec caddy run --config /config/caddy/autosave.json --resume --adapter json
    networks:
      - caddyui

  caddyui:
    image: applegater/caddyui:latest
    container_name: caddyui
    restart: unless-stopped
    depends_on:
      - caddy
    ports:
      - "8081:8080"
    volumes:
      - caddyui_data:/data
    environment:
      CADDYUI_DB: /data/caddyui.db
      CADDYUI_LISTEN: :8080
      CADDY_ADMIN_URL: http://caddy:2019
    networks:
      - caddyui

volumes:
  caddy_data:
  caddy_config:
  caddyui_data:

networks:
  caddyui:
    driver: bridge

💡 Fresh install: On first boot Caddy has no saved config yet and would crash-loop without one. The command above seeds an empty {} config automatically on first start — you don’t need to do anything extra.

Open http://localhost:8081 and complete the first-run setup (create an admin account).

Docker Run (standalone)

docker run -d \
  --name caddyui \
  -p 8081:8080 \
  -v caddyui_data:/data \
  -e CADDY_ADMIN_URL=http://your-caddy-host:2019 \
  applegater/caddyui:latest

MariaDB (optional enterprise backend)

SQLite remains the zero-configuration default. MariaDB is available for larger installations that need concurrent UI reads, continuous analytics ingestion, database replication, or platform-managed backups.

Use the included Compose overlay:

export CADDYUI_MARIADB_PASSWORD='replace-with-a-long-random-password'
export CADDYUI_MARIADB_ROOT_PASSWORD='replace-with-a-different-random-password'
docker compose -f docker-compose.yml -f docker-compose.mariadb.yml up -d

For an existing MariaDB server, configure CaddyUI directly:

environment:
  CADDYUI_DB_DRIVER: mariadb
  CADDYUI_DB_DSN: caddyui:password@tcp(mariadb.example.internal:3306)/caddyui

Create the database and user before starting CaddyUI. CaddyUI creates and migrates its tables automatically. The DSN is never written to application logs.

To copy an existing SQLite installation, first create a timestamped copy of caddyui.db, stop the normal CaddyUI container, start the empty MariaDB service, and run:

docker compose -f docker-compose.yml -f docker-compose.mariadb.yml up -d mariadb
docker compose -f docker-compose.yml -f docker-compose.mariadb.yml run --rm --no-deps \
  caddyui migrate-db \
  --from-sqlite /data/caddyui.db
docker compose -f docker-compose.yml -f docker-compose.mariadb.yml up -d

The source SQLite file is opened read-only and the MariaDB destination must be empty. Add --skip-analytics to migrate configuration, users, certificates, health history, and activity while leaving large access_events and access_daily tables behind. This is strongly recommended when the SQLite database grew primarily because of visitor analytics.

MariaDB backups are managed with mariadb-dump, snapshots, or your database platform's backup/PITR tooling. Caddy configuration snapshots inside CaddyUI continue to work on both backends.

Non-Docker systemd install

Tagged GitHub releases include linux/amd64 and linux/arm64 tarballs with the caddyui binary and a sample caddyui.service unit. This is useful for LXC/Proxmox labs, VMs, or hosts where you prefer to run CaddyUI directly under systemd.

  1. Install Caddy separately and make sure its admin API is reachable from the CaddyUI host. For a same-host install the default is http://127.0.0.1:2019.
  2. Download the archive for your CPU architecture from the latest GitHub release.
  3. Extract the archive and run the packaged installer:
tar -xvf ./caddyui_vX.Y.Z_linux_ARCH.tar.gz
cd caddyui_vX.Y.Z_linux_ARCH
./install.sh

The packaged service stores SQLite data at /var/lib/caddyui/caddyui.db, listens on 127.0.0.1:8080, talks to Caddy at http://127.0.0.1:2019, and binds the optional visitor-analytics ingest listener to 127.0.0.1:9019. The ingest listener only matters if you enable CaddyUI visitor analytics; set CADDYUI_INGEST_LISTEN= in a systemd drop-in to disable it. Override these values if your Caddy admin API is on another host:

sudo systemctl edit caddyui
[Service]
Environment=CADDYUI_LISTEN=0.0.0.0:8080
Environment=CADDY_ADMIN_URL=http://10.8.0.2:2019
# Optional: disable visitor-analytics ingest if you do not use analytics.
Environment=CADDYUI_INGEST_LISTEN=

Then restart:

sudo systemctl restart caddyui

Agent mode (edge-only Caddy, no CaddyUI)

For multi-host setups you only need one CaddyUI container. Every other host — the "agent" or "edge" nodes — runs only Caddy, and the central CaddyUI manages them remotely through Caddy's admin API (typically tunneled over WireGuard or Tailscale).

On each edge host:

services:
  caddy:
    image: applegater/caddyui-caddy:stable
    container_name: caddy
    restart: unless-stopped
    # --resume is required so admin-API pushes persist across Caddy restarts.
    command: >-
      mkdir -p /config/caddy;
      [ -f /config/caddy/autosave.json ] || echo '{}' > /config/caddy/autosave.json;
      exec caddy run --config /config/caddy/autosave.json --resume --adapter json
    ports:
      # Bind the admin API to your private tunnel IP (WireGuard / Tailscale).
      # Do NOT expose :2019 on a public interface.
      - "10.8.0.2:2019:2019"
      - "80:80"
      - "443:443"
      - "443:443/udp"
    volumes:
      - caddy_data:/data
      - caddy_config:/config

volumes:
  caddy_data:
  caddy_config:

Then in the central CaddyUI, go to System → Caddy Servers → Add Server and point it at http://10.8.0.2:2019 (or whatever private address the edge listens on). All proxy hosts, certificates, and routes for that edge are managed from the central UI — no database, no UI container, no extra port to expose on the edge.

Why --resume? In default mode Caddy loads only the Caddyfile at boot and discards anything pushed to the admin API. With --resume Caddy boots from autosave.json (the last live config it received via the admin API), so CaddyUI's pushes survive docker compose restart.

Fresh install: On first boot no autosave.json exists yet, so Caddy would crash-loop. The command above seeds an empty {} config on first start so Caddy comes up cleanly without any extra steps.


Environment Variables

Variable Default Description
CADDYUI_DB_DRIVER sqlite Database backend: sqlite or mariadb
CADDYUI_DB /data/caddyui.db Path to the SQLite database
CADDYUI_DB_DSN (unset) MariaDB DSN; required when CADDYUI_DB_DRIVER=mariadb
CADDYUI_LISTEN :8080 Listen address
CADDYUI_INGEST_LISTEN :9019 Structured Caddy log ingest for analytics, certificate lifecycle, and temporary runtime streams; set empty to disable all three
CADDY_ADMIN_URL http://caddy:2019 Caddy admin API base URL
CADDYFILE_PATH /etc/caddy/Caddyfile Path to Caddyfile (optional)
CADDYUI_SYNC_ON_START (unset) Set to 1 to push DB state to Caddy on startup

Configuration

All configuration is done through the web UI. No config files needed beyond the environment variables above.

First Run

  1. Open the UI at your configured port.
  2. Create the first administrator account in the account setup wizard.
  3. Continue into Getting Started, which checks the real state of the selected environment.
  4. Verify the bootstrap Caddy connection created from CADDY_ADMIN_URL.
  5. Enable administrator TOTP and save the recovery codes.
  6. Add DNS credentials only when you need managed records or DNS-01/wildcard certificates.
  7. Select Publish a service and complete Hostname → Upstream → Policy → Review.

Add additional Caddy environments under Administration → Caddy Fleet. The readiness guide stays available after setup, so it can be used as an operational checklist whenever the environment changes.

SMTP Notifications

Configure under Administration → Settings → Email (SMTP):

  • Supports STARTTLS (port 587), implicit TLS (port 465), and plain (port 25)
  • Cert-expiry emails fire once per 24 h per domain when within the configured threshold
  • Upstream health emails fire on state change (healthy → down, down → recovered), checked every 5 minutes

Multi-Server

Add additional Caddy instances under Administration → Caddy Fleet. Switch the active environment with the selector in the application shell. All proxy hosts, redirections, routes, and certificates are scoped per server.

To deploy the selected environment's complete managed routing configuration to another Caddy, choose Sync from current on the target fleet row. The sync is a non-destructive merge: it creates missing proxy hosts, redirects, advanced routes, and managed certificate definitions; updates resources paired by earlier deployments; and leaves target-only resources in place. Target DNS records, custom certificate selections, ACME private keys, and uploaded private keys remain server-specific. Repeating either a full sync or an individual form's Also deploy to action is idempotent and does not create duplicate routes.

Edge / remote hosts do not need to run a CaddyUI container — just Caddy with its admin API reachable over a private network (WireGuard, Tailscale, VPC). See Agent mode for the minimal compose file.


Building from Source

CaddyUI can be built and run directly on Linux without Docker. Install Caddy separately first, then make sure its admin API is reachable from the CaddyUI host. For same-host installs, Caddy's default admin endpoint is usually http://127.0.0.1:2019.

Install Go 1.25 or newer, then build:

git clone https://github.com/X4Applegate/caddyui.git
cd caddyui
git checkout "$(git tag --sort=-v:refname | head -n1)"
version="$(git describe --tags --always --dirty)"
CGO_ENABLED=0 go build \
  -trimpath \
  -ldflags "-s -w -X main.Version=${version}" \
  -o caddyui \
  ./cmd/caddyui

To test it before installing:

mkdir -p ./data
CADDYUI_DB="$PWD/data/caddyui.db" \
CADDYUI_LISTEN=127.0.0.1:8080 \
CADDY_ADMIN_URL=http://127.0.0.1:2019 \
CADDYUI_INGEST_LISTEN=127.0.0.1:9019 \
./caddyui

Open http://127.0.0.1:8080 and complete the first-run setup.

To install your locally built binary as a systemd service:

id -u caddyui >/dev/null 2>&1 || sudo useradd --system --home /var/lib/caddyui --shell /usr/sbin/nologin caddyui
sudo install -d -o caddyui -g caddyui -m 0750 /var/lib/caddyui
sudo install -m 0755 caddyui /usr/local/bin/caddyui
sudo install -m 0644 packaging/systemd/caddyui.service /etc/systemd/system/caddyui.service
sudo systemctl daemon-reload
sudo systemctl enable --now caddyui

Docker Build

docker build --build-arg VERSION=v1.0.0 -t caddyui:v1.0.0 .

Dependencies

  • Go 1.25+
  • Caddy 2.x with the admin API enabled (default)
  • No external database required — embedded SQLite is the default; MariaDB is optional

Architecture

cmd/caddyui/        Entry point, env config, startup
internal/
  auth/             Session, password hashing, TOTP
  caddy/            Admin API client, Caddyfile parser, importer
  db/               SQLite/MariaDB init, migrations, and data migration
  models/           Data types and DB queries
  server/           HTTP handlers, routes, notifiers, health poller
web/
  templates/        Go html/template pages
  static/           CSS, icons, PWA manifest & service worker

CaddyUI stores state in SQLite by default or MariaDB when configured. It communicates with Caddy exclusively through Caddy's HTTP admin API — no SSH and no direct Caddy configuration-file manipulation.


Upgrading

  1. Pull the new image tag from Docker Hub.
  2. Recreate the container (Portainer: Recreate → enable Re-pull image; CLI: docker compose pull && docker compose up -d).
  3. Database migrations run automatically on startup.
  4. Check the CHANGELOG for any breaking changes.

AI Assistance Disclosure

This project is developed with assistance from Claude (Anthropic). Claude helps with debugging, feature implementation, code review, and documentation. All code is reviewed and tested by the project maintainer before release.

Bug reports and issues are triaged by the maintainer with Claude's assistance. If you find a bug, please open an issue — it will be looked at.

Note on privacy: No proprietary code, credentials, database contents, or user data are ever shared with Claude. Only code structure and logic are discussed.


License

CaddyUI is open source under the Apache License 2.0.

  • Free for everyone — personal, commercial, and internal use alike.
  • Modify and redistribute freely, including as part of your own products or hosted/managed services.
  • Patent grant included, and your contributions are welcome under the same license.

The only conditions are the usual Apache-2.0 ones: keep the license and copyright notices, and state significant changes you make.


Contributing

See CONTRIBUTING.md.

Security

See SECURITY.md for how to report vulnerabilities.

Code of Conduct

See CODE_OF_CONDUCT.md.

About

A modern, self-hosted web UI for Caddy — manage proxy hosts, redirections, SSL certificates, and advanced routes through a clean interface, without touching config files.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

239 stars

Watchers

5 watching

Forks

Releases

Packages

Used by

Contributors

Languages