Quick Start
Get YotoShelf's container running from the registry image in under five minutes.
Pull and run
The recommended way to run YotoShelf is with Docker Compose. Create a
compose.yml file, replace the placeholder secrets with random strings of
at least 32 characters each, then bring it up.
The published image is multi-arch, supporting linux/amd64 and
linux/arm64 — Raspberry Pi 4/5 and other arm64 hosts pull natively.
services:
yotoshelf:
image: registry.gitlab.com/yotoshelf/yotoshelf:latest
container_name: yotoshelf
ports:
- "8080:8080"
volumes:
- yotoshelf-data:/data
environment:
YOTOSHELF_DB_PATH: /data/yotoshelf.db
YOTOSHELF_LIBRARY_PATH: /data/library
YOTOSHELF_SESSION_SECRET: <random-32-bytes>
YOTOSHELF_ENCRYPTION_KEY: <random-32-bytes>
YOTOSHELF_SECURE_COOKIES: false # plain HTTP on a LAN address only; remove this line behind HTTPS
# YOTOSHELF_PUBLIC_URL: https://yotoshelf.example.com
restart: unless-stopped
volumes:
yotoshelf-data:
docker compose up -d
The container is now running. Next, create your admin account; YotoShelf has no browser sign-up.
Or, a one-liner with docker run
If you prefer a quick one-off start without a Compose file:
docker run -d \
--name yotoshelf \
-p 8080:8080 \
-v yotoshelf-data:/data \
-e YOTOSHELF_DB_PATH=/data/yotoshelf.db \
-e YOTOSHELF_LIBRARY_PATH=/data/library \
-e YOTOSHELF_SESSION_SECRET=<random-32-bytes> \
-e YOTOSHELF_ENCRYPTION_KEY=<random-32-bytes> \
-e YOTOSHELF_SECURE_COOKIES=false \
registry.gitlab.com/yotoshelf/yotoshelf:latest
# YOTOSHELF_SECURE_COOKIES=false is for plain HTTP on a LAN address only; remove it behind HTTPS
# -e YOTOSHELF_PUBLIC_URL=https://yotoshelf.example.com \
Create your admin account
If local auth is enabled (the default), the server exposes a
create-admin subcommand; there is no browser sign-up. Run it once
before your first login:
docker exec yotoshelf /yotoshelf create-admin \
--email admin@example.com \
--password <password> \
--db-path /data/yotoshelf.db
Alternatively, configure OIDC and set
YOTOSHELF_LOCAL_AUTH_ENABLED=false; the first OIDC user to log in
is automatically promoted to admin. See Configuration.
Then open http://localhost:8080 and log in with the account you just created.
Link your Yoto account
Open the Accounts page and click Link Yoto Account to start the OAuth flow. This needs a Yoto OAuth application configured first; see the Yoto API section of Configuration for the client ID/secret and the exact redirect URL to register on dashboard.yoto.dev.
Required environment variables
The following variables have no defaults and must be set before the server will start in production:
| Variable | Purpose |
|---|---|
YOTOSHELF_SESSION_SECRET |
Signs session cookies. At least 32 bytes. Rotate to invalidate all sessions. |
YOTOSHELF_ENCRYPTION_KEY |
Encrypts Yoto OAuth tokens at rest. At least 32 bytes. Changing this invalidates stored tokens. |
See Configuration for the full variable reference, including database path, library path, SMTP, OIDC, and observability settings.
Volumes and data
YotoShelf writes two kinds of data:
- Database: a single SQLite file at
YOTOSHELF_DB_PATH(default:yotoshelf.dbin the working directory). Back this up regularly. - Library: audio files, cover images, and generated assets at
YOTOSHELF_LIBRARY_PATH(default:library/). Can be large; put it on the storage you intend to keep.
Both paths should be on a persistent volume. A single volume mount works fine; set both variables to paths inside it. The volume must be local disk (SD card or SSD), not a network share (CIFS/NFS): SQLite's file locking is unreliable over those protocols and can corrupt the database.
The image runs as uid 1000 and ships /data owned 1000:1000,
so a named volume works out of the box, but a bind mount must be chowned to
1000:1000 on the host (or the container run with --user
matching the host user).
Reverse proxy
YotoShelf listens on :8080 (configurable via YOTOSHELF_LISTEN_ADDR).
Place it behind a reverse proxy (Caddy, nginx, Traefik) and terminate TLS there.
Set YOTOSHELF_TRUST_PROXY=true so audit logs record real client IPs from
X-Forwarded-For.
Set YOTOSHELF_PUBLIC_URL to the externally visible URL (e.g.
https://yotoshelf.example.com). Unless overridden, it drives both OAuth
redirect URLs: the Yoto callback at
<PUBLIC_URL>/api/v1/yoto-accounts/callback and, if OIDC is configured,
<PUBLIC_URL>/api/v1/auth/oidc/callback. Enter the exact Yoto callback
URL as the Redirect URI on
dashboard.yoto.dev.
Leaving YOTOSHELF_PUBLIC_URL unset also disables CSRF origin checking.
Troubleshooting
exec format error / no matching manifest for linux/arm64
The image tag you pulled predates the first multi-arch release and is amd64-only.
Pull the current image to get a manifest list covering both linux/amd64
and linux/arm64.
403 … User does not have required scope
An account linked before the scope fix holds a token without resource scopes. Re-link it from the Accounts page. Ticking more permission boxes on dashboard.yoto.dev does not widen a token that was already issued; only re-linking does.
Verify the server is up
curl http://localhost:8080/healthz # → 200 OK
curl http://localhost:8080/readyz # → 200 OK (checks DB + OIDC + SMTP)