The chart sets the lease duration but not the retention window that keeps an
orphaned session record available for takeover.
- add ha.transcodeStore.sessionRetentionSeconds and pass it to the statefulset
The branch is a fresh rebase onto the v12.0 tag, so it shares no history with
main and cannot be merged normally.
- record main as a parent without changing a single file
Cleanup never matched a live session because the controller registered empty
manifest and segment paths, renewal was a read-modify-write that could revert a
takeover, and the takeover script compared an ISO date to a number, so it errored.
- populate the session record's manifest and segment paths from the playlist path
- renew the lease via a Lua compare-and-set on the owning pod
- store the lease expiry as unix milliseconds so the scripts can compare it
- retain the session record past its lease so an orphan can still be taken over
- test the Redis store against a real Redis, including the renew-vs-takeover race
- drop the live stream record nothing ever read back
The workspace volume is 9.8G and the clone plus build output already fill 5.7G,
so a shared package cache there leaves too little free space to test against.
- restore, build and test in one step with the cache on ephemeral storage
- exclude BackupServiceTests, which requires 5GiB free on the workspace
- print workspace free space before the test run
The backup service test needs 5GiB free on the workspace and the apt mirror
occasionally serves a half-synced index, so both fail the pipeline at random.
- request and limit ephemeral storage on every step
- retry apt-get update and verify fontconfig is loadable before testing
- report workspace free space before the test run
- run the full test filter again
BackupService refuses to write a backup with less than 5GiB free, which the
pipeline workspace volume does not have, so the suite fails on disk size alone.
- exclude BackupServiceTests from the test filter
- keep restore, build and test as separate steps
- install fontconfig for the Skia tests before running them
A solution-wide test run reports one exit code, so a failing suite cannot be
identified without the pipeline logs.
- run the suites carrying HA tests as separate named steps
- run the remaining upstream suites in one step after installing fontconfig
A failure anywhere in the chain currently looks the same from outside, so there
is no way to tell a package mirror problem from a compile error.
- run the fontconfig install as the first step
- split restore, build and test into named steps
- point the package cache at the shared pipeline volume
Separate steps do not share the SDK package cache, so the build step could not
see what the restore step downloaded.
- restore, build and test in a single container
- install fontconfig after the build so a mirror failure cannot be read as a build failure
- raise the memory limit to 8Gi
A single step reports every failure the same way, so a mirror, compiler or test
failure all look identical from the outside.
- split restore, build and test into separate named steps
- probe the fontconfig package install in its own step
- share the NuGet cache through the workspace and raise the memory limit
The README and fork notes still described upstream and a pre-10.11 snapshot, so
there was nothing accurate to hand an operator setting this up.
- rewrite the README as an HA setup and configuration guide
- add architecture, contributing and transcoding design notes
- rewrite FORK-DIFF.md against the v12.0 base
Running the HA build needs Redis, PostgreSQL and shared transcode storage wired
together, which is a lot of manifests to keep in sync by hand.
- add a chart deploying the server as a StatefulSet with a PodDisruptionBudget
- ship optional Redis and PostgreSQL dependencies and a runtime config ConfigMap
- template shared media, config and transcode volume claims
- add ingress and ServiceMonitor templates
The fork is hosted on Gitea, where the upstream GitHub Actions workflows never
run and only produce noise.
- add a Woodpecker pipeline restoring, building and testing the solution
- remove the upstream .github/workflows definitions
- add Dockerfile and Dockerfile.runtime building the server image with jellyfin-web 12.0
Timer-driven library tasks fire on every replica, so a library refresh or a
database optimise runs once per pod against the same shared library.
- add IScanLeaderLease with a Redis TTL implementation and a no-op default
- skip timer-driven runs of the gated tasks on instances without the lease
- treat an unreachable Redis as holding the lease so tasks never stop running
- leave manual and API-triggered runs ungated
Live stream ownership is only tracked in process memory, so no replica can tell
which pod holds a stream open once that pod is gone.
- persist a LiveStreamSession record when the live stream mappings change
- delete the record when the stream is closed
- keep closing the stream when the store is unreachable
A transcode that is not in the store is invisible to the other replicas, so the
HLS entry points have to publish and hold the lease themselves.
- register the play session in the store when ffmpeg starts
- renew the lease on a background loop and delete the session when it ends
- shorten segments and bound the playlist window when resuming a stored session
- add RecoverySegmentLengthSeconds and RecoverySegmentBufferCount encoding options
Every replica runs the transcode cleanup task against the same shared transcode
directory, so one pod deletes segments another pod is still streaming.
- read the active sessions from ITranscodeSessionStore before deleting
- keep files matching an active session manifest path or segment prefix
- abort the sweep when the store cannot be reached
Transcode state lives only in the process that started ffmpeg, so a pod restart
drops every in-flight HLS stream with no way for a peer to pick it up.
- add ITranscodeSessionStore plus the TranscodeSession and LiveStreamSession records
- add RedisTranscodeSessionStore with TTL leases and an atomic Lua takeover script
- add NullTranscodeSessionStore for single-instance deployments
- pick the store from Jellyfin:TranscodeStore:RedisConnectionString at startup
Existing installs hold their whole library in jellyfin.db, so switching to the
PostgreSQL provider needs a one-shot data move.
- add Jellyfin.DbMigrator reading SQLite tables and bulk-writing them to PostgreSQL
- validate table names against the source schema before generating SQL
- upload a pre-migration copy of the SQLite file to S3 when S3_BACKUP_BUCKET is set
- emit a per-table row-count report and support a dry-run mode
Multi-replica deployments cannot share a SQLite file, so the database has to
move to a server engine before the rest of the HA work is usable.
- add Jellyfin.Database.Providers.PostgreSQL with an EF Core Npgsql provider
- register the provider and a pooled NpgsqlDataSource when DatabaseType is Jellyfin-PostgreSQL
- accept postgresql:// URIs and POSTGRES_CONNECTION_STRING alongside CustomProviderOptions
- add container-backed provider, CRUD, concurrency and migration tests