4b69f74f40
- backup job -> dedicated root@pam!lubelogger-backup token (DatastoreBackup); prune made non-fatal pending a group-scoped PBS prune-job (Datastore.Prune deferred). - Homepage PBS widget -> homepage@pbs!widget (Datastore.Audit); Proxmox Beast (root@pam!homepage) left intact, deferred. Secrets in vault/.env only. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
211 lines
7.5 KiB
YAML
211 lines
7.5 KiB
YAML
---
|
|
- name: Deploy LubeLogger on docker-node01
|
|
hosts: docker-node01
|
|
become: true
|
|
vars:
|
|
app_dir: /home/tommy/lubelogger
|
|
app_port: 8099
|
|
|
|
handlers:
|
|
- name: reload systemd
|
|
ansible.builtin.systemd_service:
|
|
daemon_reload: true
|
|
|
|
tasks:
|
|
- name: Create lubelogger directory
|
|
ansible.builtin.file:
|
|
path: "{{ app_dir }}"
|
|
state: directory
|
|
owner: tommy
|
|
group: tommy
|
|
mode: "0755"
|
|
|
|
- name: Write docker-compose.yml
|
|
ansible.builtin.copy:
|
|
dest: "{{ app_dir }}/docker-compose.yml"
|
|
owner: tommy
|
|
group: tommy
|
|
mode: "0644"
|
|
content: |
|
|
# lubelogger - Vehicle & small-engine maintenance tracker
|
|
# Host: docker-node01 (.186), Port: {{ app_port }}
|
|
# Traefik: lubelogger.goattw.net (Authelia protected, @file router -> :{{ app_port }})
|
|
# Backend: PostgreSQL 18 (tables live in the `app` schema).
|
|
# In-app auth is ENABLED (root user) - the published host port is safe.
|
|
# Do NOT lose the lubelogger-keys volume (ASP.NET DataProtection keyring).
|
|
|
|
services:
|
|
lubelogger:
|
|
image: ghcr.io/hargata/lubelogger:v1.6.8
|
|
container_name: lubelogger
|
|
restart: unless-stopped
|
|
depends_on:
|
|
- lubelogger-db
|
|
ports:
|
|
- "{{ app_port }}:8080"
|
|
environment:
|
|
TZ: America/Chicago
|
|
LUBELOGGER_LOCALE_OVERRIDE: "en_US"
|
|
POSTGRES_CONNECTION: "Host=lubelogger-db:5432;Username=lubelogger;Password={{ vault_lubelogger_db_password }};Database=lubelogger;"
|
|
volumes:
|
|
- lubelogger-data:/App/data
|
|
- lubelogger-keys:/root/.aspnet/DataProtection-Keys
|
|
networks:
|
|
- lubelogger-int
|
|
labels:
|
|
- "docker-volume-backup.stop-during-backup=true"
|
|
# Diun watches by default (digest); watch_repo + semver filter
|
|
# so a NEW release tag (e.g. v1.6.9) pings the existing Diun->HA pipeline.
|
|
- "diun.enable=true"
|
|
- "diun.watch_repo=true"
|
|
- 'diun.include_tags=^v[0-9]+\.[0-9]+\.[0-9]+$'
|
|
|
|
lubelogger-db:
|
|
image: postgres:18
|
|
container_name: lubelogger-db
|
|
restart: unless-stopped
|
|
environment:
|
|
POSTGRES_USER: lubelogger
|
|
POSTGRES_PASSWORD: "{{ vault_lubelogger_db_password }}"
|
|
POSTGRES_DB: lubelogger
|
|
TZ: America/Chicago
|
|
volumes:
|
|
- lubelogger-db:/var/lib/postgresql
|
|
- /etc/localtime:/etc/localtime:ro
|
|
networks:
|
|
- lubelogger-int
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "pg_isready -U lubelogger -d lubelogger"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 5
|
|
start_period: 30s
|
|
labels:
|
|
- "docker-volume-backup.stop-during-backup=true"
|
|
|
|
networks:
|
|
lubelogger-int:
|
|
driver: bridge
|
|
|
|
volumes:
|
|
lubelogger-data:
|
|
lubelogger-keys:
|
|
lubelogger-db:
|
|
|
|
- name: Remove stale .env (secret now sourced from ansible-vault)
|
|
ansible.builtin.file:
|
|
path: "{{ app_dir }}/.env"
|
|
state: absent
|
|
|
|
- name: Deploy lubelogger with docker compose
|
|
community.docker.docker_compose_v2:
|
|
project_src: "{{ app_dir }}"
|
|
state: present
|
|
pull: missing
|
|
register: app_result
|
|
|
|
- name: Show deployment result
|
|
ansible.builtin.debug:
|
|
msg: "lubelogger is {{ 'running (changed)' if app_result.changed else 'already up (no-op)' }} on port {{ app_port }}"
|
|
|
|
# ---- Backup: pg_dump + data/keys volumes -> PBS (host/lubelogger, keep-daily 14) ----
|
|
# docker-volume-backup sidecar cannot target a PBS datastore natively, so the two
|
|
# volumes ride the same proxmox-backup-client job as the SQL dump: one snapshot,
|
|
# three archives, one retention. Secrets come from ansible-vault, never committed.
|
|
|
|
- name: Install PBS backup credentials (from vault)
|
|
ansible.builtin.copy:
|
|
dest: /etc/lubelogger-backup.env
|
|
owner: root
|
|
group: root
|
|
mode: "0600"
|
|
content: |
|
|
# Managed by deploy-lubelogger.yml. PBS push creds — NOT in git.
|
|
PBS_PASSWORD={{ vault_lubelogger_pbs_token }}
|
|
PBS_FINGERPRINT=90:6B:3E:85:C4:E7:77:F0:24:25:1D:B0:2C:F9:6D:08:AA:CE:08:CE:7B:04:4C:C7:C9:E0:CF:DD:99:44:DB:21
|
|
PBS_REPOSITORY='root@pam!lubelogger-backup@192.168.99.153:rust-usb'
|
|
no_log: true
|
|
|
|
- name: Install lubelogger backup script
|
|
ansible.builtin.copy:
|
|
dest: /usr/local/bin/lubelogger-backup.sh
|
|
owner: root
|
|
group: root
|
|
mode: "0700"
|
|
content: |
|
|
#!/bin/bash
|
|
# LubeLogger backup -> Proxmox Backup Server.
|
|
# One snapshot (host/lubelogger), three archives: Postgres logical dump +
|
|
# the data and keys volumes (uploads, config, ASP.NET DataProtection keyring).
|
|
# Managed by deploy-lubelogger.yml; driven by lubelogger-backup.timer.
|
|
set -euo pipefail
|
|
source /etc/lubelogger-backup.env
|
|
export PBS_PASSWORD PBS_FINGERPRINT
|
|
|
|
DATA_VOL=/var/lib/docker/volumes/lubelogger_lubelogger-data/_data
|
|
KEYS_VOL=/var/lib/docker/volumes/lubelogger_lubelogger-keys/_data
|
|
|
|
TMPDIR=$(mktemp -d)
|
|
trap 'rm -rf "$TMPDIR"' EXIT
|
|
|
|
# Logical DB dump (app schema + data); app stays online.
|
|
docker exec lubelogger-db pg_dump -U lubelogger lubelogger > "$TMPDIR/lubelogger.sql"
|
|
|
|
proxmox-backup-client backup \
|
|
"lubelogger-db.pxar:$TMPDIR" \
|
|
"lubelogger-data.pxar:$DATA_VOL" \
|
|
"lubelogger-keys.pxar:$KEYS_VOL" \
|
|
--repository "$PBS_REPOSITORY" \
|
|
--backup-type host \
|
|
--backup-id lubelogger
|
|
|
|
# Retention: token holds Datastore.Backup only (Prune deferred to a PBS
|
|
# group-scoped prune-job). Keep this non-fatal so nightly backups still
|
|
# succeed; it starts pruning automatically if Datastore.Prune is granted.
|
|
proxmox-backup-client prune "host/lubelogger" \
|
|
--repository "$PBS_REPOSITORY" \
|
|
--keep-daily 14 \
|
|
|| echo "NOTE: prune skipped (Datastore.Prune not granted; retention via deferred PBS prune-job)"
|
|
notify: reload systemd
|
|
|
|
- name: Install backup systemd service unit
|
|
ansible.builtin.copy:
|
|
dest: /etc/systemd/system/lubelogger-backup.service
|
|
owner: root
|
|
group: root
|
|
mode: "0644"
|
|
content: |
|
|
[Unit]
|
|
Description=LubeLogger backup (pg_dump + volumes) -> PBS
|
|
After=docker.service
|
|
Requires=docker.service
|
|
|
|
[Service]
|
|
Type=oneshot
|
|
ExecStart=/usr/local/bin/lubelogger-backup.sh
|
|
notify: reload systemd
|
|
|
|
- name: Install backup systemd timer unit
|
|
ansible.builtin.copy:
|
|
dest: /etc/systemd/system/lubelogger-backup.timer
|
|
owner: root
|
|
group: root
|
|
mode: "0644"
|
|
content: |
|
|
[Unit]
|
|
Description=Daily LubeLogger backup to PBS
|
|
|
|
[Timer]
|
|
OnCalendar=*-*-* 03:30:00
|
|
Persistent=true
|
|
|
|
[Install]
|
|
WantedBy=timers.target
|
|
notify: reload systemd
|
|
|
|
- name: Enable lubelogger backup timer
|
|
ansible.builtin.systemd_service:
|
|
name: lubelogger-backup.timer
|
|
enabled: true
|
|
state: started
|