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/Keep the filename as-is: the sidecar reads exactly/config/xique-serve.json(TS_SERVE_CONFIGin the unit), so a renamed copy is silently ignored. 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
- 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/ - Reload and start:
sh systemctl --user daemon-reload systemctl --user start xique-pod - First boot only: the sidecar prints a Tailscale login URL. Extract and
open it to approve the node:
sh journalctl --user -u xique-tailscale - Open
https://xique.<your-tailnet>.ts.net. The first page is/setup— create the admin account./setupis 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.