digitalboard.core/roles/homarr/README.md

8.1 KiB
Raw Permalink Blame History

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

The assert task at the top of the role will fail fast if the encryption key 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_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 | 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 enriched with desktop, tablet, mobile sub-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 | 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.