e7b8d4df17
Self-hosted location history. 4-container compose: Rails 8 app + Sidekiq + PostGIS 16-3.4 + Redis 7, plus watchtower. Authentik OIDC end-to-end. Image pinned at freikin/dawarich:1.7.11 (OIDC support requires >= 1.7.8). PostGIS DB lives in this LXC, not on the central DB VM (.172) — central image is postgres:16-alpine without postgis, swapping it carries broader blast radius than colocating here. Convention exception captured in homelab-docs project_dawarich memory. Roles: - dawarich: system + Docker + compose + weekly prune timer - alloy: logs+journald → Loki, node metrics → Prometheus Bring-up sequence proven 2026-06-01. README documents the 5-trap build chain (image version, entrypoint scripts, solid_cache SQLite bind mount, APPLICATION_HOSTS+localhost, force_ssl+healthcheck). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
208 lines
5.3 KiB
YAML
208 lines
5.3 KiB
YAML
---
|
|
# ---------- System setup ----------
|
|
- name: Set timezone
|
|
copy:
|
|
content: "{{ timezone }}"
|
|
dest: /etc/timezone
|
|
mode: '0644'
|
|
register: tz_file
|
|
|
|
- name: Apply timezone
|
|
command: dpkg-reconfigure -f noninteractive tzdata
|
|
when: tz_file.changed
|
|
|
|
- name: Set hostname
|
|
copy:
|
|
content: "dawarich"
|
|
dest: /etc/hostname
|
|
mode: '0644'
|
|
|
|
- name: Apply hostname
|
|
command: hostname dawarich
|
|
changed_when: false
|
|
|
|
- name: Ensure hostname in /etc/hosts
|
|
lineinfile:
|
|
path: /etc/hosts
|
|
regexp: '^127\.0\.1\.1'
|
|
line: "127.0.1.1 dawarich"
|
|
|
|
- name: Update apt cache
|
|
apt:
|
|
update_cache: true
|
|
cache_valid_time: 3600
|
|
register: apt_cache_result
|
|
failed_when: false
|
|
changed_when: apt_cache_result.changed | default(false)
|
|
|
|
- name: Install base packages
|
|
apt:
|
|
name: "{{ packages }}"
|
|
state: present
|
|
|
|
- name: Create user
|
|
user:
|
|
name: "{{ item.name }}"
|
|
groups: "{{ item.groups }}"
|
|
shell: "{{ item.shell }}"
|
|
append: true
|
|
loop: "{{ users }}"
|
|
|
|
- name: Deploy SSH authorized keys
|
|
authorized_key:
|
|
user: cbalders
|
|
key: "{{ item }}"
|
|
state: present
|
|
loop: "{{ ssh_authorized_keys }}"
|
|
|
|
# ---------- Docker ----------
|
|
- name: Check if Docker is installed
|
|
command: docker --version
|
|
register: docker_check
|
|
changed_when: false
|
|
failed_when: false
|
|
|
|
- name: Install Docker
|
|
when: docker_check.rc != 0
|
|
block:
|
|
- name: Download Docker install script
|
|
get_url:
|
|
url: https://get.docker.com
|
|
dest: /tmp/get-docker.sh
|
|
mode: '0755'
|
|
|
|
- name: Run Docker install script
|
|
command: /tmp/get-docker.sh
|
|
|
|
- name: Remove install script
|
|
file:
|
|
path: /tmp/get-docker.sh
|
|
state: absent
|
|
|
|
- name: Ensure Docker service is running
|
|
systemd:
|
|
name: docker
|
|
state: started
|
|
enabled: true
|
|
|
|
- name: Add cbalders to docker group
|
|
user:
|
|
name: cbalders
|
|
groups: docker
|
|
append: true
|
|
|
|
# ---------- Docker registry mirror ----------
|
|
- name: Ensure /etc/docker exists
|
|
file:
|
|
path: /etc/docker
|
|
state: directory
|
|
mode: '0755'
|
|
|
|
- name: Read existing daemon.json
|
|
slurp:
|
|
src: /etc/docker/daemon.json
|
|
register: daemon_json_raw
|
|
failed_when: false
|
|
|
|
- name: Configure Docker registry mirror
|
|
copy:
|
|
content: "{{ ((daemon_json_raw.content | b64decode | from_json) if daemon_json_raw.content is defined else {}) | combine({'registry-mirrors': ['http://registry.lan.balders.ca:5000']}) | to_nice_json }}"
|
|
dest: /etc/docker/daemon.json
|
|
owner: root
|
|
group: root
|
|
mode: '0644'
|
|
register: docker_daemon_config
|
|
|
|
- name: Restart Docker if mirror config changed
|
|
systemd:
|
|
name: docker
|
|
state: restarted
|
|
when: docker_daemon_config.changed
|
|
|
|
# ---------- Dawarich stack ----------
|
|
- name: Create app directories
|
|
file:
|
|
path: "{{ item.path }}"
|
|
state: directory
|
|
owner: "{{ item.owner | default('cbalders') }}"
|
|
group: "{{ item.group | default('cbalders') }}"
|
|
mode: '0755'
|
|
loop:
|
|
- { path: /opt/dawarich }
|
|
# Postgis container runs as uid 70 (alpine 'postgres' user). Pre-chown
|
|
# so the first boot doesn't fail with "directory is not empty / wrong
|
|
# permissions" the way Nextcloud did (feedback_nextcloud_db_perms).
|
|
- { path: /opt/dawarich/postgres, owner: '70', group: '70' }
|
|
# Redis runs as uid 999 on bind volumes (feedback_redis_uid_999).
|
|
- { path: /opt/dawarich/redis, owner: '999', group: '999' }
|
|
# Dawarich Rails app writes uploads/cache here as uid 1000.
|
|
- { path: /opt/dawarich/public }
|
|
- { path: /opt/dawarich/watched }
|
|
- { path: /opt/dawarich/storage }
|
|
# solid_cache + solid_queue + solid_cable SQLite files (writable by the
|
|
# rails uid inside the container — image runs as uid 1000).
|
|
- { path: /opt/dawarich/db_data }
|
|
|
|
- name: Deploy docker-compose
|
|
template:
|
|
src: docker-compose.yml.j2
|
|
dest: /opt/dawarich/docker-compose.yml
|
|
owner: cbalders
|
|
group: cbalders
|
|
mode: '0640'
|
|
register: compose_changed
|
|
|
|
- name: Pull images
|
|
command: docker compose pull
|
|
args:
|
|
chdir: /opt/dawarich
|
|
changed_when: false
|
|
|
|
- name: Recreate containers if config changed
|
|
command: docker compose up -d --force-recreate
|
|
args:
|
|
chdir: /opt/dawarich
|
|
when: compose_changed.changed
|
|
changed_when: true
|
|
|
|
- name: Ensure containers are running
|
|
command: docker compose up -d
|
|
args:
|
|
chdir: /opt/dawarich
|
|
when: not compose_changed.changed
|
|
changed_when: false
|
|
|
|
# Dawarich runs migrations on boot (entrypoint includes db:migrate). First
|
|
# boot can take ~60-90s because the initial schema + PostGIS extension load
|
|
# is heavy. Don't fail the play if it's not ready yet — Watchtower-driven
|
|
# recreates will converge.
|
|
- name: Wait for Dawarich to be healthy
|
|
uri:
|
|
url: "http://localhost:{{ dawarich_port }}/api/v1/health"
|
|
method: GET
|
|
register: dawarich_health
|
|
until: dawarich_health.status is defined and dawarich_health.status == 200
|
|
retries: 30
|
|
delay: 5
|
|
ignore_errors: true
|
|
|
|
# ---------- Weekly docker prune (Watchtower side-effect cleanup) ----------
|
|
- name: Deploy docker-prune systemd units
|
|
copy:
|
|
src: "{{ item }}"
|
|
dest: "/etc/systemd/system/{{ item }}"
|
|
owner: root
|
|
group: root
|
|
mode: '0644'
|
|
loop:
|
|
- docker-prune.service
|
|
- docker-prune.timer
|
|
notify: reload systemd
|
|
|
|
- name: Enable docker-prune timer
|
|
systemd:
|
|
name: docker-prune.timer
|
|
enabled: true
|
|
state: started
|
|
daemon_reload: true
|