Skip to content

Installation

Xique ships as a single container image deployed with rootless podman and systemd quadlets. One pod carries the web process, the worker, and a Tailscale sidecar; the app is served to your tailnet over automatic HTTPS and nothing is exposed beyond it.

Prerequisites

  • Rootless podman with systemd quadlet support (units live under ~/.config/containers/systemd/).
  • A Tailscale account; the sidecar joins your tailnet as its own node.
  • Registry access to registry.marvin8.zone/marvin8/xique.

Host layout

Create the directories the units mount:

mkdir -p ~/docker/xique/data/db ~/docker/xique/ts-state ~/docker/xique/ts-serve
  • data/db/ — the sqlite database. This is the only content that needs backing up.
  • ts-state/ — Tailscale node state, so the sidecar keeps its identity across restarts.
  • ts-serve/ — the Tailscale serve config; copy the shipped one: sh cp deploy/xique-serve.json ~/docker/xique/ts-serve/ Adjust the ${TS_CERT_DOMAIN} proxy target only if you change ports.

Environment file

~/docker/xique/xique.env is read by both xique containers. At minimum:

XIQUE_SECRET_KEY=<long random string>   # required
XIQUE_IMPORT_DIR=/data/imports-temp     # shared upload volume

XIQUE_SECRET_KEY signs sessions and encrypts stored Jellyfin tokens. If you lose it, users simply reconnect Jellyfin — but generate it properly: openssl rand -base64 32. Add XIQUE_TMDB_TOKEN (a TMDB read access token) to enable recommendations; without it the worker cannot talk to TMDB.

The Tailscale sidecar also expects a shared ~/docker/tailscale-systemd.env pinning TAILSCALE_VERSION for its image.

Bring-up

  1. Copy the five quadlet units into the systemd directory: sh cp deploy/xique.pod deploy/xique-web.container deploy/xique-worker.container \ deploy/xique-tailscale.container deploy/xique-imports.volume \ ~/.config/containers/systemd/
  2. Reload and start: sh systemctl --user daemon-reload systemctl --user start xique-pod
  3. First boot only: the sidecar prints a Tailscale login URL. Extract and open it to approve the node: sh journalctl --user -u xique-tailscale
  4. Open https://xique.<your-tailnet>.ts.net. The first page is /setup — create the admin account. /setup is refused once any user exists; further users join through admin-issued invite links.

What first boot looks like

All containers in the pod start simultaneously. On a virgin database the web process finds no schema, refuses to serve, and restarts until the worker has run its migrations — this settles in seconds and is expected behaviour, not a crash loop. Once the schema exists, web verifies it on every start and only serves when it is current.