- Drop `recreate: always` from collabora/drawio/homarr/opencloud/traefik
handlers and the authentik_outpost_ldap start task. `up -d` with
`state: present` already recreates exactly the services whose
compose definition changed; the blanket recreate was forcing
restarts even when nothing relevant moved.
- Rewrite the `*_domains` Traefik Host loop to the `Host(\`a\`) ||
Host(\`b\`)` form across authentik/collabora/garage/nextcloud so the
rule still matches when traefik can't normalize the comma-form into
the same canonical shape.
- Traefik: add `traefik_acme_tcp_only` (sets LEGO_EXPERIMENTAL_DNS_TCP_ONLY)
and `traefik_acme_disable_ans_checks` (disables lego's authoritative-NS
propagation check) for environments where the DNS path between the
traefik container and the zone's nameservers is constrained.
- Traefik DMZ collector: two-step merge so a `traefik_dmz_exposed_services`
entry that sets its own `backend_host` wins over the host fallback;
lets a route target an internal FQDN covered by the backend cert's
SANs instead of the raw IP.
- Nextcloud: add `nextcloud_notify_push_domain` override for the
`occ notify_push:setup` call so the setup check can hit an internal
FQDN instead of hairpinning through the DMZ. Push router now matches
every entry in `nextcloud_domains`.
- Nextcloud: also %2F-escape slashes in the postgres user/password
inside the notify_push DATABASE_URL.
Deploy BookStack with linuxserver.io images behind Traefik, including
Entra ID OIDC SSO support and a daily backup timer.
Stack:
- lscr.io/linuxserver/bookstack:version-v26.03.3
- lscr.io/linuxserver/mariadb:11.4.9
- Traefik labels for websecure entrypoint on internal network
- Healthcheck via mariadb-admin ping (LSIO image lacks healthcheck.sh)
Features:
- Persistent APP_KEY generated on first run, stored in volume dir
- Optional OIDC SSO via Microsoft Entra ID (configurable per-instance)
- Idempotent admin user creation with DB-based existence check
- Daily systemd timer backup (DB dump + uploads tar + APP_KEY)
with configurable retention
Implementation notes:
- DB queries use --protocol=tcp with the app user because root@localhost
uses unix_socket auth in the LSIO MariaDB image (no password) and
root@% does not exist
- docker_container_exec uses argv: (list) instead of command: (string)
to avoid argument-splitting issues
- Migration-wait task ensures users table exists before admin check,
since /login returns 200 before Laravel migrations complete
- no_log: true on all tasks that reference DB or admin passwords
- artisan absolute path (/app/www/artisan) because LSIO image WORKDIR
is not the app directory
Adds bookstack route to DMZ Traefik service registry.