Addresses the WKS PoC review (Notion 2026-05-26). All docs in English. - README: purpose, docs table of contents, annotated repo tree - docs/getting_started.md: prerequisites (WKS account, OIDC, SSH, VPN) + first deploy - docs/ansible.md: playbook table, "Running Ansible", service parameters, cheatsheet - docs/secrets.md: canonical Bao login (moved out of README) + demo defaults - docs/operations.md: full Makefile reference - docs/inventories.md: repo layout, topology, standard folder structure, walkthrough - docs/testing.md: static checks, inventory resolution, smoke test / dry run - remove ARCHITECTURE.md (architecture docs live externally) Also includes the gymburgdorf inventory build-out (bookstack, homarr, opnform, send) and scripts/bao-seed.sh. site.yml keeps a third traefik play (traefik_servers minus the vagrant _dmz/_backend split) so the demo inventories still configure their reverse proxy after the rebase onto main.
10 KiB
Playbooks & Parameters
Central reference: which plays playbooks/site.yml
runs, which service parameters are relevant per role, and where they are
located in the inventory. Example used throughout: demo-gymburgdorf.
Playbook site.yml
The only playbook is playbooks/site.yml. It
consists of a sequence of plays, each applying one role from
digitalboard.core to a host group. All plays run with
become: yes. Plays whose group has no members in an inventory run as a
no-op.
| # | Play / role | hosts: |
Target in demo-gymburgdorf? |
|---|---|---|---|
| 1 | digitalboard.core.base |
all_servers |
✅ all 4 hosts |
| 2 | digitalboard.core.traefik |
traefik_servers_backend |
— no-op (vagrant-only group) |
| 3 | digitalboard.core.traefik |
traefik_servers_dmz |
— no-op (vagrant-only group) |
| 4 | digitalboard.core.traefik |
traefik_servers:!traefik_servers_dmz:!traefik_servers_backend |
✅ all 4 (dmz on reverseproxy, otherwise backend) |
| 5 | digitalboard.core.httpbin |
httpbin_servers |
— no-op |
| 6 | digitalboard.core.389ds |
ds389_servers |
— no-op |
| 7 | digitalboard.core.keycloak |
keycloak_servers |
— no-op |
| 8 | digitalboard.core.garage |
garage_servers |
✅ storage |
| 9 | digitalboard.core.collabora |
collabora_servers |
✅ application |
| 10 | digitalboard.core.authentik |
authentik_servers |
✅ application |
| 11 | digitalboard.core.authentik_outpost_ldap |
authentik_outpost_ldap_servers |
✅ application |
| 12 | digitalboard.core.nextcloud |
nextcloud_servers |
✅ application |
| 13 | digitalboard.core.drawio |
drawio_servers |
✅ application |
| 14 | digitalboard.core.send |
send_servers |
✅ application |
| 15 | digitalboard.core.opnform |
opnform_servers |
✅ application |
| 16 | digitalboard.core.homarr |
homarr_servers |
✅ application |
| 17 | digitalboard.core.bookstack |
bookstack_servers |
✅ application |
| 18 | digitalboard.core.opencloud |
opencloud_servers |
— no-op (no group) |
Three traefik plays, two topologies.
vagrantsplits the reverse proxy intotraefik_servers_dmz+traefik_servers_backend(plays 2 and 3). The demo inventories (e.g.demo-gymburgdorf) instead group all hosts undertraefik_serversand select dmz/backend per host viatraefik_mode; play 4's:!…intersection targets exactly those hosts and stays a no-op for the vagrant split. Each topology thus triggers only the traefik play(s) that fit it — no host runs traefik twice.Which plays take effect for a tenant is controlled solely through group membership in
hosts.yml. A service becomes active as soon as its<service>_serversgroup contains a host and a matchinghost_vars/<host>/<service>.ymlexists.
Running Ansible
Prerequisite: collections installed (make install) and logged in
to OpenBao in the same shell (VAULT_TOKEN set) — without a token,
the community.hashi_vault lookups fail. Login procedure:
secrets.md § OpenBao login.
Initial setup step by step: getting_started.md.
Via Makefile (recommended)
make ping_demo # Smoke test (ping) against all demo inventories
make deploy_site_demo_gymburgdorf # single demo site
make deploy_site_demo # all three demo sites in sequence
The Make targets encapsulate the full ansible-playbook invocation
including --diff and the macOS fork env var. All targets:
operations.md § Makefile reference.
Direct ansible-playbook invocation
When you need flags that the targets do not set:
# Full deploy of an inventory
ansible-playbook playbooks/site.yml \
-i inventories/demo-gymburgdorf/hosts.yml --diff
# Only one host (e.g. just the application machine)
ansible-playbook playbooks/site.yml \
-i inventories/demo-gymburgdorf/hosts.yml --limit application
# Dry run without changes
ansible-playbook playbooks/site.yml \
-i inventories/demo-gymburgdorf/hosts.yml --check --diff
Because service selection runs through the groups in
hosts.yml(not through tags),--limit <host>is the usual way to narrow down a deploy.--checkis only of limited value with the Docker Compose-based roles — some tasks report "changed" because they only learn the actual container state at runtime.
Deploy flow and play order: operations.md § Deploy.
Where parameters belong
| Variable group | File in demo-gymburgdorf/ |
Why |
|---|---|---|
vault_addr, vault_mount |
group_vars/all/vault.yml |
Bao endpoint applies site-wide |
docker_registry_mirrors |
group_vars/all/docker.yml |
Pulls from mirror on all hosts |
traefik_acme_*, traefik_use_ssl, traefik_cert_mode, traefik_log_level |
group_vars/traefik_servers/traefik.yml |
applies to all Traefik instances (dmz + backend) |
traefik_mode: backend |
group_vars/backend_servers/traefik.yml |
default for app + storage |
traefik_mode: dmz, traefik_dmz_exposed_services |
host_vars/reverseproxy/traefik.yml |
host-specific override, only meaningful there |
nextcloud_*, authentik_*, collabora_*, drawio_*, send_*, opnform_*, homarr_*, bookstack_* |
host_vars/application/<service>.yml |
service runs on application |
garage_* |
host_vars/storage/garage.yml |
service runs on storage |
| Secrets (passwords, tokens, keys) | inline var with lookup('community.hashi_vault.hashi_vault', …) |
single source of truth via Bao, see secrets.md |
Service parameters in detail
Complete variable lists are in the defaults/main.yml of the respective
role in digitalboard.core. Below are the parameters maintained in the
demo inventories per service — as guidance on which fields a new tenant
typically needs to set.
traefik
| Variable | Example / default | Purpose |
|---|---|---|
traefik_mode |
dmz | backend |
Provider mode: dmz = file provider (public-facing, no Docker socket), backend = docker provider (auto-discovery via container labels) |
traefik_cert_mode |
acme | selfsigned |
Certificate source |
traefik_use_ssl |
true |
TLS active |
traefik_ssl_email |
hostmaster@digitalboard.ch |
ACME contact |
traefik_log_level |
DEBUG (role default INFO) |
reduce for prod |
traefik_network |
proxy |
Docker network for backend mode |
traefik_acme_dns_zone |
demo-gymb._acme.digitalboard.ch |
RFC2136 update zone |
traefik_acme_dns_nameserver |
from Bao / 172.16.9.169 (DMZ override) |
TSIG update target |
traefik_acme_tsig_algorithm / _key / _secret |
hmac-sha256 / Bao |
TSIG signature |
traefik_acme_tcp_only |
true |
force DNS lookups over TCP/53 |
traefik_acme_disable_ans_checks |
true (DMZ only) |
skip NS propagation poll |
traefik_dmz_exposed_services |
list (DMZ) | which backends the DMZ Traefik routes |
authentik (IdP — OIDC + LDAP outpost backend)
| Variable | Purpose |
|---|---|
authentik_domains |
public FQDNs (auth.gymb.souveredu.ch) |
authentik_host_rewrite_domains |
internal *.int.* names for LAN server-to-server |
authentik_secret_key, authentik_postgres_password |
Bao lookup |
authentik_ldap_apps, authentik_ldap_outpost |
LDAP app + outpost definition (base_dn, token) |
authentik_proxy_apps |
ForwardAuth apps (slug, external/internal_host, allowed_groups) |
authentik_outpost_ldap
| Variable | Purpose |
|---|---|
authentik_outpost_ldap_host |
internal Authentik host (https://auth.int.…) |
authentik_outpost_ldap_token |
outpost token (Bao, identical to authentik.ldap_outpost_token) |
nextcloud
| Variable | Purpose |
|---|---|
nextcloud_image |
image tag (pin to patched version) |
nextcloud_domains |
first entry = canonical public FQDN, further *.int.* |
nextcloud_admin_user / _password, nextcloud_postgres_password |
admin + DB (Bao) |
nextcloud_use_s3_storage, nextcloud_s3_* |
S3 primary storage via Garage (key/secret via garage_credentials lookup) |
nextcloud_enable_collabora, nextcloud_collabora_domain / _public_domain |
WOPI integration |
nextcloud_enable_drawio, nextcloud_drawio_url |
Draw.io integration |
nextcloud_oidc_providers |
OIDC login via Authentik (discovery_url, client_id/secret) |
nextcloud_ldap_enabled, nextcloud_ldap_config |
LDAP backend against Authentik outpost |
nextcloud_apps_to_install |
app list (groupfolders, richdocuments, spreed, user_ldap, …) |
nextcloud_allow_local_remote_servers, nextcloud_extra_hosts |
LAN-only routing for server-to-server calls |
collabora
| Variable | Purpose |
|---|---|
collabora_domains |
public + internal FQDN |
collabora_allowed_domains, collabora_frame_ancestors |
allowed WOPI hosts / iframe embedding |
drawio
| Variable | Purpose |
|---|---|
drawio_domain, drawio_extra_domains |
public + internal FQDN |
drawio_authentik_forward_auth, _url |
access protection via Authentik ForwardAuth |
garage (S3 object store)
| Variable | Purpose |
|---|---|
garage_s3_domains |
first entry = public S3 FQDN, further *.int.* |
garage_webui_domain, garage_webui_enabled |
admin WebUI |
garage_webui_authentik_forward_auth, _url |
WebUI behind Authentik (admins only) |
garage_rpc_secret, garage_admin_token, garage_metrics_token |
Bao lookup |
garage_bootstrap_* |
single-node cluster bootstrap (zone, capacity) |
garage_s3_keys |
keys + buckets + permissions (e.g. nextcloud) |
send / opnform / homarr / bookstack
Same pattern: <service>_domain/_domains (+ *.int.*),
<service>_base_url, admin credentials and app keys via Bao lookup,
plus OIDC integration with Authentik (<service>_oidc_*: issuer, client_id,
client_secret, admin_group). For the concrete fields, see the respective
host_vars/application/<service>.yml.
Variable cheatsheet
Short form of the location table above — "which variable goes where":
- Site-wide →
group_vars/all/(Bao endpoint, Docker mirror) - All Traefik →
group_vars/traefik_servers/ - app + storage →
group_vars/backend_servers/ - Single host →
host_vars/<host>/<service>.yml - Secrets → always Bao lookup, never plaintext (see secrets.md)