Skip to content

Operations

This page is organized around common operator tasks. For a complete command list, see the Command reference.

Daily checks

A small daily check can be:

./caddy-docker.sh status
./caddy-docker.sh logs
./caddy-docker.sh check-updates

Also open https://status.<DOMAIN> and run all service checks.

Look for:

  • containers that are restarting or stopped;
  • repeated DNS challenge failures;
  • upstream connection errors;
  • disk space pressure under caddy-data/;
  • a Caddy update recommendation;
  • certificate snapshot errors.

Start, stop, and restart

Start:

./caddy-docker.sh start

Stop without removing containers:

./caddy-docker.sh stop

Restart existing containers:

./caddy-docker.sh restart

Remove containers and the project network:

./caddy-docker.sh down

down is usually safer than clean because it does not intentionally remove volumes. clean is project-scoped and does not prune unrelated Docker state.

Follow logs

Both services:

./caddy-docker.sh logs --follow

Only Caddy:

./caddy-docker.sh logs --follow caddy

Only dnsmasq:

./caddy-docker.sh logs --follow dnsmasq

Press Ctrl+C to stop following. The service continues running.

The application also writes rotating logs to:

caddy-data/logs/app.log

Add a service safely

  1. Add one entry to upstreams.yml.
  2. Preview the Caddyfile:
./caddy-docker.sh print-caddyfile >/tmp/Caddyfile
  1. Confirm Caddy can resolve and reach the target.
  2. Restart the stack:
./caddy-docker.sh restart
  1. Follow Caddy logs and test with curl --resolve before changing shared DNS.
  2. Run the service checks in the dashboard.

Example:

wiki:
  ip: wiki
  port: 3000
  scheme: http

If wiki is a Docker service, ensure it shares the same Compose network as Caddy.

Change an upstream address or port

Edit upstreams.yml, preview the generated configuration, then restart:

./caddy-docker.sh print-caddyfile >/tmp/Caddyfile
./caddy-docker.sh restart
./caddy-docker.sh logs --follow caddy

A changed upstream does not normally require rebuilding Caddy. Rebuilds are for Caddy versions or DNS provider plugins.

Change the DNS provider

  1. Create a restricted token at the new provider.
  2. Update DNS_PROVIDER, DNS_TOKEN, and when necessary CADDY_DNS_PLUGIN_TOKEN_FIELD.
  3. Force a plugin rebuild:
./caddy-docker.sh rebuild-caddy
  1. Validate the generated Caddyfile.
  2. Watch DNS-01 challenge logs closely.
  3. Revoke the old provider token after the migration is verified.

Changing providers is a security-sensitive operation because both old and new tokens may remain valid during the transition.

Pin or upgrade Caddy

Check current state:

./caddy-docker.sh check-updates

Set an explicit version in .env:

CADDY_VERSION=v2.10.2

Then restart or rebuild:

./caddy-docker.sh rebuild-caddy

The wrapper runs a build-only operation. The temporary binary must be executable, contain the exact configured DNS module, and match the requested version before it can replace the active file. When Caddy is already running, the wrapper restarts only the Caddy service after successful installation. A failed validation leaves the previous binary and running process untouched.

After an upgrade:

./caddy-docker.sh print-caddyfile >/tmp/Caddyfile
./caddy-docker.sh status
./caddy-docker.sh logs

Test every important route, not only the dashboard.

Reload dnsmasq

After changing wildcard DNS target settings:

./caddy-docker.sh reload-dnsmasq

Confirm from a client that uses this dnsmasq instance:

nslookup app.example.net 192.168.1.20

Replace the hostname and DNS server address.

If the result is wrong, inspect:

./caddy-docker.sh data
./caddy-docker.sh logs --follow dnsmasq

Export and distribute the internal CA

Caddy must initialize its internal PKI before export files exist.

Export:

./caddy-docker.sh app --export-certs

Generated public trust files appear below:

caddy-data/exported-certs/

Inspect the certificate:

openssl x509 \
  -in caddy-data/exported-certs/caddy-internal-ca.pem \
  -noout -subject -issuer -dates -fingerprint -sha256

Distribute only the public CA certificate. Never distribute a private CA key.

The generated endpoint also serves the public files:

https://internal-ca.<DOMAIN>/cert/caddy-internal-ca.pem
https://internal-ca.<DOMAIN>/cert/caddy-internal-ca.crt

Protect access according to your network policy even though root CA certificates are public trust material.

Back up runtime state

Important state is under caddy-data/. Back it up while preserving permissions.

Example local archive:

tar --xattrs --acls -czf \
  "certify-reverse-data-$(date +%F).tar.gz" \
  caddy-data/

Store backups away from the host and protect them as sensitive because runtime data may contain private keys and service certificates.

Also back up application volumes independently. certify-reverse does not back up WordPress, databases, or other upstream data.

Restore runtime state

  1. Stop the stack:
./caddy-docker.sh stop
  1. Move the current directory aside rather than deleting it:
mv caddy-data "caddy-data.before-restore.$(date +%s)"
  1. Extract the backup in the repository root.
  2. Check ownership and permissions.
  3. Start and inspect logs:
./caddy-docker.sh start
./caddy-docker.sh logs --follow caddy
  1. Test routes and certificate behavior before deleting the pre-restore copy.

Inspect generated state

Redacted configuration plus generated-file summary:

./caddy-docker.sh config

Generated files and logs:

./caddy-docker.sh data

Inside the container:

./caddy-docker.sh shell
ls -la /data

Use the shell for diagnosis, not permanent manual changes. Generated files can be replaced on restart.

Emergency Caddyfile override

If caddy-data/Caddyfile.overwrite exists, certify-reverse starts Caddy with it instead of the generated Caddyfile.

Use this only for a controlled emergency:

  1. copy the generated file;
  2. make the smallest change;
  3. validate it with Caddy;
  4. document why the override exists;
  5. remove it after moving the change into supported configuration.

An old override creates configuration drift because later upstreams.yml changes do not affect the active runtime file.

Rotate a DNS token

  1. Create the new restricted token.
  2. Replace DNS_TOKEN in .env.
  3. Restart the stack.
  4. Force a certificate-related operation or observe a normal renewal path.
  5. Confirm DNS API operations succeed.
  6. Revoke the old token.
  7. Check shell history, backups, and operator notes for accidental copies.

The dashboard does not reveal the configured token.

Release verification

Run the complete gate:

./caddy-docker.sh verify

It checks:

  • synchronized package versions;
  • frozen Python dependency resolution;
  • unit, regression, and Compose integration tests;
  • Ruff and Mypy;
  • wheel and source-distribution creation;
  • shell syntax;
  • base and Caddyfile-print Compose configurations;
  • generated Caddyfile validation;
  • static site, HTML handbook, PDF handbook, man pages, manifest, and docs archive.

Build and preview documentation

./caddy-docker.sh docs
./caddy-docker.sh docs-serve

See Documentation publishing for renderer pins and output details.

Maintenance checklist

Weekly

  • inspect logs for repeated warnings;
  • run dashboard service checks;
  • confirm backups completed;
  • inspect disk usage;
  • check Caddy updates.

Monthly

  • restore one backup into a test location;
  • review DNS provider tokens and operator access;
  • inspect certificate expiration data;
  • update pinned documentation dependencies when needed;
  • run the full release gate on the current main branch.

Before any major change

  • save redacted configuration output;
  • back up caddy-data/ and upstream data;
  • record the current commit and Caddy version;
  • validate the rollback path;
  • change one layer at a time.