feat(vikunja): bring PIHA Vikunja under GitOps (postgres + Forgejo OIDC)
Wraps the existing manually-run Vikunja instance on PIHA in the standard service layout. No deploy — definition only, for review. - services/vikunja/: docker-compose.yml (vikunja + postgres:16-alpine), service.yaml, README, healthcheck, config.yml (OIDC, secret-free), .env.example. Real .env stays gitignored. - Pins EXISTING named volumes (vikunja_vikunja_db, vikunja_vikunja_files) so DB + uploaded files survive cutover. - extra_hosts forgejo.okit.pl=192.168.31.5 (npm on PIHA) so OIDC discovery resolves over LAN instead of flaky public DNS. - OIDC client secret injected via env (VIKUNJA_AUTH_OPENID_PROVIDERS_ FORGEJO_CLIENTSECRET); config.yml keeps trailing-slash authurl/redirecturl. - Registers vikunja in hosts/piha/services.yaml + inventory/topology.yaml, plus hosts/piha/runtime/vikunja override (advisory mem_limits). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
dd64b9c878
commit
f7e1391f30
11
hosts/piha/runtime/vikunja/docker-compose.override.yml
Normal file
11
hosts/piha/runtime/vikunja/docker-compose.override.yml
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
# PIHA-specific overrides for Vikunja.
|
||||
#
|
||||
# PIHA is not as RAM-constrained as the VPS, so these limits are advisory
|
||||
# guard-rails (a leaking process gets restarted by Docker) rather than a hard
|
||||
# budget. Adjust if Vikunja's working set grows.
|
||||
services:
|
||||
vikunja:
|
||||
mem_limit: 768m
|
||||
|
||||
db:
|
||||
mem_limit: 256m
|
||||
|
|
@ -40,3 +40,20 @@ services:
|
|||
external: [control-plane]
|
||||
runtime:
|
||||
config_path: /opt/homelab/config/brain-watchdog
|
||||
|
||||
vikunja:
|
||||
role: task-management
|
||||
deployment_model: docker-compose
|
||||
exposure: private # public via npm reverse proxy at vikunja.okit.pl
|
||||
offline_required: false
|
||||
depends_on:
|
||||
local: []
|
||||
external: [forgejo] # OIDC identity provider
|
||||
ports:
|
||||
- name: http
|
||||
container_port: 3456
|
||||
protocol: tcp
|
||||
runtime:
|
||||
# secrets (.env) + OIDC config (config.yml) live alongside the compose file
|
||||
config_path: services/vikunja
|
||||
# data is in Docker named volumes: vikunja_vikunja_db, vikunja_vikunja_files
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ nodes:
|
|||
- node-agent
|
||||
- ha-diag-agent
|
||||
- brain-watchdog
|
||||
- vikunja # Task management (vikunja + postgres), public via npm
|
||||
|
||||
solaria:
|
||||
roles:
|
||||
|
|
|
|||
17
services/vikunja/.env.example
Normal file
17
services/vikunja/.env.example
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
# Vikunja secrets — copy to .env and fill with the REAL live values.
|
||||
# .env is gitignored; never commit it.
|
||||
#
|
||||
# These three values MUST match the running instance or you lose access /
|
||||
# break login at cutover. Do not regenerate them.
|
||||
|
||||
# Postgres superuser password for the vikunja DB.
|
||||
POSTGRES_PASSWORD=change-me-very-strong
|
||||
|
||||
# Must be identical to POSTGRES_PASSWORD — this is how Vikunja connects to it.
|
||||
VIKUNJA_DATABASE_PASSWORD=change-me-very-strong
|
||||
|
||||
# JWT signing secret. Changing it invalidates all existing sessions/tokens.
|
||||
VIKUNJA_SERVICE_JWTSECRET=change-me-super-long-random-string
|
||||
|
||||
# OIDC client secret for the Forgejo provider (config.yml stays secret-free).
|
||||
VIKUNJA_AUTH_OPENID_PROVIDERS_FORGEJO_CLIENTSECRET=change-me-forgejo-oidc-client-secret
|
||||
44
services/vikunja/README.md
Normal file
44
services/vikunja/README.md
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
# Vikunja
|
||||
|
||||
Self-hosted task management. Runs on the **PIHA** node with a dedicated
|
||||
PostgreSQL backend, exposed publicly at `https://vikunja.okit.pl` via the npm
|
||||
reverse proxy on PIHA. Login is via Forgejo OIDC.
|
||||
|
||||
## Stack
|
||||
|
||||
| Container | Image | Purpose |
|
||||
|-------------|------------------------|----------------------------------|
|
||||
| `vikunja` | `vikunja/vikunja:latest` | App + API (port 3456) |
|
||||
| `vikunja-db`| `postgres:16-alpine` | PostgreSQL (service alias `db`) |
|
||||
|
||||
## Data (do not move)
|
||||
|
||||
Persistent state lives in pre-existing Docker named volumes — these are pinned
|
||||
by name so the live data survives cutover:
|
||||
|
||||
- `vikunja_vikunja_db` → `/var/lib/postgresql/data`
|
||||
- `vikunja_vikunja_files` → `/app/vikunja/files`
|
||||
|
||||
## Configuration
|
||||
|
||||
- `config.yml` — committed, **secret-free**. Holds OIDC provider metadata.
|
||||
- `.env` — **gitignored**. Copy from `.env.example` and fill with the real
|
||||
live values (must be identical to the running instance):
|
||||
- `POSTGRES_PASSWORD` / `VIKUNJA_DATABASE_PASSWORD` (same value)
|
||||
- `VIKUNJA_SERVICE_JWTSECRET`
|
||||
- `VIKUNJA_AUTH_OPENID_PROVIDERS_FORGEJO_CLIENTSECRET`
|
||||
|
||||
## Notes
|
||||
|
||||
- `forgejo.okit.pl` is pinned via `extra_hosts` to `192.168.31.5` (npm on PIHA)
|
||||
so OIDC discovery resolves over the LAN, avoiding flaky public DNS.
|
||||
- Both `redirecturl` and the provider `authurl` MUST keep their trailing slash —
|
||||
the `authurl` must match the OIDC issuer exactly or discovery fails.
|
||||
|
||||
## Cutover checklist
|
||||
|
||||
1. `git pull` on PIHA.
|
||||
2. Create `services/vikunja/.env` from `.env.example` with the real values.
|
||||
3. Confirm the named volumes exist: `docker volume ls | grep vikunja_vikunja`.
|
||||
4. `docker compose -f services/vikunja/docker-compose.yml up -d`.
|
||||
5. Verify: `./healthcheck.sh` and a test OIDC login.
|
||||
23
services/vikunja/config.yml
Normal file
23
services/vikunja/config.yml
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# Vikunja runtime config — committed to git (secret-free).
|
||||
#
|
||||
# Database, public URL, JWT secret and the OIDC client SECRET are supplied via
|
||||
# environment variables (see docker-compose.yml + .env). Only non-secret OIDC
|
||||
# provider metadata lives here.
|
||||
#
|
||||
# Env vars override matching keys, so anything secret stays out of this file.
|
||||
|
||||
auth:
|
||||
openid:
|
||||
enabled: true
|
||||
# MUST end with a trailing slash — matches Vikunja's redirect handling.
|
||||
redirecturl: "https://vikunja.okit.pl/auth/openid/"
|
||||
providers:
|
||||
forgejo:
|
||||
name: "Forgejo"
|
||||
# authurl MUST have a trailing slash — it must match the OIDC issuer
|
||||
# exactly or discovery fails.
|
||||
authurl: "https://forgejo.okit.pl/"
|
||||
clientid: "15ad6f99-b8ca-48dc-92b9-97907ddc4367"
|
||||
# clientsecret is injected via
|
||||
# VIKUNJA_AUTH_OPENID_PROVIDERS_FORGEJO_CLIENTSECRET (.env) — never here.
|
||||
scope: "openid profile email"
|
||||
62
services/vikunja/docker-compose.yml
Normal file
62
services/vikunja/docker-compose.yml
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
services:
|
||||
vikunja:
|
||||
image: vikunja/vikunja:latest
|
||||
container_name: vikunja
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
# forgejo.okit.pl is pinned to the npm reverse proxy on PIHA so OIDC
|
||||
# discovery resolves over the LAN instead of flaky public DNS, which was
|
||||
# causing OIDC discovery to fail.
|
||||
extra_hosts:
|
||||
- "forgejo.okit.pl:192.168.31.5"
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
- VIKUNJA_DATABASE_TYPE=postgres
|
||||
- VIKUNJA_DATABASE_HOST=db
|
||||
- VIKUNJA_DATABASE_PORT=5432
|
||||
- VIKUNJA_DATABASE_USER=vikunja
|
||||
- VIKUNJA_DATABASE_DATABASE=vikunja
|
||||
- VIKUNJA_DATABASE_PASSWORD=${VIKUNJA_DATABASE_PASSWORD:?set in .env}
|
||||
- VIKUNJA_SERVICE_PUBLICURL=https://vikunja.okit.pl
|
||||
- VIKUNJA_SERVICE_JWTSECRET=${VIKUNJA_SERVICE_JWTSECRET:?set in .env}
|
||||
# OIDC client secret injected from .env so config.yml stays secret-free.
|
||||
- VIKUNJA_AUTH_OPENID_PROVIDERS_FORGEJO_CLIENTSECRET=${VIKUNJA_AUTH_OPENID_PROVIDERS_FORGEJO_CLIENTSECRET:?set in .env}
|
||||
- TZ=Europe/Warsaw
|
||||
volumes:
|
||||
- vikunja_files:/app/vikunja/files
|
||||
- ./config.yml:/etc/vikunja/config.yml:ro
|
||||
ports:
|
||||
- "3456:3456"
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "-qO-", "http://localhost:3456/api/v1/info"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
|
||||
db:
|
||||
image: postgres:16-alpine
|
||||
container_name: vikunja-db
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- POSTGRES_USER=vikunja
|
||||
- POSTGRES_DB=vikunja
|
||||
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:?set in .env}
|
||||
- TZ=Europe/Warsaw
|
||||
volumes:
|
||||
- vikunja_db:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U vikunja -d vikunja"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
# Pinned to the EXISTING named volumes so the live database and uploaded files
|
||||
# survive cutover. Do not rename these — the data lives here.
|
||||
volumes:
|
||||
vikunja_db:
|
||||
name: vikunja_vikunja_db
|
||||
vikunja_files:
|
||||
name: vikunja_vikunja_files
|
||||
23
services/vikunja/healthcheck.sh
Executable file
23
services/vikunja/healthcheck.sh
Executable file
|
|
@ -0,0 +1,23 @@
|
|||
#!/bin/bash
|
||||
# Healthcheck for Vikunja (app + database)
|
||||
|
||||
# Vikunja app container must be running
|
||||
if ! docker ps --filter "name=vikunja" --filter "status=running" | grep -qw "vikunja"; then
|
||||
echo "[FAIL] vikunja container is not running"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Postgres container must be running
|
||||
if ! docker ps --filter "name=vikunja-db" --filter "status=running" | grep -qw "vikunja-db"; then
|
||||
echo "[FAIL] vikunja-db container is not running"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# API must respond
|
||||
if ! curl -sf http://localhost:3456/api/v1/info > /dev/null; then
|
||||
echo "[FAIL] Vikunja API is not responding on :3456"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "[OK] Vikunja is healthy"
|
||||
exit 0
|
||||
31
services/vikunja/service.yaml
Normal file
31
services/vikunja/service.yaml
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
service:
|
||||
name: vikunja
|
||||
owner_node: piha
|
||||
exposure: private # reachable on Tailscale; public via npm reverse proxy
|
||||
dependencies:
|
||||
- forgejo # OIDC identity provider (forgejo.okit.pl)
|
||||
ports:
|
||||
- container: 3456
|
||||
host: 3456
|
||||
protocol: tcp
|
||||
healthcheck:
|
||||
type: http
|
||||
endpoint: http://localhost:3456/api/v1/info
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
restart_policy: unless-stopped
|
||||
persistence:
|
||||
# Data lives in pre-existing Docker named volumes — do NOT move/rename.
|
||||
paths:
|
||||
- vikunja_vikunja_db # postgres data -> /var/lib/postgresql/data
|
||||
- vikunja_vikunja_files # uploads -> /app/vikunja/files
|
||||
runtime:
|
||||
config_files:
|
||||
- config.yml # OIDC provider metadata (secret-free, in git)
|
||||
- .env # secrets (gitignored, supplied per node)
|
||||
env_vars:
|
||||
- POSTGRES_PASSWORD
|
||||
- VIKUNJA_DATABASE_PASSWORD
|
||||
- VIKUNJA_SERVICE_JWTSECRET
|
||||
- VIKUNJA_AUTH_OPENID_PROVIDERS_FORGEJO_CLIENTSECRET
|
||||
Loading…
Reference in a new issue