Files
oikos/plans/done/2026-06-29-grimmory-migration.md
dtoro fd35b48c8d Phase 1-4: full doc reorg
Phase 1 — fix stale state after strong migration (Phase 1+2, 2026-07-05)
  - README: corrected IPs (jellyfin 206→246, arriman 132→245, etc.),
    added missing containers (128 trmnl, 129 house, 133 seanime, 134 romm,
    124 authentik), updated last-refreshed date, added strong host context
  - containers/101-jellyfin.md: IP 206→246, host hubris→strong, mount
    /mnt/library→/mnt/media_local, GPU 760M→680M+RX7600, privilege→priv
  - containers/118-elementsynapse.md: IP 239→242, added Host: strong
  - containers/122-arriman.md: IP 132→245, mount→/mnt/media_local, added Host
  - containers/129-house.md: IP 212→244, added Host: strong
  - containers/130-grimmory.md: IP 213→247, mount→/mnt/media_local, added Host
  - containers/121-caddy.md: fixed site list (books→grimmory, removed auth→VPS,
    added house, roms, teddy, trmnl)
  - hosts/strong.md: updated At-a-glance to reflect 7 LXCs hosted
  - containers/123-claudio-bot.md, 127-mule-photos-new.md: archived to
    containers/archive/ (were destroyed LXCs with living pages)
  - inventory.yaml: verified correct — no changes needed

Phase 2 — structural cleanup
  - infrastructure/index.md: one-page overview of all cross-cutting systems
  - runbooks/: moved runbook-budget-from-csv.md and runbook-dpkg-interrupted.md
    from operations/ with YAML frontmatter added
  - plans/done/: moved 4 completed plans out of active view; updated index
  - vms/index.md: added VM index page

Phase 3 — navigation & discoverability
  - GLOSSARY.md: term definitions (Authentik, Caddy, LXC, VAAPI, etc.)
  - README: added table of contents, links to glossary + infrastructure index
  - investigations/: archived 2 resolved cases (crash-loop, authentik-migration)
    to investigations/archive/; updated index with active vs archived sections

Phase 4 — ongoing discipline
  - CONTRIBUTING.md: documented same-session update rule with explicit checklist
  - README: replaced full LXC table with summary + link to containers/index.md
    (single source of truth; de-duplication)
2026-07-06 00:46:27 +02:00

247 lines
6.4 KiB
Markdown

# Plan: Migrate Booklore → Grimmory (LXC 130)
**Status:** in-progress
**Date:** 2026-06-29
**Goal:** Replace Booklore on shared apps LXC 105 with Grimmory on a dedicated LXC 130. Grimmory is the community fork/successor of Booklore with the same database schema and port, so the migration is a near-drop-in swap.
---
## Pre-flight checklist
- [ ] Note Booklore MariaDB credentials from Portainer compose on LXC 105 (`DATABASE_PASSWORD`, `MYSQL_ROOT_PASSWORD`)
- [ ] Confirm `/mnt/library/books` is readable on LXC 105 (`ls /mnt/library/books | head`)
---
## Step 1 — Dump Booklore MariaDB
```bash
# On hubris — find the MariaDB container name
pct exec 105 -- docker ps --format '{{.Names}}' | grep -i maria
# Dump (replace <CONTAINER> and <PASSWORD> from Portainer compose)
pct exec 105 -- docker exec <CONTAINER> \
mysqldump -u grimmory -p<PASSWORD> grimmory \
> /tmp/booklore-$(date +%Y%m%d).sql
# Pull to hubris root for safekeeping
pct pull 105 /tmp/booklore-$(date +%Y%m%d).sql /root/
```
---
## Step 2 — Create LXC 130
```bash
# On hubris — list available Debian 13 templates
pveam list local | grep debian-13
# Create LXC
pct create 130 local:vztmpl/debian-13-standard_13.0-1_amd64.tar.zst \
--hostname grimmory \
--ostype debian \
--unprivileged 0 \
--cores 1 --memory 2048 --rootfs local-lvm:16 \
--net0 name=eth0,bridge=vmbr0,ip=dhcp \
--onboot 1 \
--mp0 /mnt/library,mp=/mnt/library \
--features nesting=1
pct start 130
pct exec 130 -- apt-get update -qq
```
Set the static IP directly in PVE (same pattern as all other LXCs — no Fritz!Box reservation needed):
```bash
pct set 130 --net0 name=eth0,bridge=vmbr0,ip=192.168.8.213/24,gw=192.168.8.1
pct reboot 130
```
---
## Step 3 — Bootstrap LXC 130
```bash
pct exec 130 -- bash -c '
# Media group
groupadd -g 10000 media
# Docker
apt-get install -y ca-certificates curl
curl -fsSL https://get.docker.com | sh
systemctl enable --now docker
'
```
---
## Step 4 — Deploy Grimmory compose
```bash
pct exec 130 -- mkdir -p /opt/grimmory/mariadb/config /opt/grimmory/data /opt/grimmory/bookdrop
```
Write `/opt/grimmory/docker-compose.yml` on LXC 130:
```yaml
services:
grimmory:
image: ghcr.io/grimmory-tools/grimmory:latest
container_name: grimmory
restart: unless-stopped
ports:
- "192.168.8.213:6060:6060"
volumes:
- ./data:/app/data
- /mnt/library/books:/books
- ./bookdrop:/bookdrop
environment:
- DATABASE_URL=jdbc:mariadb://mariadb:3306/grimmory
- DATABASE_USERNAME=grimmory
- DATABASE_PASSWORD=${GRIMMORY_DB_PASSWORD}
- USER_ID=0
- GROUP_ID=10000
- TZ=Europe/Berlin
- FORCE_DISABLE_OIDC=false
extra_hosts:
- "auth.hubris.network:192.168.8.175"
depends_on:
mariadb:
condition: service_healthy
mariadb:
image: lscr.io/linuxserver/mariadb:11.4.8
container_name: grimmory-mariadb
restart: unless-stopped
volumes:
- ./mariadb/config:/config
environment:
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
- MYSQL_DATABASE=grimmory
- MYSQL_USER=grimmory
- MYSQL_PASSWORD=${GRIMMORY_DB_PASSWORD}
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
```
Write `/opt/grimmory/.env` on LXC 130 (fill real passwords):
```
GRIMMORY_DB_PASSWORD=<same_password_as_booklore>
MYSQL_ROOT_PASSWORD=<root_password>
```
Start:
```bash
pct exec 130 -- bash -c 'cd /opt/grimmory && docker compose up -d mariadb'
# wait ~15s for MariaDB to init, then start grimmory
pct exec 130 -- bash -c 'cd /opt/grimmory && docker compose up -d'
```
Verify Grimmory responds (before DB restore — will show setup wizard):
```bash
curl -s -o /dev/null -w '%{http_code}' http://192.168.8.213:6060
# expect 200 or 302
```
---
## Step 5 — Restore Booklore DB
```bash
# Stop Grimmory (keep MariaDB running)
pct exec 130 -- docker stop grimmory
# Copy dump to LXC 130
pct push 130 /root/booklore-$(date +%Y%m%d).sql /tmp/booklore.sql
# Restore (replace <PASSWORD>)
pct exec 130 -- docker exec -i grimmory-mariadb \
mysql -u grimmory -p<GRIMMORY_DB_PASSWORD> grimmory \
< /tmp/booklore.sql
# Restart Grimmory
pct exec 130 -- docker start grimmory
```
Verify books appear:
```bash
curl -s http://192.168.8.213:6060 | grep -i grimmory
```
---
## Step 6 — Authentik OIDC update
In Authentik Admin UI (`https://auth.hubris.network`):
1. Providers → find `Booklore` provider
2. Edit:
- Name: `Grimmory`
- Client Type: **Public** (Grimmory uses PKCE — no secret needed)
- Redirect URIs: `https://books.hubris.network/oauth2-callback`
- Scopes: openid, profile, email, offline_access
- Back-channel logout URL: `http://192.168.8.213:6060/api/v1/auth/oidc/backchannel-logout`
3. Note the **Client ID** and **Application slug** for Grimmory's OIDC settings
In Grimmory Admin UI (`http://192.168.8.213:6060` → Settings → Authentication → OIDC):
- Issuer URI: `https://auth.hubris.network/application/o/<slug>/` (trailing slash required!)
- Client ID: (from Authentik)
- Client Secret: leave blank (PKCE)
- Click **Test Connection** — all checks should pass (container reaches Authentik via extra_hosts)
---
## Step 7 — Caddy cutover
In the `dtoro/caddy-conf` repo, update `books.hubris.network`:
```caddy
books.hubris.network {
reverse_proxy 192.168.8.213:6060
}
```
Git push → Caddy webhook auto-reloads (see [caddy (121)](../containers/121-caddy.md)).
Test:
```bash
curl -s -o /dev/null -w '%{http_code}\n' https://books.hubris.network
```
---
## Step 8 — Enroll as homelab client
```bash
homelab client add grimmory --lan-ip 192.168.8.213
```
Commits the `age_pubkey` back to `inventory.yaml`.
---
## Step 9 — Verify end-to-end
- [ ] `https://books.hubris.network` loads Grimmory
- [ ] OIDC login via Authentik works
- [ ] Library books from `/mnt/library/books` are visible
- [ ] Reading progress / metadata from Booklore is present
---
## Step 10 — Decommission Booklore on LXC 105
1. Portainer → navigate to the Booklore stack → Stop → Remove
2. Keep the dump at `/root/booklore-<date>.sql` on hubris (or archive to `/mnt/library/documents/`)
---
## Rollback
If something goes wrong before Caddy cutover: no user-visible impact, just shut down LXC 130.
If Caddy already cut over: revert the `books.hubris.network` block to `192.168.8.205:6060` and push. Booklore still running on LXC 105 until Portainer stack is removed.