Initial commit: arr-stack homelab + local AI

Docker Compose media stack (gluetun, *arr, Jellyfin), Ternary-Bonsai AI
(Open WebUI, Forgejo, optional OpenHands), and Ansible bootstrap for the
SER5 Ubuntu host.
This commit is contained in:
tim 2026-07-22 06:28:49 -07:00
commit 3645d1314c
33 changed files with 2673 additions and 0 deletions

View file

@ -0,0 +1,171 @@
---
# Local homelab operator account — broad permissions (LAN-trusted host)
- name: Ensure stack user exists with bash shell
ansible.builtin.user:
name: "{{ stack_owner }}"
state: present
shell: /bin/bash
create_home: true
groups: "{{ extra_user_groups }}"
append: true
tags: [users, permissions]
- name: Ensure useful system groups exist (create if missing)
ansible.builtin.group:
name: "{{ item }}"
state: present
loop: "{{ extra_user_groups }}"
failed_when: false
tags: [users, permissions]
- name: Add stack user to all useful groups
ansible.builtin.user:
name: "{{ stack_owner }}"
groups: "{{ extra_user_groups }}"
append: true
tags: [users, permissions]
- name: Passwordless sudo for stack user (full admin)
ansible.builtin.copy:
dest: "/etc/sudoers.d/{{ stack_owner }}"
mode: "0440"
owner: root
group: root
validate: "visudo -cf %s"
content: |
# Homelab operator — passwordless sudo (local trusted host)
{{ stack_owner }} ALL=(ALL) NOPASSWD:ALL
Defaults:{{ stack_owner }} !env_reset
Defaults:{{ stack_owner }} secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin"
tags: [users, permissions, sudo]
- name: Allow stack user to manage Docker and arr-stack via systemctl without password
ansible.builtin.copy:
dest: "/etc/sudoers.d/{{ stack_owner }}-services"
mode: "0440"
owner: root
group: root
validate: "visudo -cf %s"
content: |
# Redundant with NOPASSWD:ALL; kept explicit for clarity if sudo is tightened later
{{ stack_owner }} ALL=(root) NOPASSWD: /bin/systemctl * docker*, /bin/systemctl * containerd*, /bin/systemctl * arr-stack*, /usr/bin/systemctl * docker*, /usr/bin/systemctl * containerd*, /usr/bin/systemctl * arr-stack*
tags: [users, permissions, sudo]
- name: Install ACL tools
ansible.builtin.apt:
name: acl
state: present
tags: [users, permissions]
- name: Own stack and storage roots
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: "{{ stack_owner }}"
group: "{{ stack_group }}"
mode: "0775"
loop:
- "{{ stack_root }}"
- "{{ storage_root }}"
tags: [users, permissions]
- name: Recursive ownership of stack tree
ansible.builtin.file:
path: "{{ stack_root }}"
state: directory
owner: "{{ stack_owner }}"
group: "{{ stack_group }}"
recurse: true
tags: [users, permissions]
- name: Recursive ownership of storage tree
ansible.builtin.file:
path: "{{ storage_root }}"
state: directory
owner: "{{ stack_owner }}"
group: "{{ stack_group }}"
recurse: true
# Large trees: may take a moment; safe to re-run
tags: [users, permissions]
- name: Default ACLs so new files under stack stay group-writable
ansible.builtin.command: >
setfacl -R -d -m u:{{ stack_owner }}:rwx,g:{{ stack_group }}:rwx,o::r-x {{ item }}
loop:
- "{{ stack_root }}"
- "{{ storage_root }}"
changed_when: true
failed_when: false
tags: [users, permissions]
- name: Current ACLs for stack owner full access
ansible.builtin.command: >
setfacl -R -m u:{{ stack_owner }}:rwx,g:{{ stack_group }}:rwx {{ item }}
loop:
- "{{ stack_root }}"
- "{{ storage_root }}"
changed_when: true
failed_when: false
tags: [users, permissions]
- name: Discover DRM nodes for permission tweak
ansible.builtin.find:
paths: /dev/dri
patterns: "*"
file_type: any
register: dri_nodes
failed_when: false
tags: [users, permissions, gpu]
- name: Make /dev/dri nodes world-accessible (trusted local host)
ansible.builtin.shell: |
set -e
if [ -d /dev/dri ]; then
chmod a+rw /dev/dri/* 2>/dev/null || true
ls -la /dev/dri
fi
register: dri_chmod
changed_when: "'rw' in dri_chmod.stdout"
failed_when: false
tags: [users, permissions, gpu]
- name: Persist permissive DRM udev rule for trusted host
ansible.builtin.copy:
dest: /etc/udev/rules.d/99-homelab-dri-permissive.rules
mode: "0644"
content: |
# Trusted LAN homelab — allow any user/container access to GPU nodes
KERNEL=="renderD*", MODE="0666"
KERNEL=="card*", MODE="0666"
KERNEL=="controlD*", MODE="0666"
notify: Reload udev
tags: [users, permissions, gpu]
- name: Ensure docker socket is group-writable for docker group
ansible.builtin.file:
path: /var/run/docker.sock
group: docker
mode: "0660"
failed_when: false
tags: [users, permissions, docker]
- name: Verify stack user can sudo without password
become: true
become_user: "{{ stack_owner }}"
ansible.builtin.command: sudo -n true
changed_when: false
tags: [users, permissions]
- name: Show stack user identity and groups
become: true
become_user: "{{ stack_owner }}"
ansible.builtin.command: id
register: stack_user_id
changed_when: false
tags: [users, permissions]
- name: Report stack user permissions
ansible.builtin.debug:
msg: "{{ stack_user_id.stdout }}"
tags: [users, permissions]