|
Some checks failed
CI / Lint, typecheck, test (pull_request) Successful in 5m27s
CI / Build container images (pull_request) Successful in 1m16s
CI / Auth e2e pack (pull_request) Successful in 7m49s
CI / Import/export fidelity gate (pull_request) Successful in 57s
CD / Build and push images (push) Successful in 19s
CD / Deploy to Test (push) Successful in 28s
CD / Smoke tests against Test (push) Successful in 1m32s
CD / Promote to Int (push) Successful in 12s
CI / Lint, typecheck, test (push) Successful in 5m14s
CI / Build container images (push) Has been skipped
CI / Auth e2e pack (push) Successful in 7m41s
CI / Import/export fidelity gate (push) Failing after 10s
Verification result: only deploy/compose/.env.example was ever tracked (full-history check), zero hits for obvious secret patterns across all added lines in history — recorded on issue #231 (residual-risk list). The new CI step in the checks job fails if any .env other than .env.example is tracked or a tracked file matches an obvious secret pattern (private key blocks, AWS/GitHub/GitLab/Slack token shapes). .env.example already documents every variable the compose files reference (verified: comm of compose ${VAR} refs vs example keys is empty). README states the example as the authoritative reference. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0168Ph5uBmHm8X28CSVpbpnJ |
||
|---|---|---|
| .. | ||
| legal-template.md | ||
| public-api.md | ||
| README.md | ||
Self-hosting Dorfteich
Everything you need to install, run, update, and back up your own Dorfteich with Docker — this guide is the complete contract: if a step here does not work, that is a bug (issue #88).
Docs are English-only by decision (issue #88): the product UI is fully localized (de/en), operator documentation is not — one authoritative text beats two drifting ones.
Requirements
- Docker Engine with the Compose plugin (
docker compose version≥ 2.20). - 2 GB RAM, ~2 GB disk for images plus room for your content and backups.
- A domain pointing at the host — TLS via your own reverse proxy or the
bundled
caddyprofile (below). - Outbound SMTP relay (optional at install time: the setup wizard can configure it later, or you skip mail entirely at first).
Install
-
Create a directory and fetch the two reference files from the repository (
deploy/compose/):docker-compose.yml,.env.example— plusCaddyfileif you want thecaddyprofile.mkdir dorfteich && cd dorfteich # copy docker-compose.yml, .env.example (and Caddyfile) here cp .env.example .env && chmod 600 .env -
Edit
.env— the minimum:POSTGRES_PASSWORD,COLLAB_TOKEN_SECRET: long random strings (openssl rand -base64 32).IMAGE_PREFIX=gitea.101010.cloud/stwaidele/dorfteichandTAG: releases are semver tags (v1.2.3); until the first public release,testtracks the latest verified build.APP_BASE_URL=https://wiki.example.com— must be exactly what browsers will use; e-mail links and the CSRF origin check derive from it.- Every other variable is documented inline in
.env.examplewith its default and effect; nothing outside that file configures the stack. .env.exampleis the authoritative reference: real values live only in your local.envand never enter the repository — a CI check fails if any.envother than.env.exampleis ever tracked (issue #198).
-
Start:
docker compose up -d # behind your own reverse proxy docker compose --profile caddy up -d # or with the bundled TLS ingress- Own proxy: route
/*→127.0.0.1:$WEB_PORT,/api/*→$API_PORT,/collab*→$COLLAB_PORT(WebSocket upgrade required on/collab). caddyprofile: setDOMAIN=wiki.example.comin.env; Caddy publishes 80/443 and obtains Let's Encrypt certificates automatically (both ports must be reachable from the internet).DOMAIN=localhostissues an internal-CA certificate — for smoke tests only.
- Own proxy: route
-
Open
https://your-domain/— a fresh instance shows the first-run setup wizard.
First-run wizard
Six steps, all in the browser (issue #80/#81): language → the Site-Admin
account (created verified, you are signed in immediately) → instance name
and default language → SMTP relay (Save runs a live test and sends a
test mail to you; Skip leaves mail unconfigured — sign-up verification
will not work until an admin adds a relay) → registration mode
(open/closed) → summary + finish. The wizard locks itself permanently on
completion. Until it completes, the api answers everything but the wizard
and health endpoints with 503 setup_required — that is not an error.
Unattended installs skip the wizard by pre-seeding: set the
SETUP_ADMIN_* variables in .env before the first start (see
.env.example).
Updating
# edit .env: TAG=v1.3.0
docker compose pull && docker compose up -d
Database migrations run automatically at api start. Release notes flag
releases with a migration label and any manual steps. Downgrade
window: one minor release — TAG back + pull + up -d is supported
one step back; further back, restore the backup taken before the update
instead (the nightly sidecar gives you one at most 24 h old).
Backups & restore
Enabled by default (ADR 0015): the backup sidecar dumps the database and
archives the uploads/plugins volumes nightly at BACKUP_TIME onto the
backups volume, prunes by BACKUP_RETENTION_DAYS, writes status.json,
and — with BACKUP_MAIL_TO set — mails you on failure.
- On-demand backup:
docker compose run --rm -e BACKUP_RUN_ONCE=1 backup, or the Back up now button under Admin → System. - List sets:
docker compose exec backup ls /backups - Restore:
./restore.sh <backup-id>(fetchdeploy/backup/restore.shnext to your compose file) — details indocs/operations/restore-runbook.md.
Off-host copies to a Nextcloud
Get the backups off the host — a backup on the same disk protects against mistakes, not against losing the host. Any Nextcloud you can reach works as the target; configure it entirely in the admin UI (Admin → System → Backups):
- In Nextcloud, create an app password for the account that should hold the backups (Settings → Security → Devices & sessions).
- In Dorfteich, enable Upload backups to Nextcloud, enter the plain
Nextcloud address (e.g.
https://cloud.example.com), the username, the app password and a folder, and use Test connection — it verifies the credentials and creates the folder. The password is kept in the secret store on thesecretsvolume, never in the database. - Pick the upload schedule (after every nightly backup, weekly, or manual only) and the retention for both sides. After each successful upload, old remote bundles beyond the retention are pruned — never the newest one.
Each upload is ONE self-contained archive
(dorfteich-backup-<id>.tar.gz = database dump + files archive +
manifest) — everything needed to rebuild the instance after total loss.
readyz warns (backup_remote check) when the off-host copy grows stale,
and upload failures alert through the backup failure mail.
Restore from the admin UI: Admin → System → Backups → Restore lists
local and Nextcloud sets. Restoring asks you to re-type the backup id,
then the instance enters maintenance mode (everything answers 503 plus a
status page), restores itself through the backup sidecar, and restarts.
If the app itself is gone, use the operator path in
docs/operations/restore-runbook.md instead — it documents fetching a
bundle from Nextcloud by hand.
Public REST API
Scripts and integrations can talk to the instance through a
token-authenticated API at /api/public/v1, and MCP clients (Claude Code
and friends) through the built-in MCP endpoint at /api/mcp — both off by
default, enabled per instance and per pond. Details, token walkthrough,
and the OpenAPI document: public-api.md.
Health & troubleshooting
GET /api/v1/readyzis the instance's own diagnosis. HTTP 503 = database/migrations broken (the instance cannot serve). HTTP 200 with"status":"degraded"= a warning-level check:converter/rendererdown (import/export/PDF degrade, everything else works) orbackupstale (last success older than 26 h). Each check carries adetail. Monitor set:deploy/monitoring.md.- Logs:
docker compose logs api(orweb,collab,backup,db) — structured JSON, rotated by Docker. - Proxy pitfalls: editor never connects / "offline" although the page
loads → the proxy does not upgrade WebSockets on
/collab. All mutations fail with 403csrf_origin_mismatch→APP_BASE_URLdoes not match the URL in the browser (scheme and host must be identical). E-mail links point at the wrong host → same variable. - Wizard reappears after a restart → the database volume was not
persisted; never run without the
db-datavolume. docker compose psshowsunhealthy→ that container's liveness check fails; a degraded readyz alone never marks containers unhealthy and never restarts anything.
Building from source instead
Clone the repository and build the images locally — the reference compose carries the build contexts already:
docker compose build && docker compose up -d
Same layout, same volumes; you trade the registry pull for a local toolchain (Node 22 build stages run inside Docker, nothing else needed).
Verified install
The guide is verified by a scripted clean-machine run
(deploy/self-hosting-verify.sh): fresh directory, reference compose +
.env.example only, --profile caddy with an internal-CA certificate,
asserting that the wizard answers over TLS. Run it yourself on any Docker
host — it uses its own compose project name and high ports, then removes
everything.