Skip to content

Development and Releases

X4Applegate edited this page Sep 10, 2026 · 1 revision

Development and Releases

Repository layout

cmd/caddyui/        entry point, env config, start-up, migrate-db command
internal/
  auth/             sessions, password hashing, TOTP
  caddy/            admin API client, Caddyfile adapter/parser, importer, config rendering
  db/               SQLite/MariaDB init, schema migrations, data migration
  dns/              DNS providers, one file each, registered through a descriptor
  models/           data types and queries
  server/           HTTP handlers, routes, sync, notifiers, health poller, background workers
web/
  templates/        Go html/template pages (embedded)
  static/           CSS, icons, PWA manifest and service worker (embedded)
packaging/systemd/  sample unit for the binary install
Dockerfile          scratch-based CaddyUI image, non-root uid 10001
Dockerfile.caddy    Caddy with every supported DNS provider and the CrowdSec bouncer

CaddyUI keeps its state in SQLite or MariaDB and talks to Caddy only through the HTTP admin API: no SSH, no editing of Caddy's files. Everything the browser needs is embedded in the binary, so there are no runtime downloads.

Building from source

Go 1.26 or newer.

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

Run it against a local Caddy:

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

Install as a service with the sample unit:

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 image:

docker build --build-arg VERSION=v2.45.2 -t caddyui:v2.45.2 .

The VERSION build argument stamps main.Version, which the sidebar shows and the update check compares against Docker Hub. The repository compose file builds locally with CADDYUI_VERSION=vX.Y.Z docker compose up -d --build; without it the UI shows dev.

Tests

gofmt -l .            # CI fails on unformatted files
go vet ./...
go test ./...

Opt-in integration tests run against a real Caddy or MariaDB when these are set:

CADDYUI_TEST_CADDY_ADMIN=http://127.0.0.1:2019 go test ./internal/caddy/... ./internal/server/...
CADDYUI_TEST_MARIADB_DSN='caddyui:pw@tcp(127.0.0.1:3306)/caddyui_test' go test ./internal/db/... ./internal/models/...

A throwaway Caddy for the first one: docker run --rm -p 127.0.0.1:2019:2019 -e CADDY_ADMIN=0.0.0.0:2019 caddy:2-alpine.

Continuous integration

Every pull request runs:

  • Go build and test on SQLite, plus the MariaDB integration job against a service container.
  • gofmt check.
  • CodeQL static analysis (its path-injection findings are why typed file paths go through the vetting helpers).
  • Claude code review — an automatic review comment on every non-draft, non-Dependabot pull request, using the maintainer's Claude subscription through a repository secret. Mention @claude in an issue or PR comment to ask questions or request follow-up changes. Both jobs skip themselves in forks without the secret.

release-binaries.yml runs on tags and attaches the linux/amd64 and linux/arm64 tarballs with the systemd unit and installer to the GitHub release.

Contributing

Read CONTRIBUTING.md. In short: open an issue or discussion first for anything larger than a fix, keep pull requests focused, add a CHANGELOG entry under a new version heading, make sure gofmt is clean and go build ./... and go test ./... pass, and expect the Claude review comment to be read as seriously as a human one. Bug reports are triaged by the maintainer with Claude's assistance; no credentials or database contents are ever shared with it.

Adding a DNS provider: implement the provider interface in internal/dns/, register a descriptor with its credential fields, add the Caddy module to Dockerfile.caddy, map the credentials to the module's JSON in the server's provider config switch, and add a docs section. The Gandi provider (v2.45.0) with its fake-API test is the template.

Release process

  1. Merge the pull request (squash). CI must be green.
  2. Bump nothing in code: the version comes from the tag. Create an annotated tag vX.Y.Z on main with the CHANGELOG subject as message and push it.
  3. The release workflow publishes the binaries. The Docker image is built from the tag with --build-arg VERSION=vX.Y.Z and pushed to Docker Hub as applegater/caddyui:vX.Y.Z, :latest and :stable, all pointing at the same digest.
  4. The Operations dashboard of running installs announces the new version.

Versioning follows semver: a patch for fixes, a minor for features, and the CHANGELOG entry per version is the release note.

License

CaddyUI Source Available License 1.0: free for personal use, non-profits, education, small businesses and internal use in any organisation; a commercial licence is required to offer CaddyUI as a hosted or managed service.

Clone this wiki locally