# homarr Deploy [Homarr](https://github.com/homarr-labs/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_extra_hosts` | `[]` | Container `/etc/hosts` overrides (`host:ip`) — pin IdP FQDN to LAN IP | | `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-` and `item-` | | `name` | yes | Display name | | `icon` | yes | Icon URL | | `href` | yes | Click target | | `width` | yes | Tile width in grid cells (1–10) | | `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 ```yaml 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`: ```yaml - 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: ```jinja {{ 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`: ```bash 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 ```yaml - name: Deploy Homarr service hosts: homarr_servers become: true roles: - digitalboard.core.homarr ``` With inventory variables: ```yaml # inventories//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: ```bash 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