digitalboard.core/roles/homarr/README.md
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

250 lines
8.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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_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
```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/<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:
```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