#SPDX-License-Identifier: MIT-0 --- # tasks file for homarr # ===================================================================== # 0. VALIDATION # ===================================================================== - name: Validate encryption key ansible.builtin.assert: that: - homarr_secret_encryption_key | length == 64 - homarr_secret_encryption_key is match('^[a-fA-F0-9]+$') fail_msg: >- homarr_secret_encryption_key must be a 64-character hex string. Generate with: openssl rand -hex 32 Provide via OpenBao, Ansible Vault or extra-vars. success_msg: Encryption key validation passed - name: Validate OIDC configuration when enabled ansible.builtin.assert: that: - homarr_oidc_client_secret | length > 0 fail_msg: >- homarr_oidc_client_secret must be set when 'oidc' is in homarr_auth_providers. Set via OpenBao or remove 'oidc' from homarr_auth_providers. when: "'oidc' in homarr_auth_providers" - name: Validate homarr_apps have unique ids ansible.builtin.assert: that: - homarr_apps | map(attribute='id') | list | length == homarr_apps | map(attribute='id') | unique | list | length fail_msg: >- homarr_apps contains duplicate ids. Each app must have a unique 'id'. Got: {{ homarr_apps | map(attribute='id') | list }} success_msg: All app ids are unique when: homarr_apps | length > 0 # ===================================================================== # 1. PREPARATION: packages and directories before container start # ===================================================================== - name: Ensure required packages are installed ansible.builtin.package: name: - sqlite3 - python3-docker state: present - name: Create docker compose directory ansible.builtin.file: path: "{{ homarr_docker_compose_dir }}" state: directory mode: '0755' - name: Create Homarr data directories ansible.builtin.file: path: "{{ item }}" state: directory owner: "1000" group: "1000" mode: "0755" loop: - "{{ homarr_appdata_dir }}" - "{{ homarr_appdata_dir }}/db" - name: Check if database already exists ansible.builtin.stat: path: "{{ homarr_db }}" register: db_exists # ===================================================================== # 2. START CONTAINER # ===================================================================== - name: Create docker-compose file for homarr ansible.builtin.template: src: docker-compose.yml.j2 dest: "{{ homarr_docker_compose_dir }}/docker-compose.yml" mode: '0644' - name: Start homarr containers community.docker.docker_compose_v2: project_src: "{{ homarr_docker_compose_dir }}" state: present # ===================================================================== # 3. WAIT FOR DATABASE # ===================================================================== - name: Wait for database to be created by Homarr ansible.builtin.wait_for: path: "{{ homarr_db }}" state: present timeout: 60 when: not db_exists.stat.exists - name: Wait for database schema to be initialized ansible.builtin.command: cmd: sqlite3 "{{ homarr_db }}" "SELECT name FROM sqlite_master WHERE type='table' AND name='board';" register: schema_check until: schema_check.stdout == "board" retries: 30 delay: 2 changed_when: false when: not db_exists.stat.exists # ===================================================================== # 4. GENERATE BCRYPT HASH (on controller, not on target) # ===================================================================== - name: Generate bcrypt hash for admin password ansible.builtin.shell: cmd: python3 -c "import bcrypt, sys; print(bcrypt.hashpw(sys.stdin.read().encode(), bcrypt.gensalt(rounds=10)).decode())" stdin: "{{ homarr_admin_password }}" stdin_add_newline: false delegate_to: localhost become: false register: bcrypt_result changed_when: false no_log: true - name: Set bcrypt hash fact ansible.builtin.set_fact: homarr_bcrypt_hash: "{{ bcrypt_result.stdout }}" no_log: true # ===================================================================== # 5. COMPUTE APP LAYOUTS # ===================================================================== # Packing is done by the homarr_compute_layouts filter plugin (Python) # rather than inline Jinja, so the seed template stays readable and the # packing algorithm can be unit-tested in isolation. - name: Compute Homarr app layouts ansible.builtin.set_fact: homarr_layout: "{{ homarr_apps | digitalboard.core.homarr_compute_layouts }}" - name: Show computed app layouts ansible.builtin.debug: var: homarr_layout verbosity: 1 # ===================================================================== # 6. SEED DATABASE (only if local admin user does not exist yet) # ===================================================================== - name: Check if local admin user exists ansible.builtin.command: cmd: sqlite3 "{{ homarr_db }}" "SELECT id FROM user WHERE id='user-local-admin';" register: admin_exists changed_when: false failed_when: false - name: Seed Homarr database ansible.builtin.command: cmd: sqlite3 "{{ homarr_db }}" stdin: "{{ lookup('template', 'homarr_seed.sql.j2') }}" register: seed_result changed_when: seed_result.rc == 0 when: admin_exists.stdout == "" notify: restart homarr