digitalboard.core/roles/homarr
Simon Bärlocher 3236ca332f
docs(collection): document all roles and fix metadata drift
Replace ansible-galaxy init placeholders across the collection and
correct documentation that drifted from the code, after a multi-agent
review of every role README against its defaults, tasks and templates.

Collection level:
- README: role table for all 16 roles, requirements and role-ordering
- galaxy.yml: declare community.docker and community.general deps,
  real description/tags/urls; normalize license to MIT-0
- meta/runtime.yml: requires_ansible '>=2.15.0'
- plugins/README: document the homarr_layout filter and
  garage_credentials lookup instead of scaffold boilerplate

Per-role meta/main.yml and README for the placeholder roles
(389ds, authentik, authentik_outpost_ldap, base, collabora, drawio,
garage, homarr, httpbin, keycloak, nextcloud, opencloud, traefik).

Correctness fixes found during review:
- keycloak: wrong domain default, drop invented keycloak_cert_resolver,
  document the provisioning feature
- garage: root_domain is .s3.<first-entry>, not the bare domain
- opnform: jwt/front_api secrets use `openssl rand -hex 32`; align the
  validation fail_msg in tasks/main.yml accordingly
- send: S3 example references garage_s3_domains[0] (was singular)
- opencloud: document required opencloud_wopi_domain

License normalized to MIT-0 across galaxy.yml, role meta and READMEs to
match the SPDX headers.
2026-05-27 23:12:24 +02:00
..
defaults feat(services): multi-domain routing, split-horizon and OIDC hardening 2026-05-27 23:12:24 +02:00
filter_plugins/tests feat(bookstack): add role for self-hosted BookStack deployment 2026-05-26 15:30:21 +02:00
handlers feat: drop blanket recreates, ACME-DNS knobs, notify_push override 2026-05-27 23:12:23 +02:00
meta docs(collection): document all roles and fix metadata drift 2026-05-27 23:12:24 +02:00
tasks feat(services): multi-domain routing, split-horizon and OIDC hardening 2026-05-27 23:12:24 +02:00
templates feat(services): multi-domain routing, split-horizon and OIDC hardening 2026-05-27 23:12:24 +02:00
tests chore: add new boilerplate role for homarr 2026-05-13 13:30:34 +00:00
vars chore: add new boilerplate role for homarr 2026-05-13 13:30:34 +00:00
README.md docs(collection): document all roles and fix metadata drift 2026-05-27 23:12:24 +02:00

homarr

Deploy Homarr as a self-contained Docker Compose stack behind Traefik, with seeded admin user, OIDC group and customizable application tiles.

What this role does

  • Deploys the official Homarr container with Traefik labels
  • Seeds the SQLite database with:
    • server settings (locale, analytics, crawling, default board)
    • a default board with the three layouts (desktop/tablet/mobile)
    • a local admin user with bcrypt-hashed password
    • OIDC and credentials admin groups with full permissions
    • application tiles defined in homarr_apps, auto-laid-out across all three screen sizes via the bundled homarr_compute_layouts filter
  • Skips the onboarding wizard so the instance is usable right after deploy
  • Restarts the container via handler when the seed or compose file changes

What this role does NOT do

  • Does not configure OIDC end-to-end — set homarr_oidc_* variables and configure the corresponding client in your identity provider
  • Does not migrate existing Homarr databases — only seeds empty ones
  • Does not create users beyond the single local admin (OIDC users are provisioned on first login)

Required variables

Provide via OpenBao, Ansible Vault, or extra-vars. Never commit real secrets to version control.

Variable Format Generate with
homarr_secret_encryption_key 64-char hex string openssl rand -hex 32
homarr_admin_password strong password openssl rand -base64 24
homarr_oidc_client_secret from your identity provider

homarr_oidc_client_secret is only required when oidc is in homarr_auth_providers; the role asserts it then. The encryption key is always required — the assert task at the top of the role fails fast if it is missing or malformed.

Configurable variables

See defaults/main.yml for the full list. Most useful overrides:

Variable Default Purpose
homarr_domain homarr.local.test Traefik Host rule
homarr_extra_domains [] Extra Host-rule hostnames (OR-combined), e.g. internal *.int.* FQDN
homarr_base_url https://home.local.test NEXTAUTH_URL / BASE_URL
homarr_auth_providers credentials credentials, oidc, or both
homarr_oidc_issuer empty Identity provider issuer URL
homarr_oidc_client_id empty OIDC client id
homarr_oidc_admin_group homarr-admins Group granting admin role
homarr_apps [] List of application tiles, see below

Application tiles

homarr_apps is a list of tile definitions that are seeded into the default board. Each entry needs:

Field Required Description
id yes Unique slug, used as app-<id> and item-<id>
name yes Display name
icon yes Icon URL
href yes Click target
width yes Tile width in grid cells (110)
description no Tooltip / subtitle
height no Tile height (default 1)

The role validates that all id values are unique.

Auto-layout

Tiles are packed left-to-right into three layouts:

  • Desktop: 10 columns
  • Tablet: 6 columns
  • Mobile: 2 columns

When a tile does not fit the remaining width of a row, it wraps to the next row. Tile width is clamped to the grid width (a tile with width: 8 becomes width: 6 on tablet and width: 2 on mobile).

Example

homarr_apps:
  - id: nextcloud
    name: Nextcloud
    description: Cloud Storage & Collaboration
    icon: https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/nextcloud.png
    href: https://cloud.example.com
    width: 2
  - id: keycloak
    name: Keycloak
    description: Identity & Access Management
    icon: https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/keycloak.png
    href: https://auth.example.com
    width: 2

Layout filter plugin

The grid-packing algorithm that places tiles on the desktop, tablet and mobile layouts lives in filter_plugins/homarr_layout.py rather than inside the Jinja seed template. This keeps the SQL template readable and lets the algorithm be unit-tested in isolation.

The filter is invoked once from tasks/main.yml:

- name: Compute Homarr app layouts
  ansible.builtin.set_fact:
    homarr_layout: "{{ homarr_apps | digitalboard.core.homarr_compute_layouts }}"

This produces a homarr_layout fact with two keys, both consumed by templates/homarr_seed.sql.j2:

Key Shape Purpose
apps list, same order as homarr_apps each entry gains desktop/tablet/mobile dicts of {x, y, w, h}
section_height dict with desktop, tablet, mobile minimum height of the parent section so all tiles fit

The filter signature accepts custom column counts if Homarr ever changes the breakpoint widths:

{{ homarr_apps | digitalboard.core.homarr_compute_layouts(desktop_cols=12, tablet_cols=8, mobile_cols=4) }}

To debug a layout without running the full deploy, run the play with -vv — the Show computed app layouts task dumps the full homarr_layout fact.

Running the filter tests

The filter is covered by unit tests in filter_plugins/tests/test_homarr_layout.py:

pip install pytest ansible-core
pytest filter_plugins/tests/

15 tests cover packing, width clamping, height/section-height, input validation and custom grid sizes.

First login

After the role completes, log in at {{ homarr_base_url }} with:

  • Username: value of homarr_admin_username (default admin)
  • Password: value of homarr_admin_password

OIDC users are provisioned on first login if their identity provider group matches homarr_oidc_admin_group. They receive admin permissions automatically through the seeded group-oidc-admins group.

Example playbook

- name: Deploy Homarr service
  hosts: homarr_servers
  become: true
  roles:
    - digitalboard.core.homarr

With inventory variables:

# inventories/<env>/group_vars/homarr_servers.yml
homarr_domain: home.digitalboard.ch
homarr_base_url: "https://home.digitalboard.ch"

homarr_auth_providers: "credentials,oidc"
homarr_oidc_issuer: "https://auth.digitalboard.ch/realms/Digitalboard"
homarr_oidc_client_id: "homarr-digitalboard"
homarr_oidc_client_name: "Digitalboard"

homarr_secret_encryption_key: >-
  {{ lookup('community.hashi_vault.vault_kv2_get',
            'digitalboard/homarr',
            mount_point='kv').data.data.encryption_key }}
homarr_admin_password: >-
  {{ lookup('community.hashi_vault.vault_kv2_get',
            'digitalboard/homarr',
            mount_point='kv').data.data.admin_password }}
homarr_oidc_client_secret: >-
  {{ lookup('community.hashi_vault.vault_kv2_get',
            'digitalboard/homarr',
            mount_point='kv').data.data.oidc_client_secret }}

homarr_apps:
  - id: nextcloud
    name: Nextcloud
    description: Cloud Storage
    icon: https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/nextcloud.png
    href: https://cloud.digitalboard.ch
    width: 2
  - id: keycloak
    name: Keycloak
    description: Identity & Access Management
    icon: https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/keycloak.png
    href: https://auth.digitalboard.ch
    width: 2

Re-running the role

The role is idempotent for the typical re-run case:

  • The seed only runs when no local admin user exists in the database
  • The compose file and seed template are deployed via template, which only changes content when the inputs change
  • The restart handler only fires when one of those templates changes

If you need to re-seed an existing database (for example after deleting the database file to apply schema changes), the role will detect the fresh database and seed it again on the next run.

Troubleshooting

Login fails after deploy. Verify that the bcrypt hash was written correctly:

sqlite3 /srv/data/homarr/homarr/appdata/db/db.sqlite \
  "SELECT id, name, email, length(password), provider FROM user;"

Expected: one row with user-local-admin, password length 60, provider credentials.

Encryption key validation fails. The key must be exactly 64 characters and contain only hex digits ([a-fA-F0-9]). Both upper- and lowercase are accepted.

App tiles overlap. Check homarr_apps for duplicate id values. The role validates this, but if you bypass the check, the seed will still run and Homarr will display only one of the duplicates.

License

MIT-0