From 3783d03d6385c8090c9d5b058a7431aefd701fd3 Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 20 May 2026 15:19:11 +0100 Subject: [PATCH] docs(docker): document settings.json writable-layer + env-var-vs-file semantics (#7819) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two related operator-facing docs gaps, both surfaced by #7819: 1. settings.json on disk is a *template*; env-var substitution happens at load time in memory only. Operators repeatedly mistake the templated file for a stale config because the docs never spell out that the on-disk file is intentionally unchanged by env vars. 2. The default docker-compose.yml puts settings.json in the container's writable layer with no host mount, which means admin /settings edits are silently lost on `docker compose down && up`, `pull`, or watchtower — but preserved across plain `restart`. Operators don't reliably know which compose verbs recreate the container. Adds two prose sections to doc/docker.md (explaining both gotchas, with a recreate-vs-restart table) and a commented-out `./settings.json:…` bind mount in both docker-compose.yml and the README compose example. Bind mount is opt-in so existing setups behave identically. No runtime change. Co-Authored-By: Claude Opus 4.7 (1M context) --- doc/docker.md | 38 ++++++++++++++++++++++++++++++++++++++ docker-compose.yml | 8 ++++++++ 2 files changed, 46 insertions(+) diff --git a/doc/docker.md b/doc/docker.md index 8be4dbdaaee..815a836aa56 100644 --- a/doc/docker.md +++ b/doc/docker.md @@ -29,6 +29,37 @@ Edit `/settings.json.docker` at your will. When rebuilding the image, t **Each configuration parameter can also be set via an environment variable**, using the syntax `"${ENV_VAR}"` or `"${ENV_VAR:default_value}"`. For details, refer to `settings.json.template`. +### How `settings.json` and environment variables interact + +This trips people up often enough that it's worth calling out explicitly (see [#7819](https://github.com/ether/etherpad/issues/7819)): + +* `settings.json` inside the container is a **template** containing `${VAR:default}` placeholders. +* Environment variable substitution happens at **load time, in memory only** — env vars never overwrite `settings.json` on disk. +* `docker exec cat /opt/etherpad-lite/settings.json` will therefore always show the *templated* file (e.g. `"port": "${PORT:9001}"`), regardless of what `PORT` is set to in your environment. The resolved value is what Etherpad uses at runtime; the file is unchanged. +* The admin /settings page also reads this file directly, so the raw view shows placeholders too. The page now surfaces a banner and an "Effective" tab that displays the in-memory resolved values when placeholders are present. + +### Persisting admin /settings edits across container recreates + +`settings.json` lives in the container's writable layer by default. That means: + +| Operation | Effect on `settings.json` | +|------------------------------------------|------------------------------------------| +| `docker restart` | Preserved (writable layer is reused) | +| `docker compose restart` | Preserved | +| `docker compose down && docker compose up` | **Reset** to the image template | +| `docker compose pull && docker compose up` | **Reset** to the new image template | +| Watchtower / image auto-update | **Reset** to the new image template | +| `docker rm` + `docker run` | **Reset** to the image template | + +If you intend to edit `settings.json` through the admin UI (rather than relying solely on env vars), mount the file from the host so edits survive container recreate: + +```yaml +volumes: + - ./settings.json:/opt/etherpad-lite/settings.json +``` + +(Bootstrap by copying `settings.json.docker` to `./settings.json` on the host before the first `up`.) The default compose example below ships this line commented out — uncomment it if you need persistent on-disk edits. + ### Rebuilding including some plugins If you want to install some plugins in your container, it is sufficient to list them in the ETHERPAD_PLUGINS build variable. The variable value has to be a space separated, double quoted list of plugin names (see examples). @@ -282,6 +313,13 @@ services: volumes: - plugins:/opt/etherpad-lite/src/plugin_packages - etherpad-var:/opt/etherpad-lite/var + # OPTIONAL: persist admin /settings edits across container recreates. + # Without this mount, settings.json lives in the image's writable + # layer — `docker compose restart` preserves it, but `docker compose + # down && up`, `pull`, or watchtower reverts it to the image + # template. Uncomment if you intend to edit settings.json through + # the /admin UI. See https://github.com/ether/etherpad/issues/7819. + # - ./settings.json:/opt/etherpad-lite/settings.json depends_on: - postgres environment: diff --git a/docker-compose.yml b/docker-compose.yml index e009c99f847..90d2f86b591 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,6 +7,14 @@ services: volumes: - plugins:/opt/etherpad-lite/src/plugin_packages - etherpad-var:/opt/etherpad-lite/var + # OPTIONAL: persist admin /settings edits across container recreates. + # Without this mount, settings.json lives in the image's writable + # layer — `docker compose restart` preserves it, but `docker compose + # down && up`, `pull`, or watchtower reverts it to the image + # template. Uncomment if you intend to edit settings.json through + # the /admin UI instead of (or in addition to) env vars. See + # https://github.com/ether/etherpad/issues/7819. + # - ./settings.json:/opt/etherpad-lite/settings.json depends_on: - postgres environment: