HexOS-branded first-run setup shim for Jellyfin. Wraps the official
jellyfin/jellyfin image (the same image the TrueNAS community catalog
uses), holds startup behind a branded setup UI, then hands off to the real
Jellyfin once configuration completes.
This is a shim, not a fork — additive only. We override the entrypoint and add a thin layer of HexOS automation; the image stays maintained upstream.
The official Jellyfin image has no s6-overlay, so the boot sequence is
wrapped via entrypoint.sh:
- Already configured? If
/config/config/system.xmlhasIsStartupWizardCompleted=true(or our/config/.hexos-configuredmarker exists), exec straight into/jellyfin/jellyfin./configpersists across restarts, so configured servers take this fast path. - First run: start the Flask setup server (
setup_server.py) on:8096. It serves the branded two-screen UI plus stubs for/System/Info/Public(sentinelProductName="HexOS Setup") and/health(so the container's healthcheck passes during setup). - The user clicks Set up Jellyfin. Flask generates the admin password,
returns the credentials to the browser, and writes the signal file
/tmp/jellyfin-setup-go. - The entrypoint stops Flask, frees
:8096, spawns the configurator (jellyfin_hook.py) in the background, and execs into real Jellyfin. - The configurator polls until Jellyfin's API is up, then drives the
startup wizard: admin user → server config → libraries → hardware
transcoding → complete. It writes the
.hexos-configuredmarker and exits. - The browser polls
/System/Info/Public,/Users, and/Library/VirtualFolders; when all three pass it shows the credentials and redirects to Jellyfin's login.
- Admin user:
adminwith a randomsecrets.token_urlsafe(18)password (shown once in the browser; never logged). - Libraries: Movies (
/movies), Shows (/shows), Music (/music). Paths that aren't mounted are skipped (graceful degradation). - Transcoding: auto-detected —
qsvif/dev/dri/renderD128exists,nvencif/dev/nvidia*exists, otherwisenone.
Mount host paths to /config, /cache, and the library paths
(/movies, /shows, /music). /config must persist.
docker compose up --build
# open http://localhost:8096Reset for a clean first-run test:
docker compose down && rm -rf data/config data/cachePublished to ghcr.io/joshualmejia/jellyfintesthexos by the GitHub Actions
workflow on push to main and on tags.
static/Urbanist.woff2 and static/hexostextlogo.svg are placeholders
(marked with SWAP: comments). Drop the real HexOS assets in with the same
filenames; no code changes needed.