main
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>
homelab-ansible-lxc-dawarich
Configuration for dawarich (LXC 459 on pve02, 192.168.1.159) — self-hosted
location-history app, Dawarich. Ingests location data
from OwnTracks, the Dawarich iOS/Android apps, and Google Takeout exports;
renders heatmaps, places visited, distance/time statistics, and timeline view.
- URL:
dawarich.balders.ca(Caddy →192.168.1.159:3000, Authentik OIDC) - Image:
freikin/dawarich:1.7.11(pinned invars/main.yml) — must be ≥ 1.7.8 for OIDC. - Tier: T2 (Proxmox)
- Auth: Authentik OIDC (SSO-only —
ALLOW_EMAIL_PASSWORD_REGISTRATION=false) - DB: PostGIS 16-3.4 local to this LXC. The central DB VM (.172) runs
postgres:16-alpinewhich doesn't bundle the PostGIS extension; rather than swap that image (16 other databases) we run a colocated container here. See memoryproject_dawarichfor the convention-exception rationale.
Architecture
Phone (OwnTracks / Dawarich app)
│ HTTP POST /api/v1/owntracks/points?api_key=…
▼
Caddy (TLS, Authentik OIDC on UI; API bypassed by query-string auth)
│
▼
dawarich_app (Rails 8 + Puma) ──▶ dawarich_db (PostGIS 16-3.4)
│ ▲
▼ │
dawarich_sidekiq ─── enqueue ─────────── dawarich_redis
└──── solid_queue ──▶ /dawarich_db_data (SQLite)
Roles
| Role | Purpose |
|---|---|
dawarich |
System setup, Docker, registry mirror, 4-container compose, prune timer |
alloy |
Docker logs + journald → Loki, node metrics → Prometheus |
Prerequisites (before first deploy)
- LXC provisioned —
module.dawarichin homelab-terraform (CTID 459, pve02,.159).tf.sh apply lxcthen bootstrap the cbalders user manually on the new CT (the bpg module only seeds root keys; cbalders + NOPASSWD sudo is post-create). Seefeedback_lxc_bootstrap_user. vars/vault.yml— holdsvault_infisical_client_secret. Shared universal-auth client across all LXC repos — copy fromhomelab-ansible-lxc-rag/vars/vault.yml.- Infisical
/dawarichfolder + 5 secrets — run./scripts/bootstrap-secrets.sh. It creates the folder if missing and pushes:vault_dawarich_db_password(openssl rand -hex 32)vault_dawarich_secret_key_base(openssl rand -hex 64)vault_dawarich_otp_primary_key(openssl rand -hex 32)vault_dawarich_otp_deterministic_key(openssl rand -hex 32)vault_dawarich_otp_salt(openssl rand -hex 32)
- Infisical
/oidcsecrets — generatevault_dawarich_oidc_client_id(40 hex) andvault_dawarich_oidc_client_secret(64 hex), push as--type shared. These values ARE the credentials — pi-auth creates the Authentik provider with them, it doesn't fetch them back. Generation pattern:infisical secrets set "vault_dawarich_oidc_client_id=$(openssl rand -hex 20)" \ --projectId 50062d7c-06ff-4d5c-8ca3-6c0cdba9f270 --env prod --path /oidc --type shared infisical secrets set "vault_dawarich_oidc_client_secret=$(openssl rand -hex 32)" \ --projectId 50062d7c-06ff-4d5c-8ca3-6c0cdba9f270 --env prod --path /oidc --type shared - pi-auth site.yml mapping —
homelab-ansible-pi-auth/site.ymlmust have adawarich_oidc_client_id/dawarich_oidc_client_secretset_fact line reading fromoidc_secrets.secrets.vault_dawarich_oidc_client_{id,secret}. Without this, pi-auth's loop fails with'dawarich_oidc_client_id' is undefined.
Deploy
./deploy.sh # full deploy
./deploy.sh --tags dawarich # dawarich role only
./logs.sh -f # follow dawarich_app
./logs.sh dawarich_sidekiq -f # follow background jobs
Bring-up order (proven 2026-06-01)
cd ../homelab-terraform && ./tf.sh apply lxc→ CT 459 created.- Bootstrap
cbaldersuser + SSH keys + NOPASSWD sudo on the new CT:ssh root@pve02 'pct exec 459 -- bash -c " useradd -m -s /bin/bash -G sudo cbalders && mkdir -p /home/cbalders/.ssh && cat > /home/cbalders/.ssh/authorized_keys <<EOF <your-keys-here> EOF chown -R cbalders:cbalders /home/cbalders/.ssh && chmod 700 /home/cbalders/.ssh && chmod 600 /home/cbalders/.ssh/authorized_keys && apt-get update -qq && apt-get install -y -qq sudo && mkdir -p /etc/sudoers.d && echo \"cbalders ALL=(ALL) NOPASSWD:ALL\" > /etc/sudoers.d/cbalders && chmod 440 /etc/sudoers.d/cbalders"' ./scripts/bootstrap-secrets.sh→ 5/dawarichsecrets in Infisical.- Push
/oidcsecrets (see prereq 4 above). - Add
dawarich_oidc_client_{id,secret}set_fact mapping inhomelab-ansible-pi-auth/site.yml. - First
./deploy.shhere → app boots, migrations run (~3 min cold-start). cd ../homelab-ansible-pi-auth && ./deploy.sh→ creates Authentik provider + application.cd ../homelab-ansible-proxy && ./deploy.sh→ adds Caddy vhost + Technitium CNAME.cd ../homelab-ansible-pve && ./deploy.sh→ adds CTID 459 topbs-prod-daily.- Browser-test
https://dawarich.balders.ca/users/sign_in→ click "Sign in with Authentik".
Mobile clients
After first OIDC login, the user goes to Settings → API Keys in the Dawarich UI to generate a per-user token. Use it as the bearer for either:
- OwnTracks (any platform) — HTTP recorder mode, URL:
https://dawarich.balders.ca/api/v1/owntracks/points?api_key=<TOKEN> - Dawarich iOS/Android app — paste the token + host on first launch.
Google Takeout import
- Take the
location-history.jsonout of the Takeout archive. - Drop it into
/opt/dawarich/watched/on the LXC (NFS mount orscp). - Sidekiq picks it up via the import watcher within ~30s and emits progress
to
dawarich_sidekiqlogs. Large histories (>10y) can take an hour.
Gotchas (build-day 2026-06-01)
- Image version pin. OIDC support landed in
freikin/dawarich:1.7.8. The0.27.xline silently ignoresOIDC_*env vars (no OmniAuth controllers in the codebase). Pinned at1.7.11. Bump app + sidekiq lockstep — same image tag in two containers, breaking schema migrations on minor bumps. - Image entrypoint scripts are required. Image ships
ENTRYPOINT ["bundle", "exec"]with no CMD. Compose MUST setentrypoint: ["/usr/local/bin/web-entrypoint.sh"](andsidekiq-entrypoint.shfor sidekiq). Those scripts rundb:prepare, create the SQLite files for solid_cache/solid_queue/solid_cable at/dawarich_db_data/, and migrate Postgres before exec'ing the command. - Multi-DB SQLite persistence. The solid_cache/solid_queue/solid_cable
files live at
/dawarich_db_data/— bind-mount/opt/dawarich/db_data:/dawarich_db_datato keep in-flight Sidekiq jobs across recreates. APPLICATION_HOSTSmust include localhost. Rails 8 host authorization blocks loopback if only the public host is listed — kills Docker HEALTHCHECK and the Ansible health-wait.- Rails 8
force_ssl+ healthcheck.APPLICATION_PROTOCOL=httpsmakes Rails 301-redirect every plain-HTTP request to HTTPS. The Docker healthcheck is a TCP-socket probe (ruby -rsocket -e 'TCPSocket.new("localhost",3000).close') rather than HTTP, because curl/wget either chase the redirect to TLS (Puma is plain-HTTP behind Caddy → "SSL wrong version number") or fail 2xx. The AnsibleWait for Dawarich to be healthytask isignore_errors: truefor the same reason. - SSO button is POST, not GET. The signin page renders a
<form method="post" action="/users/auth/openid_connect">; the button submits with a Rails CSRF token.GET /users/auth/openid_connectreturns 404 — that's CSRF working, not a routing bug.
Notes
- Postgres data lives at
/opt/dawarich/postgres(uid 70, alpinepostgresuser). Pre-chowned in the role to avoid the empty-dir trap. - Redis data at
/opt/dawarich/redis(uid 999) — seefeedback_redis_uid_999. - PBS LXC snapshot covers everything nightly (the postgis + db_data volumes). No central pg_backup wiring — DB is local.
- Watchtower exposes its HTTP API on port 8088 inside the LXC (matches the fleet pattern; 8080 collides with potential second app on this CT).
Description
Dawarich (self-hosted location history) — LXC 459 on pve02. Rails 8 + PostGIS + Sidekiq + Redis, Authentik OIDC.
Languages
Jinja
72.3%
Shell
27.7%