Files
vibe-kanban/deploy/readme.md
rob thijssen db061184bc deploy: bring the quadlets in from lair/containers
These describe how this repo's image is run; they belong with it rather
than in the third-party image repo. Captured from the live units on bob,
with the postgres password redacted out of the DSN.

Records the two things that are easy to get wrong and expensive to
rediscover: the db -> server -> electric ordering (the server's migrations
create the role and publication Electric attaches to, and Electric's
Requires= means a server restart stops it without bringing it back), and
the firewalld/aardvark interaction that presents as an auth failure.

Image= is a pinned immutable tag and AutoUpdate=registry is gone:
podman-auto-update.timer is enabled on bob, so a floating tag would let
the timer decide which build is running.
2026-07-21 15:54:35 +03:00

2.5 KiB

deploy

The podman quadlets for the self-hosted stack, as deployed on bob.hanzalova.internal in /etc/containers/systemd/. Fronted by nginx on hanzalova.internal as https://kanban.internal.

The image is built and published by .gitea/workflows/container.yml in this repo as git.lair.cafe/lair/vibe-kanban-remote.

Ordering is load-bearing

db -> server -> electric, and not merely cosmetically:

  • The server's sqlx migrations are what create the electric_sync role and the publication Electric attaches to, so Electric started first has nothing to attach to.
  • Notify=healthy on the server holds systemd in "starting" until /v1/health answers, so Electric genuinely waits for the migrations rather than racing them.
  • vibe-kanban-electric sets Requires=vibe-kanban.service, so restarting the server stops Electric but does not bring it back. Restart it explicitly.

Pinned images, deliberately

Image= names an immutable ${version}-g${sha} tag, not :latest, and AutoUpdate=registry is deliberately absent. podman-auto-update.timer is enabled on bob, so a floating tag would mean the running version is decided by whenever the timer last fired. Deploys here are a one-line edit plus a restart.

To roll forward: take the tag the container workflow prints, edit Image=, systemctl daemon-reload, restart the server, then restart Electric.

Secrets

/etc/vibe-kanban/env (mode 0600, root:root) holds the postgres DSN and the local-auth settings; it mirrors pass lair/vibe-kanban/*. It is not in this repo. Note the DSN embeds the password inline, so redact on the URL shape rather than on variable names when pasting any of it around.

Auth is local single-account mode (SELF_HOST_LOCAL_AUTH_*) with no OAuth provider; the server refuses to start unless at least one provider is configured. Moving to a real IdP means adding a provider, not editing these files.

Networking

vibe-kanban.network is a private bridge with dns_enabled, so the containers resolve each other by name via aardvark-dns on the bridge address. That requires the bridge to sit in firewalld's trusted zone — firewall-cmd --reload drops runtime-only zone assignments, which silently kills all DNS inside these containers and surfaces as login 500s (failed to fetch local auth user by email ... Temporary failure in name resolution). The assignment is made permanent on bob; if it is ever lost:

firewall-cmd --permanent --zone=trusted --add-interface=podman1
firewall-cmd --reload