CaddyUI v2.4.3 — dashboard polish, smarter health dots, multi-host fixes

Hey all,

Shipped CaddyUI v2.4.3 today — a GUI for managing Caddy (proxy hosts, certs, managed DNS across Cloudflare/Namecheap/GoDaddy/DigitalOcean/Hetzner, multi-server push over WG/Tailscale). Sharing a few of the real-world fixes from this cycle in case they’re useful to anyone running similar setups.

Features

  • 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

  • Advanced Routes — import raw Caddyfile blocks or write JSON directly for anything the UI can’t model

  • Certificates — manage custom PEM/path certificates; expiry alerts via email and/or webhook

  • Multi-server — manage multiple Caddy instances from a single UI; switch with a dropdown. Edge hosts only need Caddy — no CaddyUI container required (see Agent mode)

  • Multi-user — admin and user roles; each user sees and manages only their own proxies

  • Email notifications — SMTP support (STARTTLS / TLS / plain) for cert-expiry and upstream health alerts

  • Upstream health — live health check per proxy; polls Caddy’s own admin API so Docker-internal hostnames work correctly

  • Activity log — every create/edit/delete/sync action is logged with actor and timestamp

  • Snapshots — one-click SQLite database backup; auto-snapshot on sync

  • Import from Caddy — pull your existing live Caddy config into the DB on first run

  • Paste Caddyfile — convert a Caddyfile block into a managed advanced route

  • Dark mode — toggleable, remembers your choice; system preference respected on first visit

  • 2FA / TOTP — per-user time-based one-time passwords

  • PWA — installable on desktop and mobile; offline-capable service worker

  • Update notifications — sidebar badge when a newer Docker Hub release is available**
    Repo:** https://github.com/X4Applegate/caddyui
    Release: https://github.com/X4Applegate/caddyui/releases/tag/v2.4.3
    Docker: applegater/caddyui:v2.4.3 (multi-arch, scratch base, SBOM + provenance)

What’s in v2.4.3

1. Stop flagging Docker-name backends as “down”
If your proxy target is a Docker service name (status-server:3000, snipeit-app:80, etc.), CaddyUI used to show a red “unreachable” dot because the caddyui container itself usually isn’t on that backend’s Docker network — only Caddy is. It asked Caddy’s /reverse_proxy/upstreams first, but if the upstream wasn’t cached there it fell back to a direct probe that couldn’t resolve the name.

Now: hostnames with no dots → skip the direct probe, render an amber “unknown” with a tooltip explaining why. DNS-resolution errors on public names get the same treatment. Genuinely unreachable public hostnames still go red.

2. Server-online indicator stops flapping over WG
Health poller was marking servers “offline” after a single failed 5 s ping. Over WireGuard that flaps every time rekey drops a packet. Now: 3 consecutive misses + 8 s timeout. Every success resets the counter immediately. Dashboard stays stable.

3. Dashboard source gives you both actions
Click the domain pill → opens the site in a new tab. Click the pencil icon next to it → opens the edit form. Save redirects back to /proxy-hosts, so click-pencil-edit-save-back is a clean loop.

v2.4.x cumulative highlights (context for anyone new)

  • Per-server public IP for managed DNS — editing one server’s IP retargets only that server’s records (no more rewriting every provider record when one IP changes)

  • Startup sync (CADDYUI_SYNC_ON_START=1) rehydrates Caddy from the DB on every docker compose restart, with a safety guard that refuses to push when the DB is empty so it can’t wipe an existing Caddyfile

  • Firewall docs — the Docker-iptables-bypass gotcha bit me personally. Binding port 2019 to the WG interface ("10.8.0.2:2019:2019") is the clean fix; UFW rules alone don’t block Docker-published ports

One operational note that’s worth calling out

If you run Caddy in default mode (caddy run --config /etc/caddy/Caddyfile) and push config to it via the admin API from CaddyUI or anything else, pushes don’t survive Caddy container restarts — the static Caddyfile reloads and overwrites them. The fix is --resume:

services:

caddy:

command: caddy run --config /config/caddy/autosave.json --resume --adapter json

That way Caddy persists its current config to autosave.json and reloads from there on restart. Not a CaddyUI bug but a common pothole for multi-host setups using the admin API as a source of truth.

Feedback welcome

Happy to hear what’s missing for your use case. Anything around multi-host orchestration, DNS providers, or observability in particular — that’s where I’ve been focusing.

— Richard

1 Like

CaddyUI v2.5.0 — switchable CAPTCHA, timezone picker, branded error pages

Hey everyone :waving_hand:

Small update from the last time I posted — two releases shipped today that I thought the folks here might care about, since they’re all Caddy-adjacent features and a few of you asked for them directly.

For context: CaddyUI is a self-hosted web UI that drives Caddy through its admin API. It stores everything in SQLite, pushes JSON config to Caddy on save, and covers proxy hosts, redirects, certs, advanced/raw routes, multi-server, multi-user, TOTP, snapshots, and paste-a-Caddyfile import. If you’ve seen Nginx Proxy Manager, the surface area is similar, but it speaks Caddy natively instead of translating to nginx.


What’s new in v2.5.0

Switchable CAPTCHA protection

You can now pick one of three modes from Settings → CAPTCHA protection:

  • Off (default on fresh installs)
  • Cloudflare Turnstile (managed, free, privacy-friendly)
  • Google reCAPTCHA v3 (score-based, invisible)

…and it applies to three forms: /login, /login/totp, and /users/new (admin creating a new account).

Why two providers? A while back someone in the community had a Cloudflare outage that briefly made Turnstile unreachable, and they got stuck on their own login page. v2.5.0 ships a reCAPTCHA v3 fallback plus an env-var kill-switch:

environment:
  CADDYUI_CAPTCHA_DISABLE: "1"

Set that, restart the container, and the widget stops rendering and the server stops verifying. Pull it back out once you’ve logged in. Intended specifically for “I’m locked out of my own admin” recovery.

Existing Turnstile keys from v2.4.x upgrade in place — the old settings keys are preserved, so if you had Turnstile configured, flip the provider to Turnstile and everything Just Works. Inactive-provider keys also stay in the DB across switches, so you can toggle between Turnstile and reCAPTCHA without re-typing credentials.

Small implementation details that might matter to you:

  • TOTP captcha failure does not consume the pending-TOTP token (5-min auto-expire still caps abuse — wrong captcha ≠ burned 2FA slot).
  • reCAPTCHA v3 uses a submit-hook: first submit fetches a token via grecaptcha.execute, populates a hidden input, then re-submits. If api.js fails to load (ad-blocker, outage), the fallback path just submits anyway so the server returns a clean “Security check failed” error instead of a stuck form.
  • Verify-endpoint HTTP client has a 10s timeout — a slow siteverify can’t wedge /login for 30+ seconds.

Also bundled in v2.4.12 (shipped earlier today)

Timezone picker

Settings → Timezone now has an IANA zone dropdown (America/New_York, Europe/London, etc.) with an “Other…” free-text fallback. Every DB-stored timestamp in the UI flows through it: cert expiry, activity log, snapshots, “last contact”, “last sync”. Resolution priority is:

  1. DB value (what you picked)
  2. TZ environment variable (Go’s time.Local reads this at startup)
  3. UTC

There’s also a new TZ: ${TZ:-UTC} env entry on both services in docker-compose.yml — pair it with the same zone on your Caddy container so the access-log timestamps line up.

Branded error pages

This one’s Caddy-flavored and I’m curious what you think. CaddyUI now injects a set of routes into apps.http.servers.srv0.errors.routes so every 404/502/503/504 from Caddy itself (not from an upstream that returns its own error body) renders a dark-mode-aware HTML page with:

  • The status code + short human-readable explanation
  • {http.error.id} — Caddy’s 9-char correlation ID (same one that ends up in the access log, which is the whole point: when a user screenshots a 502, you can grep the log)
  • Current HTTP-Date timestamp

Had to bang my head on one thing worth mentioning: the {err.status_code} / {err.id} placeholders you see in the Caddyfile docs only work through the Caddyfile adapter. If you’re pushing raw JSON to /load (which CaddyUI does), you have to use the full {http.error.status_code} / {http.error.id} paths. Lost an hour to that. Writing it down here so you don’t.

E2E-validated against caddy:2-alpine with a reverse_proxy to a dead upstream — 502 returns the branded page with real {http.error.id} and {time.now.http} substitutions.


Upgrade

docker pull applegater/caddyui:v2.5.0
# or
docker pull applegater/caddyui:latest

Multi-arch linux/amd64 + linux/arm64, SBOM + provenance attestations, scratch base, non-root UID 10001.

No schema migration required — captcha settings default to “off” on fresh installs, and existing Turnstile keys carry over.


Links


Happy to answer questions, take feature requests, or hear about things that break. Especially interested in feedback on the error-page design — it’s the first time I’ve written HTML that Caddy itself serves, and I’d rather get the conventions right early.

Thanks again for approving the last thread — really appreciate the warm reception. :folded_hands:

2 Likes

This looks very cool and useful, thanks for sharing it. I know a lot of people have asked for a management interface.

How much of it was vibe coded? Edit: Ah, I see the disclosure in the GitHub readme. I guess Claude did most of the heavy lifting. Thanks for using your credits for a Caddy project! I’m sure it can benefit a lot of people.

Huh, sorry about that. I don’t see why that is surprising though: why would you expect the Caddyfile docs to apply to the JSON config? If you’re writing JSON config, don’t use the Caddyfile docs. IMO the placeholders are very clearly explained in the JSON docs: json/apps/http/servers/errors/routes — Caddy Documentation

If you can elaborate on where the confusion lies maybe we can improve the situation…

Like you wrote the HTML yourself? Congrats, if so – it can take some getting used to. :slight_smile:

3 Likes

Thanks, Matt — appreciated the kind words, and yeah, Claude did most of the heavy lifting. I wanted to be upfront about that in the README.

On the placeholder thing: fair point, and you’re right that the JSON docs spell it out clearly once you land on the right page. Let me explain where I went sideways, since you asked.

I was building out the error-page piece of CaddyUI and searched something like “caddy error placeholders.” The first result I hit was the Caddyfile docs page showing `{err.status_code}` and `{err.id}`. In my head, placeholders felt like a Caddy concept rather than a Caddyfile-adapter concept, so I didn’t think to double-check whether those short forms were adapter-expanded. I pasted them into my JSON, got empty strings back, and spent an hour debugging before I found the JSON docs section you linked.

If it’s worth anything as a suggestion: a one-line note on the Caddyfile error-handling docs saying something like *“These shorthand placeholders are expanded by the Caddyfile adapter — if you’re writing JSON directly, use the full paths json/apps/http/servers/errors/routes — Caddy Documentation* would’ve caught me. Totally understand if that’s not worth the churn — just sharing the failure mode since you offered.

On the HTML — Claude helped with that too, to be straight with you. I don’t believe in lying to anyone. Honesty and loyalty matter a lot to me, so I’d rather disclose up front that I use AI for assistance than have someone find out later and feel misled. That said, it was still a genuinely useful learning exercise for me — seeing how Caddy serves static files and wiring the template paths together taught me a lot.

Thanks again for taking a look at the project.

3 Likes

Just release and now we can use adguard DoH and DoT with Caddy no issues! :smiley:

3 Likes

I see, so a misranked search result seems to have been the cause of the confusion.

The Caddyfile docs for handle_errors does say:

The following placeholders are available while handling errors. They are [Caddyfile shorthands](/docs/caddyfile/concepts#placeholders) for the full placeholders which can be found in [the JSON docs for an HTTP server’s error routes](/docs/json/apps/http/servers/errors/#routes).

So, that must not have been the page. Maybe it was the Caddyfile Concepts page where we talk about the table of shorthand placeholders. I guess we can clarify that part of the docs for others who are misled in search results, sure. I’ll push a commit soon.

I’m glad you had a good learning experience!

3 Likes

CaddyUI — what’s new since v2.5.0**

A walk through ~three weeks of releases, from 2.5.0 to today’s 2.7.5.**

Hey folks — shipping v2.7.5 today and entering a stable window. Going forward, CaddyUI moves to a twice-a-month cadence (roughly the 1st and 15th). The next two weeks are quiet on purpose.

But first — a lot has shipped since 2.5.0, and the release notes are granular. Here’s the bird’s-eye view, grouped by what you can actually do now that you couldn’t before.


:glowing_star: The biggest additions

:people_holding_hands: Groups — team-level resource visibility (v2.7.4)

The headline feature of this cycle. Admin bundles user-role accounts into a Group, and every member sees every other member’s proxy hosts, redirections, advanced routes, and certificates in their own dashboard.

  • Read-only sharing — teammate rows show an amber Team: teammate@example.com chip. Ownership stays with the creator; you can see but can’t edit their rows.

  • One team, many users — Alice and Bob at Customer X both see the full site roster without either being able to stomp the other’s work.

  • Admin creates / manages groups at /groups (new sidebar entry under Access Control).

:bar_chart: Visitor analytics (v2.7.0, polished in v2.7.1)

Opt-in traffic analytics, self-hosted, no external service. Caddy streams access logs to CaddyUI over TCP.

  • Top hosts, 24 h sparkline, status-code mix, unique visitor counts.

  • Per-server filter — narrow analytics to one Caddy node in a multi-server fleet.

  • Toggle on/off from Settings. Completely off by default, zero traffic collected until you enable it.

:bust_in_silhouette: Per-user ownership of everything (v2.7.2 → v2.7.4)

Resources can now belong to specific users:

  • v2.7.2 — users can upload and manage their own TLS certificates (not just admin-owned globals).

  • v2.7.3 — admin can reassign ownership on any resource’s edit form. Provision a proxy host for a customer then hand it off in one click.

  • v2.7.4 — Groups extend ownership into shared visibility (above).

Scope: proxy hosts, redirections, advanced routes, certificates.

:rocket: “Deploying…” page after save (v2.5.2 → v2.5.6)

When you save a proxy host or advanced route, CaddyUI now shows an interstitial page that polls in real time:

  • :white_check_mark: “DNS points to this server’s public IP”

  • :white_check_mark: “TLS cert issued”

  • :white_check_mark: “HTTPS responds”

No more guessing whether Caddy actually picked up your change. Handles the tricky cases:

  • v2.5.3 — correct expected-IP on multi-server setups (uses the target server’s public IP, not the CaddyUI host’s).

  • v2.5.4 — cert check works on routers without hairpin NAT (checks from outside your LAN so ISP routers that don’t reflect back still report green).

  • v2.5.5 — accounts for Cloudflare-proxied domains (the orange cloud) so they don’t show a false “not propagated” error.

:globe_with_meridians: Managed DNS — big expansion (v2.3.0 → v2.5.11)

What started as “Cloudflare only” is now a full multi-provider system:

  • Providers — Cloudflare, Porkbun, Namecheap, GoDaddy, DigitalOcean, Hetzner.

  • Per-provider credentials stored per-user in encrypted settings.

  • Per-provider zone allow-lists (v2.4.7) — explicitly whitelist which zones CaddyUI is allowed to touch. Keeps it out of domains you don’t want it managing.

  • Per-server public IPs (v2.4.0) — multi-Caddy fleets resolve the correct A record for the host that’ll actually serve the site.

  • Safe surgical edits (v2.4.9) — DNS “override” only touches A / AAAA / CNAME records. Your MX, TXT, SRV, CAA are never touched.

  • “Clear credentials” button (v2.4.6) — rotate or remove provider secrets per-provider without hunting through Settings.

  • Record per hostname, not just the first (v2.5.9) — a proxy host with three domains creates three A records, not one.

  • Alias-only domain changes react on edit (v2.5.10) — add an alias to an existing host and the new DNS record appears automatically.

  • Smarter zone picker (v2.5.1) — ranks matching zones by specificity so api.example.com finds example.com before example.net.

  • Collision warnings (v2.4.8 + v2.5.6) — “DNS record already exists” warning instead of a silent overwrite, with a one-click “yes, overwrite” confirmation.

  • Advanced routes (v2.5.6) — Managed DNS now works for advanced routes too, not just proxy hosts.


:locked_with_key: Security & hardening

  • CAPTCHA on login (v2.5.0) — switchable between Cloudflare Turnstile and reCAPTCHA v3. Off by default.

  • reCAPTCHA v3 fix (v2.7.0) — the score check was accepting low scores; tightened the threshold to the documented default.

  • Three-role RBAC (v2.7.0) — admin / user / view. Admin-only pages (Users, Groups, Settings, Caddy Servers, Snapshots) return 404 for non-admins, not a rendered page with disabled buttons.

  • XSS hardening (v2.5.11) — zone-picker error paths now escape provider-supplied strings. Nobody was actively exploited; found during internal review.


:artist_palette: UX polish

  • Tap-to-edit rows (v2.4.10) — click anywhere on a proxy-host row to open the edit form. No need to hunt for the pencil.

  • Explicit Edit + Delete buttons (v2.5.7) — on mobile especially, the tap-to-edit zone and the delete button are now clearly separated so you don’t accidentally delete something.

  • Visible pencil icons on every editable identifier (v2.4.11) — dashboard pattern extended to every list page.

  • Sticky Actions column on wide tables (v2.4.8) — the Edit/Delete buttons stay visible when you scroll horizontally on a proxy-host row with lots of domains.

  • Branded error pages (v2.4.12) — CaddyUI-styled 404 / 502 / 503 / 504 pages injected into Caddy automatically, consistent with the admin UI look.

  • Timezone picker in Settings (v2.4.12) — pick your display timezone; all timestamps across the UI render in it. Defaults to TZ env var, falls back to UTC.

  • Caddyfile paste-import captures TLS policies (v2.5.8) — when you paste a Caddyfile block, per-site tls automation policies (DNS challenge, issuer overrides) are preserved, not dropped.


:pill: Reliability & bug fixes

  • Backup download works on scratch image (v2.7.5, today) — /backup was returning SQLITE_CANTOPEN because our minimal scratch final image had no /tmp directory. Backup now writes next to the live DB (guaranteed-writable) and the Dockerfile pre-creates /tmp for future code paths.

  • Analytics toggle actually enables analytics (v2.7.1) — the form always stored 0 due to a FormValue bug with the hidden-input + checkbox pattern. Admins who’d flipped it on were silently seeing no data.

  • End-to-end App health dot (v2.4.4) — catches the “port open but app wedged” case where Caddy thinks the upstream is fine but the app itself is hung.

  • Split-horizon DNS handled gracefully (v2.4.5) — no more false-red App dot when the server can’t reach its own public hostname because LAN DNS resolves it to the private IP.

  • Amber “unknown” for Docker-named backends (v2.4.3) — hostnames like myapp:3000 that only resolve inside Docker don’t show red (we can’t probe them from outside).

  • Health-poller WG flap fix (v2.4.2) — three consecutive failed pings required before a server flips to “offline”. One tunnel blip no longer marks your whole fleet down.

  • App-response amber state (v2.4.5) — distinguishes “can’t tell” from “broken” so you don’t chase ghosts.


:puzzle_piece: Ecosystem

  • Agent mode fully documented — run only Caddy on edge nodes, no CaddyUI container, managed remotely by a central CaddyUI over your private network (WireGuard, Tailscale, VPC).

  • Admin API auth (v2.1.0) — optional HTTP Basic Auth on the bootstrap Caddy admin endpoint.

  • Unix socket support (v2.1.0) — for the paranoid.

  • In-app docs (v2.1.0) — /docs page with feature walkthroughs, FAQ, troubleshooting.


:package: How to update

docker compose pull && docker compose up -d

Or in Portainer: Recreate → enable Re-pull image.

Multi-arch on Docker Hub (linux/amd64 + linux/arm64):

docker pull applegater/caddyui:v2.7.5

docker pull applegater/caddyui:latest

Migrations run automatically on startup. No downtime beyond the container restart.


:world_map: On the whiteboard for upcoming releases

No promises, but things I’m thinking about:

  • Per-group resource templates — spin up a new user in a group and auto-seed them with a starter proxy host.

  • Export / import for migrations — move a set of proxy hosts between servers in one click.

  • More DNS providers — Route 53, Gandi, Njalla if there’s demand.

  • Audit-log retention policies — configurable rollover for the activity table.

  • Per-host rate limiting — Caddy supports it; the UI doesn’t expose it yet.

Want something specific? Open an issue on GitHub. I read every single one and will batch them into the next release window.


:pause_button: Why the quiet window

Shipping twenty releases in three weeks is great for iteration and rough on anyone running this in production. From today, CaddyUI ships twice a month (roughly the 1st and the 15th), with emergency fixes only in between.

The next two weeks: no more updates. Go set up your groups. Migrate a customer to their own account. Turn on analytics and watch the sparkline. Hand off some sites. Enjoy the stability.

Bug reports are still welcome — they’ll land in the next window. Feature requests too.


Thanks to everyone who’s been using CaddyUI, and pushing it to its limits. Every single feature above came from someone going “wait, I wish it did X” and writing an issue. Keep going — this project is only as good as the use cases you throw at it.

Please enjoy! :rocket:

— the maintainer

2 Likes

CaddyUI v2.10.0 released — heads up: this one’s for the tinkerers

Hi all :waving_hand:

After a long preview cycle (the v2.9.1 → v2.9.267 builds) I just promoted
v2.10.x to :latest on Docker Hub. Big release — full changelog is on
GitHub but here’s what matters:

:warning: Fair warning: this release leans toward power users.
The proxy-host form now has ~300 fields across many collapsible
sections. If you just want “domain + upstream + auto-SSL,” the form
still works exactly like before — just ignore the new sections. But the
new options need a basic understanding of HTTP / Caddy / TLS to use
safely, and a few of them can break things if you misconfigure.

What’s actually new

:repeat_button: Multi-upstream proxy hosts

A single proxy host can now route different paths to different
upstreams — what you’d write in Caddyfile as
handle /push/* { reverse_proxy notify_push:7867 } etc. Drove this
with my Nextcloud setup (Nextcloud + AppAPI + notify_push on one

:round_pushpin: Path-based routing on both proxy and redirect hosts

  • Proxy hosts: per-path redirect rules that fire before the
    reverse_proxy. e.g. //webmail 302 while everything else
    proxies to your backend.
  • Redirect hosts: per-path destinations + status codes. Migrate
    /old-blog/* to a new domain while leaving the rest untouched.
    Empty destination + 410 = “Gone” for retired URLs.

:date: Sunset dates on redirects

Set a date; after that date the redirect returns 410 Gone instead.
Compliance-friendly cleanup for “redirect from old → new until
2027-01-01, then drop it.”

:memo: Caddyfile editor on Advanced routes + Validate button

Paste Caddyfile, hit Validate, see if Caddy accepts it before saving.
Tabbed editor — switch between Caddyfile and JSON view.

:test_tube: Form search / filter

Hit any proxy-host edit page and there’s a sticky search input at the
top. Type x-forwarded, hsts, tls, redirect, health, etc.
Fields filter, sections auto-expand, “N matches” counter inside the
input. Esc clears. Defense against the form sprawl :sweat_smile:

:shield: Built-in bot/admin-path blocking

  • Block common bots (one toggle) — AhrefsBot, SemrushBot, Bytespider,
    MJ12bot, etc. Doesn’t touch Google/Bing.
  • Block common admin paths (one toggle) — /wp-admin, /.git,
    /.env, /phpmyadmin, etc. → 404. Kills script-kiddie scans cheap.
  • Force canonical host — redirect alt hostnames to your canonical
    one. SEO consolidation in one field.

:rocket: Analytics speedup

The 30/90-day pages were doing full-table scans. They now use a daily
rollup that gets populated automatically. Multi-second loads → ~10ms.

:locked_with_key: Security hardening

  • SMTP CRLF header injection fix in both email send paths
  • Log injection fix for unauthenticated input handlers
  • CodeQL custom config with documented rationale for what’s intentional
  • Baseline X-Frame-Options / X-Content-Type-Options / Referrer-Policy
    headers from CaddyUI itself (defense-in-depth)

:plus: ~70 new per-host options

Too many to list. Notable ones:

  • add_x_real_ip and add_x_real_scheme (proxy real client IP/scheme
    to upstream)
  • add_x_forwarded_user/email/groups/roles (static identity headers
    for trusted-network impersonation)
  • health_check_basic_auth, health_check_query_params (probe behind
    auth or with ?token=…)
  • upstream_tls_alpn, upstream_tls_ca_pem_inline,
    upstream_tls_server_name_from_host (fine-grained upstream TLS)
  • forward_auth_skip_paths (bypass forward_auth for /health,
    /metrics)
  • redirect_strip_path_prefix, redirect_wildcard_subdomain
  • Per-route path redirects before reverse_proxy

Upgrade

docker pull applegater/caddyui:latest                  
# then recreate your container (Portainer Recreate, or stop+rm+run)
                                                                                                                                                                                          
DB migrations are additive ALTER TABLE ADD COLUMN — safe on existing                                                                                                                      
installs, no manual SQL needed. Every new option defaults to off/empty,                                                                                                                   
so existing rows behave identically until you opt in.                                                                                                                                     
                                                                                                                                                                                          
If your CaddyUI container can't resolve LAN hostnames your proxy hosts                                                                                                                    
use (Docker's embedded DNS at 127.0.0.11 doesn't see your LAN), add
--dns=<router-ip> or --add-host=… to the container, or run with                                                                                                                           
--network=host. Only affects CaddyUI's own port-dot probes — Caddy                                                                                                                        
upstream routing is unaffected.                                                                                                                                                           
                                                                                                                                                                                          
Known caveats                                                                                                                                                                             
                                                                                                                                                                                          
- The proxy-host form is dense. Use the search input. Most fields                                                                                                                         
do nothing unless ticked, but if you tick something without
understanding what it does, you can break a working setup.                                                                                                                                
- The add_x_* echo headers (X-Request-Method, X-Request-Query, etc.)                                                                                                                      
are mostly cargo-cult — only useful if a specific backend reads                                                                                                                           
that exact header. Don't enable speculatively.                                                                                                                                            
- A few features (block_admin_paths, block_bot_user_agents) are                                                                                                                           
built-in regex/path lists. They're sensible defaults but not                                                                                                                              
perfect — if you have a legitimate use of one of those paths/UAs,                                                                                                                         
leave the toggle off.                                                                                                                                                                     
                                                                                                                                                                                          
Source / changelog / report bugs                                                                                                                                                          
                                                       
- Repo: https://github.com/X4Applegate/caddyui                                                                                                                                            
- Release notes: https://github.com/X4Applegate/caddyui/releases/tag/v2.10.0
- Docker Hub: https://hub.docker.com/r/applegater/caddyui                                                                                                                                 
                                                                                                                                                                                          
Feedback and bug reports very welcome — especially "I tried option X                                                                                                                      
and it did Y when I expected Z." Have fun 🎉                                                                                                                                              
                                                                                                                                                                                          
1 Like
1 Like

CaddyUI Update Announcement

:rocket: New Release — Bug Fixes & Critical Updates


We are pleased to announce a new update for CaddyUI is now available.

This release addresses several important issues, including critical bug fixes related to the database and overall stability improvements.


:package: Where to Get It

Resource Link
:spouting_whale: Docker Hub applegater/caddyui
:laptop: GitHub Repository X4Applegate/caddyui

:hammer_and_wrench: What’s Changed in This Release

  • :white_check_mark: General bug fixes — Resolved various minor issues affecting stability and performance
  • :red_circle: Critical database bug fix — Addressed a serious issue within the database layer that could impact data integrity or application behavior
  • :wrench: Additional improvements — Further refinements and corrections across the codebase

:high_voltage: How to Update

Docker:

docker pull applegater/caddyui:latest


:light_bulb: Tip: We recommend all users update to the latest version as soon as possible, particularly due to the critical database fix included in this release.

CaddyUI v2.16.8 Released — DNS-01, Faster Proxy Management, Multi-Server Support, and More

Hi everyone,

I’m happy to announce the release of CaddyUI v2.16.8.

CaddyUI is a free, open-source, self-hosted web interface for Caddy. It lets you manage proxy hosts, redirects, certificates, DNS records, users, multiple Caddy servers, and advanced routes through a browser.

Before getting into the release, I want to apologize for not posting an update here for a while. Life has been busy, and I’m very happy to share that I’m going to be a dad!

Going forward, I plan to work on CaddyUI once or twice a month—fixing bugs, improving the experience, and adding useful features. This schedule will allow me to continue supporting the project while giving me the time I need to focus on my growing family.

Thank you for your patience, support, bug reports, and understanding. :heart:

Project links

What is CaddyUI?

CaddyUI is a separate management application that connects to Caddy through its Admin API.

It does not replace Caddy. Caddy continues handling traffic, TLS, routing, and certificate automation. CaddyUI provides a browser-based interface for building and synchronizing Caddy’s JSON configuration.

You can use one CaddyUI installation to manage:

  • A local Caddy server
  • Multiple remote Caddy servers
  • Caddy servers connected through WireGuard or Tailscale
  • Docker-based Caddy installations
  • Native Caddy installations running on VMs, LXCs, or bare-metal systems

Remote servers only need Caddy. They do not need another CaddyUI container.

Changes in v2.16.8

Faster Proxy Hosts page

The Proxy Hosts page previously loaded every configuration field for every host.

A CaddyUI proxy-host record can contain several hundred optional settings, including:

  • Advanced configuration
  • Custom request and response headers
  • Health-check configuration
  • Security rules
  • Path-based routing rules
  • Maintenance pages
  • Custom error pages
  • Upstream TLS settings
  • Load-balancing options

Most of that information is unnecessary when displaying the list page.

CaddyUI now loads only the 19 fields required to render the Proxy Hosts list. The live-health endpoint uses the same lightweight query.

This should noticeably improve page-loading performance, especially when an installation contains many hosts or large advanced configurations.

Removed a duplicate Caddy request

Opening Proxy Hosts previously started two separate live requests to the Caddy Admin API.

One updated the health indicators beside each host, while the other populated the detailed upstream-status panel. If a remote Caddy server was unavailable, the second request could remain open for up to five seconds.

Row health continues to load normally. The detailed upstream panel now loads only when the user presses Refresh.

Changes introduced in v2.16.7

DNS-01 certificate issuance

CaddyUI can now configure ACME DNS-01 challenges using the same credential profile selected under Managed DNS.

Supported providers include:

  • Cloudflare
  • Porkbun
  • Namecheap
  • GoDaddy
  • DigitalOcean
  • Hetzner

DNS-01 works with standard hostnames and wildcard certificates:

example.com
app.example.com
*.example.com

When Managed DNS and Auto SSL are enabled, CaddyUI:

  1. Reads the selected DNS credential profile.
  2. Creates a subject-specific TLS automation policy.
  3. Configures the selected DNS provider for the ACME challenge.
  4. Sends the updated configuration to Caddy.
  5. Leaves certificate issuance, storage, renewal, and serving to Caddy.

This avoids entering the same DNS credentials separately for A-record management and certificate automation.

DNS plugin requirement

The Caddy server must contain the matching caddy-dns provider module.

For example, Cloudflare requires:

github.com/caddy-dns/cloudflare

Porkbun requires:

github.com/caddy-dns/porkbun

Updating CaddyUI does not automatically add modules to a separate Caddy installation.

The repository’s custom Dockerfile.caddy now builds Caddy with all six DNS provider modules supported by CaddyUI.

Fixed skip_redirects synchronization errors

Some users encountered this validation error when pressing Sync Caddy:

json: unknown field "skip_redirects"

The problem was that CaddyUI attempted to use a per-host automatic_https.skip_redirects JSON field that Caddy does not support.

CaddyUI now represents hosts with Force SSL disabled using an explicit HTTP server on port 80.

The resulting behavior is:

  • Force SSL enabled: HTTP redirects to HTTPS.
  • Force SSL disabled: HTTP and HTTPS are both available.
  • Auto SSL disabled: the host can remain HTTP-only when configured that way.
  • HTTPS traffic remains on port 443.
  • Old unsupported configuration is removed before validation.

This follows the configuration model produced by Caddy’s own Caddyfile adapter.

Other recent features

Multiple DNS credential profiles

You can save multiple DNS credential profiles instead of having only one global provider account.

This is useful when:

  • Domains are split across multiple Cloudflare accounts.
  • Different customers use separate provider accounts.
  • Tokens have access to different zone groups.
  • A single CaddyUI installation manages several organizations.

Each proxy host, redirection, or advanced route can select its own credential profile.

Managed DNS lifecycle

Managed DNS can automatically:

  • Find the appropriate zone for a hostname
  • Create A records
  • Create records for aliases and multiple hostnames
  • Detect existing records before making changes
  • Avoid overwriting records CaddyUI did not create
  • Retarget records when a server’s public IP changes
  • Delete CaddyUI-managed records when a route is removed
  • Reuse credentials for DNS-01 certificate issuance

Multiple Caddy servers

One CaddyUI installation can manage several Caddy servers.

Resources are scoped to the selected server, including:

  • Proxy hosts
  • Redirection hosts
  • Advanced routes
  • Certificates
  • DNS records
  • Health information
  • Analytics
  • Snapshots

For remote management, the Caddy Admin API should be available only through a private network such as WireGuard or Tailscale. Port 2019 should not be exposed publicly.

Proxy hosts

Proxy Hosts provide a guided interface for configuring reverse proxies.

Options include:

  • HTTP and HTTPS upstreams
  • Automatic HTTPS
  • Custom certificates
  • WebSocket support
  • HTTP/2 and HTTP/3 controls
  • Multiple upstreams
  • Load-balancing policies
  • Active and passive health checks
  • Request and response headers
  • Path-based upstream rules
  • URL rewrites
  • CORS controls
  • Basic authentication
  • IP allowlists and blocklists
  • Upstream TLS verification
  • SNI overrides
  • Timeouts and retry policies
  • Maintenance mode and schedules
  • Custom error pages
  • Security headers
  • Compression
  • Access-log controls

Redirection hosts

Redirection Hosts support:

  • HTTP 301
  • HTTP 302
  • HTTP 307
  • HTTP 308
  • Path preservation
  • Path-specific redirect rules
  • Wildcard subdomain redirects
  • Temporary and permanent redirects
  • Automatic HTTPS
  • Custom certificates
  • Managed DNS
  • DNS-01 certificate issuance
  • Scheduled sunset behavior

Advanced routes

Advanced Routes are available for configurations that do not fit the guided forms.

You can:

  • Paste Caddyfile site blocks
  • Enter Caddy JSON routes
  • Import adapted Caddy configurations
  • Preserve custom handlers
  • Assign certificates
  • Configure Managed DNS
  • Use DNS-01 automation
  • Reclassify simple routes into Proxy Hosts or Redirections

Caddyfile import

CaddyUI can adapt pasted Caddyfile configuration and classify routes automatically.

Simple configurations become editable Proxy Hosts or Redirections. More complex configurations remain Advanced Routes.

Imported TLS automation policies, including DNS-01 configuration and custom issuers, are preserved.

Certificate management

Certificate features include:

  • Automatic Caddy-managed certificates
  • Custom PEM certificates
  • File-path-based certificates
  • Certificate assignment to hosts
  • Bulk certificate assignment
  • Certificate-expiration monitoring
  • Email and webhook expiration alerts
  • Live-certificate inspection
  • Porkbun SSL certificate import

Bulk actions

Bulk actions are available across the main resource pages.

Depending on the resource type, you can:

  • Enable multiple entries
  • Disable multiple entries
  • Delete multiple entries
  • Enable or disable maintenance mode
  • Assign a certificate to several proxy hosts
  • Clear custom certificates and return hosts to Auto/ACME

Drag-and-drop ordering

Proxy hosts and redirections can be reordered using drag-and-drop controls.

The order is stored in CaddyUI and used when building the synchronized route list.

Access control

CaddyUI includes three account roles:

  • admin: complete access
  • user: manages owned resources
  • view: read-only access

Additional access-control features include:

  • Per-user ownership
  • Group-based shared visibility
  • Administrator resource reassignment
  • API tokens
  • Read-only, proxy-write, and full token scopes
  • Two-factor authentication with TOTP
  • Session management
  • Optional Cloudflare Turnstile or reCAPTCHA protection

Snapshots and recovery

CaddyUI can create SQLite and Caddy configuration snapshots.

Features include:

  • Manual snapshots
  • Automatic snapshots before synchronization
  • Snapshot comparison
  • Configuration restoration
  • Retention controls
  • Activity records for synchronization failures

Health monitoring

CaddyUI can display:

  • Caddy server availability
  • Upstream connection status
  • Application-response status
  • HTTP response codes
  • Probe latency
  • Last-check times
  • Down and recovered transitions
  • Multi-server health summaries

Visitor analytics

Optional visitor analytics include:

  • Requests
  • Unique visitors
  • Bandwidth
  • Status-code distribution
  • Top hosts
  • Top paths
  • Browser information
  • Traffic trends
  • Per-server filtering
  • Configurable retention

Analytics remain optional and can be disabled.

Notifications

Notification options include:

  • SMTP email
  • Generic webhooks
  • ntfy
  • Certificate-expiration alerts
  • Upstream down/recovered alerts
  • Configurable notification thresholds

AI assistant

CaddyUI includes an optional assistant that can answer Caddy, TLS, DNS, and CaddyUI configuration questions.

Supported backends include:

  • Local Ollama
  • Ollama Cloud
  • Anthropic
  • OpenAI-compatible APIs
  • OpenRouter
  • Groq
  • Together
  • vLLM
  • LM Studio

The assistant can also propose Proxy Hosts or Redirections through structured tool calls. The user sees the proposed configuration and must press Apply before it is created.

AI-generated changes are recorded in the activity log.

REST API

The REST API supports:

  • Proxy Hosts
  • Redirection Hosts
  • Advanced Routes
  • Certificates
  • Toggle actions
  • Scoped API tokens
  • Ownership checks
  • Synchronization after writes

A community-maintained MCP integration is also available:

CaddyUI-MCP

For automated integrations, I recommend creating a dedicated CaddyUI user and using the narrowest token scope that fits the workflow.

Additional interface features

CaddyUI also includes:

  • Global command palette
  • Search and filtering
  • Dark mode
  • Progressive Web App support
  • Mobile-friendly layouts
  • Per-resource ownership indicators
  • Dashboard recommendations
  • Activity history
  • Caddy JSON preview
  • Caddyfile export
  • JSON import and export
  • Update notifications
  • Branded Caddy error pages

Installation

Docker

Docker is the recommended installation method.

Pull the pinned release:

docker pull applegater/caddyui:v2.16.8

Or use the current stable release:

docker pull applegater/caddyui:latest

Available tags:

v2.16.8
latest
stable
preview

A minimal standalone container can be started with:

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

Open:

http://localhost:8081

Then complete the first-run administrator setup.

The complete Docker Compose example is available in the repository:

docker-compose.yml

Native Linux installation

GitHub releases include Linux archives for:

  • amd64
  • arm64

Each archive includes:

  • CaddyUI binary
  • Installation script
  • Example systemd service
  • README
  • Changelog
  • License
  • Checksums

Download the appropriate archive from the v2.16.8 release page.

Important security note

The Caddy Admin API is extremely powerful.

Do not expose port 2019 directly to the public internet. For remote Caddy servers, use a private network such as:

  • WireGuard
  • Tailscale
  • A private VLAN
  • A properly secured internal reverse proxy with authentication

CaddyUI should communicate with Caddy over a trusted network.

Updating

Before updating, backing up the CaddyUI data volume or SQLite database is always recommended.

For Docker Compose:

docker compose pull
docker compose up -d

If you use a pinned tag, update the image to:

image: applegater/caddyui:v2.16.8

Then redeploy the stack.

If you use Portainer, update the image tag and redeploy the stack from Portainer.

Testing and release checks

Before publishing v2.16.8, the project passed:

  • Go unit tests
  • Go vet
  • Application build
  • Docker image build
  • GitHub Actions CI
  • GitHub CodeQL analysis
  • Real Caddy JSON validation for the DNS-01 provider schemas
  • Real Caddy validation for the HTTP/HTTPS routing changes

Feedback

If you try the release, I would appreciate feedback—especially from installations managing many proxy hosts or using DNS-01.

Please report bugs or feature requests here:

github.com/X4Applegate/caddyui/issues

For general questions or ideas:

github.com/X4Applegate/caddyui/discussions

Thank you to everyone who has tested CaddyUI, reported bugs, suggested features, or contributed feedback.

I may be working on a slightly slower release schedule as I prepare to become a dad, but I’m still committed to improving CaddyUI and keeping it useful for the community. :heart: