Skip to content

feat: add Caddy web server provider#4534

Open
masonjames wants to merge 16 commits into
Dokploy:canaryfrom
masonjames:codex/caddy-web-server-v0296-clean-pr
Open

feat: add Caddy web server provider#4534
masonjames wants to merge 16 commits into
Dokploy:canaryfrom
masonjames:codex/caddy-web-server-v0296-clean-pr

Conversation

@masonjames

@masonjames masonjames commented Jun 1, 2026

Copy link
Copy Markdown

Summary

Adds Caddy as an opt-in web-server provider alongside Traefik. Traefik remains the default and is untouched unless a user explicitly runs the guarded migration.

  • Provider-neutral web-server abstraction (settings, health, file views become provider-aware)
  • Caddy setup + deterministic config generation for dashboard, application, compose, and domain flows
  • Guarded Traefik→Caddy migration: dry-run report, live upstream preflight, apply with rollback snapshots, fail-closed on missing artifacts
  • Settings UI for provider selection, migration workflow, trusted proxies, and request analytics
  • Fail-closed runtime Drizzle migration handling

Related: #1246 (feature request). RFC with architecture comparison: #4615.

Review map — where the +27.7k actually is

Cluster Files Lines Review effort
Generated Drizzle snapshot 3 +8,520 Skip (generated; schema source is web-server-settings.ts / server.ts)
Tests + fixtures 35 +8,050 (230 cases) Skim for coverage
Real source 82 +11,150/−870 Review

Real-source hotspots, largest first:

  • packages/server/src/utils/caddy/config.ts (+1,151) — Caddy JSON compilation + per-server config write lock
  • packages/server/src/utils/caddy/migration/ (~4,300 across prepare/apply/rollback/translators/preflight/files/types)
  • apps/dokploy/server/api/routers/web-server.ts (+839) — all provider/web-server/caddy-migration endpoints, extracted from settings.ts (the webServerRouter extraction previously flagged here is done; procedures are spread into settingsRouter, so client api.settings.* paths are unchanged)
  • apps/dokploy/server/api/routers/settings.ts (+88/−26) — only in-place provider-awareness edits to existing procedures
  • packages/server/src/utils/caddy/{domain,compose,web-server,types}.ts (~600) — route generation
  • packages/server/src/setup/caddy-setup.ts (+332)
  • Provider-neutral refactors: services/settings.ts, services/web-server-settings.ts, services/domain.ts, utils/docker/domain.ts, db schema/validations
  • UI: components/dashboard/settings/web-server/* (migration panel, provider selector, trusted proxies, env editor), domains/handle-domain.tsx, show-traefik-* made provider-aware
  • Analytics: utils/access-log/{utils,handler}.ts

Safety model

  • Traefik default; direct provider switching is blocked outside migration/rollback flows
  • Caddy config paths constrained to Dokploy-owned directories
  • Fragment read-modify-write and compile+reload sequences hold a per-server lock (withCaddyConfigLock), so concurrent deployments (Feat/concurrent deployments in memory queue #4645) cannot publish a config missing a concurrently written route or roll back another deployment's fragment
  • Manual Caddy fragments detected; unsafe migration output conflicts block apply
  • Unsupported BasicAuth hashes blocked, not silently downgraded
  • Migration reports sanitized for non-admin surfaces
  • getActiveWebServerProvider is protectedProcedure (org-scoped via server access check): member-visible surfaces (domain forms, the web-server files page) need the provider name to render the right fields — precedent is getWebServerSettings, which is already protected

Merged with canary — 2026-07-03

Conflicts with the last 16 canary commits are resolved; notable decisions:

Validation

Fresh run on the merged branch (2026-07-03):

  • pnpm --filter=@dokploy/server typecheck — clean (tsc --noEmit)
  • pnpm --filter=dokploy typecheck — clean (tsc --noEmit)
  • pnpm --filter dokploy run test — 85/86 test files pass: 758 passed, 1 skipped, 4 failed. All 4 failures are in __test__/deploy/application.real.test.ts ("REAL Execution" tests that require the local Docker daemon to be a swarm manager) — environment-dependent and unrelated to this PR; every caddy/db/traefik suite passes
  • Running in production since 2026-06-01 on a 2-node swarm: ~30 services, ~40 domains, HTTP/3, Prometheus metrics; one documented failed-and-rolled-back cutover (2026-05-23) drove the fail-closed preflight design

@masonjames masonjames requested a review from Siumauricio as a code owner June 1, 2026 23:07
@dosubot dosubot Bot added size:XL This PR changes 500-999 lines, ignoring generated files. enhancement New feature or request labels Jun 1, 2026
@masonjames masonjames marked this pull request as draft June 1, 2026 23:08
@masonjames masonjames marked this pull request as ready for review June 1, 2026 23:10
@dosubot

dosubot Bot commented Jun 1, 2026

Copy link
Copy Markdown

Related Knowledge

1 document with suggested updates is ready for review.

Dokploy's Space

README /dokploy/blob/canary/README.md — ⏳ Awaiting Merge

How did I do? Any feedback?  Join Discord

@masonjames masonjames force-pushed the codex/caddy-web-server-v0296-clean-pr branch from 46a438e to 7baf056 Compare June 2, 2026 10:40
@dosubot dosubot Bot added size:XS This PR changes 0-9 lines, ignoring generated files. and removed size:XL This PR changes 500-999 lines, ignoring generated files. labels Jun 2, 2026
@masonjames masonjames mentioned this pull request Jun 2, 2026
@masonjames

Copy link
Copy Markdown
Author

Architecture RFC for this PR (comparison with the label-driven approach, production evidence, and a proposed stacked split so nobody has to review 27.8k lines at once): #4615 — a commit regroup and a review-map description update for this PR are coming shortly to make it tractable.

@masonjames masonjames force-pushed the codex/caddy-web-server-v0296-clean-pr branch from 8a4292f to 0dcc5f9 Compare June 10, 2026 23:17
masonjames and others added 7 commits June 10, 2026 19:31
…validate

Caddy's Server.trusted_proxies_strict JSON field is an int; the
compiler emitted a JSON boolean, so `caddy validate` rejected any
config with strict trusted proxies enabled. Found in production on
2026-06-10. Emit 1 when strict; keep omitting the field otherwise.
Resolves conflicts with 16 upstream commits, notably:
- Renumber caddy migration 0172_parched_ares -> 0173_cool_vargas
  (upstream 0172_quick_the_professor from concurrent deployments Dokploy#4645
  is canonical); regenerated snapshot on top of upstream's, restored
  idempotent DDL so hosts that applied the old identity no-op cleanly
- Adopt dynamic traefik container resolution in access-log cleanup
  (Dokploy#4646) within the provider-aware handler
- Accept admin-tools refactor (Dokploy#4625): traefik file-system modal is
  deleted; /dashboard/traefik page with ServerFilter is now the entry
  point and is cloud-enabled, ShowTraefikSystem stays provider-aware
  with a provider-aware empty state path
- Fold buildsConcurrency (server + webServerSettings) alongside caddy
  provider columns in db schema
- Tailwind v4 class renames (shadow-xs, h-140) applied to our
  provider-aware components, keeping caddy language switch and
  pointer-events-none read-only overlay fix

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Upstream's concurrent deployments (Dokploy#4645) allow builds to finish
simultaneously. The active caddy.json is compiled from the fragments
directory, so an unsynchronized read-compile-write could publish a
config missing a concurrently written fragment, and the rm-rf-based
restoreCaddyRouteFragments rollback could delete another deployment's
fragment outright. Add a per-server promise-chain lock; the public
compile+reload entry takes it, and the fragment read-modify-write flows
(application domains, compose domains, dashboard route) hold it across
their full capture-mutate-compile-rollback sequences.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Mechanical move of the 20 provider/web-server/caddy-migration
procedures plus their input schemas and helpers from settings.ts into
web-server.ts, spread back into settingsRouter so client api.settings.*
paths and existing tests are unchanged. settings.ts now carries only
the in-place provider-awareness edits to upstream procedures, which
keeps its diff against canary reviewable.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
getActiveWebServerProvider was adminProcedure, but it is consumed by
member-visible surfaces: domain forms, application traefik/caddy config
views, and the web-server files page (cloud-enabled by the admin-tools
refactor Dokploy#4625 and gated only by traefikFiles.read). For non-admin
members the query returned FORBIDDEN, so provider resolution failed and
those surfaces silently degraded (e.g. traefik-only fields shown on
caddy installs, file editor locked). getWebServerSettings, a superset
of this data, is already protectedProcedure; ensureServerAccess keeps
remote-server lookups scoped to the caller's organization.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Apply the shadow-xs rename from the tailwind v4 migration (Dokploy#4706) to
the caddy components it could not have touched, and biome-format the
provider-aware empty state.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@masonjames

Copy link
Copy Markdown
Author

Merged canary (through 91abc93) and resolved all conflicts — the PR is mergeable again. Notable updates beyond the merge itself:

  • Migration renumbered to 0173_cool_vargas: canary's 0172_quick_the_professor (Feat/concurrent deployments in memory queue #4645) keeps its number; ours is regenerated on top of its snapshot with idempotent DDL
  • Per-server Caddy config write lock: with concurrent deployments (Feat/concurrent deployments in memory queue #4645), the fragment read-compile-write and the snapshot-based rollback could race; all such sequences now hold withCaddyConfigLock (covered by new tests)
  • webServerRouter extraction done: the 20 provider/migration endpoints moved to server/api/routers/web-server.ts and are spread into settingsRouter, so api.settings.* paths are unchanged — settings.ts now diffs at +88/−26 against canary
  • Adopted refactor: unify server admin tools into dashboard pages with server selector #4625: file-system modal removed in favor of the cloud-enabled /dashboard/traefik page; the component resolves the active provider itself
  • getActiveWebServerProvider is now protectedProcedure (org-scoped) since member-visible surfaces consume it

Validation on the merged branch: both typechecks clean; 758 tests passed / 1 skipped, with only the 4 known environment-dependent application.real.test.ts failures (require a swarm-manager Docker daemon). PR description refreshed with the updated review map.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request size:XS This PR changes 0-9 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant