Deploy native Jellyfin service on Lyra #190

Merged
nimmo merged 3 commits from feat/issue-30-native-jellyfin-prep into main 2026-08-24 21:52:17 +01:00
Owner

Summary

  • replace the stopped LSIO Docker Jellyfin container with the native NixOS Jellyfin service
  • preserve UI-managed Jellyfin state in the conventional /var/lib/jellyfin and /var/cache/jellyfin locations
  • provide RTX 4070 CUDA/NVENC access through the native systemd service
  • add a reusable read-only media-library NFS module for /mnt/media and future host migrations
  • remove the completed one-time state importer from the final configuration

Validation

  • nix flake check passes
  • evaluated Lyra configuration retains jellyfinRequires = [ "mnt-media.mount" ]
  • evaluated media mount remains 192.168.8.238:/volume1/media at /mnt/media, read-only over NFS 4.1
  • final just test completed on Lyra
  • Jellyfin restarted after the final activation and reported startup complete in 8.9 seconds

Live deployment evidence

This native replacement has already been deployed and tested on Lyra, replacing the stopped Docker container. Library access and real video playback are confirmed working. During the playback test Jellyfin selected CUDA hardware decoding, scale_cuda, and RTX 4070 hevc_nvenc encoding, then continued reporting an active playback session without CUDA, NVENC, media-access, or FFmpeg errors.

The stopped pre-cutover snapshot remains available for the rollback window; this MR does not delete the imported state or source snapshot.

Closes #30

## Summary - replace the stopped LSIO Docker Jellyfin container with the native NixOS Jellyfin service - preserve UI-managed Jellyfin state in the conventional `/var/lib/jellyfin` and `/var/cache/jellyfin` locations - provide RTX 4070 CUDA/NVENC access through the native systemd service - add a reusable read-only `media-library` NFS module for `/mnt/media` and future host migrations - remove the completed one-time state importer from the final configuration ## Validation - `nix flake check` passes - evaluated Lyra configuration retains `jellyfinRequires = [ "mnt-media.mount" ]` - evaluated media mount remains `192.168.8.238:/volume1/media` at `/mnt/media`, read-only over NFS 4.1 - final `just test` completed on Lyra - Jellyfin restarted after the final activation and reported startup complete in 8.9 seconds ## Live deployment evidence This native replacement has already been deployed and tested on Lyra, replacing the stopped Docker container. Library access and real video playback are confirmed working. During the playback test Jellyfin selected CUDA hardware decoding, `scale_cuda`, and RTX 4070 `hevc_nvenc` encoding, then continued reporting an active playback session without CUDA, NVENC, media-access, or FFmpeg errors. The stopped pre-cutover snapshot remains available for the rollback window; this MR does not delete the imported state or source snapshot. Closes #30
feat(lyra): deploy native Jellyfin service
All checks were successful
AI pull-request review / review (pull_request) Successful in 1m10s
Build and Push Attic Cache / test (pull_request) Successful in 1m43s
Build and Push Attic Cache / build (pull_request) Has been skipped
Build and Push Attic Cache / pin-lifecycle (pull_request) Has been skipped
Build and Push Attic Cache / remediate (pull_request) Has been skipped
Build and Push Attic Cache / notify-remediation (pull_request) Has been skipped
Build and Push Attic Cache / record-remediation-notify-failure (pull_request) Has been skipped
88e72e28f6
Author
Owner

Automated code review

Reviewed commit: c9c905fd23b2add7c5d5a99ca8e1bc1c09939acc

Verdict: Requires further work

Resolve the blocking or important findings and investigate failed deterministic checks before merging.

Overall assessment

The PR introduces a native Jellyfin service and a reusable NFS media-library module on the Lyra host, replacing a Docker-based setup. It configures NVIDIA hardware acceleration, hard dependencies on the NFS mount to prevent scanning empty directories, and integrates with existing backup and failure monitoring systems. The reviewer notes that the NFS mount timeout is intentional and bounded, and that nofail was removed to align with Jellyfin's hard dependency.

Blocking findings

None.

Important findings

  • NFS mount timeout may cause boot delays if NAS is unavailable (modules/services/media-library.nix:36; medium confidence)
    "x-systemd.mount-timeout=30s"
    Impact: If the NFS server is unreachable during boot, the system will wait up to 30 seconds for each mount attempt before failing or timing out, potentially delaying boot or service startup.
    Suggested fix: Consider if 30s is sufficient for your network conditions. If the NAS is often unavailable, nofail might be appropriate for non-critical mounts, but Jellyfin's hard dependency (Requires=) means the mount must succeed for Jellyfin to start. The current setup is consistent with the stated goal of preventing Jellyfin from scanning an empty mount.

Suggestions

  • Jellyfin service failure monitoring coverage (modules/services/jellyfin.nix:92; high confidence)
    services.serviceFailureMonitor.services = lib.mkAfter [ "jellyfin" ];
    Impact: Ensures Jellyfin is monitored for failures. This is good practice.
    Suggested fix: No action needed; this is a positive finding confirming proper integration with the existing failure monitoring system.

  • Backup data path configuration (modules/services/jellyfin.nix:91; high confidence)
    nixosConfig.backup.dataPaths = lib.mkAfter [ dataDir ];
    Impact: Ensures Jellyfin's state directory is included in backups. This is critical for preserving library metadata and settings.
    Suggested fix: No action needed; this is a positive finding confirming proper backup integration.

  • NVIDIA device access configuration (modules/services/jellyfin.nix:70; high confidence)
    DeviceAllow = [ "char-nvidiactl rw" "char-nvidia-caps rw" "char-nvidia-frontend rw" "char-nvidia-uvm rw" ];
    Impact: Explicitly grants Jellyfin access to necessary NVIDIA devices for hardware acceleration. This is more secure than PrivateDevices = false alone.
    Suggested fix: No action needed; this is a positive finding confirming proper security hardening for GPU access.

Tests and validation

  • No configured deterministic checks.

Questions

  • Has the media-library module been tested on any host other than Lyra to ensure its reusability?
  • Are there any other services on Lyra that depend on /mnt/media that might need similar NFS mount configuration?

Review limitations

  • I cannot verify the live deployment evidence or the actual performance of hardware acceleration.
  • I cannot test the NFS mount behavior under network failure conditions.
  • The review is based on the provided diff and does not include full context of all host configurations.

Diff coverage

  • README.md: reviewed — included in a context-limited batch
  • hosts/lyra/default.nix: reviewed — included in a context-limited batch
  • modules/services/jellyfin.nix: reviewed — included in a context-limited batch
  • modules/services/media-library.nix: reviewed — included in a context-limited batch
Review metadata
  • Reviewer: forgejo-ai-review v1
  • Model: qwen3.6:35b-a3b-q8_0
  • Guidance loaded: .forgejo/ai-review.md, .forgejo/ai-review.yml, AGENTS.md, README.md
  • Commits considered: 3
  • Previous automated review: updated
  • Findings: 4
<!-- forgejo-ai-review:v1 --> ## Automated code review Reviewed commit: `c9c905fd23b2add7c5d5a99ca8e1bc1c09939acc` ## Verdict: Requires further work Resolve the blocking or important findings and investigate failed deterministic checks before merging. ### Overall assessment The PR introduces a native Jellyfin service and a reusable NFS media-library module on the Lyra host, replacing a Docker-based setup. It configures NVIDIA hardware acceleration, hard dependencies on the NFS mount to prevent scanning empty directories, and integrates with existing backup and failure monitoring systems. The reviewer notes that the NFS mount timeout is intentional and bounded, and that `nofail` was removed to align with Jellyfin's hard dependency. ### Blocking findings None. ### Important findings - **NFS mount timeout may cause boot delays if NAS is unavailable** (modules/services/media-library.nix:36; medium confidence) "x-systemd.mount-timeout=30s" Impact: If the NFS server is unreachable during boot, the system will wait up to 30 seconds for each mount attempt before failing or timing out, potentially delaying boot or service startup. Suggested fix: Consider if 30s is sufficient for your network conditions. If the NAS is often unavailable, `nofail` might be appropriate for non-critical mounts, but Jellyfin's hard dependency (`Requires=`) means the mount must succeed for Jellyfin to start. The current setup is consistent with the stated goal of preventing Jellyfin from scanning an empty mount. ### Suggestions - **Jellyfin service failure monitoring coverage** (modules/services/jellyfin.nix:92; high confidence) services.serviceFailureMonitor.services = lib.mkAfter [ "jellyfin" ]; Impact: Ensures Jellyfin is monitored for failures. This is good practice. Suggested fix: No action needed; this is a positive finding confirming proper integration with the existing failure monitoring system. - **Backup data path configuration** (modules/services/jellyfin.nix:91; high confidence) nixosConfig.backup.dataPaths = lib.mkAfter [ dataDir ]; Impact: Ensures Jellyfin's state directory is included in backups. This is critical for preserving library metadata and settings. Suggested fix: No action needed; this is a positive finding confirming proper backup integration. - **NVIDIA device access configuration** (modules/services/jellyfin.nix:70; high confidence) DeviceAllow = [ "char-nvidiactl rw" "char-nvidia-caps rw" "char-nvidia-frontend rw" "char-nvidia-uvm rw" ]; Impact: Explicitly grants Jellyfin access to necessary NVIDIA devices for hardware acceleration. This is more secure than `PrivateDevices = false` alone. Suggested fix: No action needed; this is a positive finding confirming proper security hardening for GPU access. ### Tests and validation - No configured deterministic checks. ### Questions - Has the `media-library` module been tested on any host other than Lyra to ensure its reusability? - Are there any other services on Lyra that depend on `/mnt/media` that might need similar NFS mount configuration? ### Review limitations - I cannot verify the live deployment evidence or the actual performance of hardware acceleration. - I cannot test the NFS mount behavior under network failure conditions. - The review is based on the provided diff and does not include full context of all host configurations. ### Diff coverage - `README.md`: **reviewed** — included in a context-limited batch - `hosts/lyra/default.nix`: **reviewed** — included in a context-limited batch - `modules/services/jellyfin.nix`: **reviewed** — included in a context-limited batch - `modules/services/media-library.nix`: **reviewed** — included in a context-limited batch <details><summary>Review metadata</summary> - Reviewer: forgejo-ai-review v1 - Model: qwen3.6:35b-a3b-q8_0 - Guidance loaded: .forgejo/ai-review.md, .forgejo/ai-review.yml, AGENTS.md, README.md - Commits considered: 3 - Previous automated review: updated - Findings: 4 </details>
refactor: address Jellyfin mount review
All checks were successful
AI pull-request review / review (pull_request) Successful in 1m19s
Build and Push Attic Cache / test (pull_request) Successful in 1m41s
Build and Push Attic Cache / build (pull_request) Has been skipped
Build and Push Attic Cache / pin-lifecycle (pull_request) Has been skipped
Build and Push Attic Cache / remediate (pull_request) Has been skipped
Build and Push Attic Cache / notify-remediation (pull_request) Has been skipped
Build and Push Attic Cache / record-remediation-notify-failure (pull_request) Has been skipped
ea35684a50
Author
Owner

Addressed the automated review in ea35684.

  • Removed the MR-specific NFS findmnt preflight. Jellyfin still has Requires= and After= on mnt-media.mount, plus RequiresMountsFor=/mnt/media, so the generated mount unit remains the authoritative hard dependency. The only evaluated ExecStartPre now is the upstream NixOS Jellyfin pre-start script.
  • Made nixosConfig.mediaLibrary.source required instead of embedding the Lyra NAS IP as a reusable-module default. Lyra now sets 192.168.8.238:/volume1/media explicitly.

The 30-second mount timeout is intentional as a bounded startup wait; the deployed mount established successfully, while hard,timeo=600,retrans=2 governs established NFS I/O. The former LSIO identity/environment handling is not required by the native service: native state ownership is managed by the NixOS module, UI-managed configuration was imported, and NVIDIA access and transcoding are declared in jellyfin.nix.

Validation after the changes: full nix flake check passes; evaluated Lyra configuration preserves Requires=["mnt-media.mount"] and the same NAS export.

Addressed the automated review in `ea35684`. - Removed the MR-specific NFS `findmnt` preflight. Jellyfin still has `Requires=` and `After=` on `mnt-media.mount`, plus `RequiresMountsFor=/mnt/media`, so the generated mount unit remains the authoritative hard dependency. The only evaluated `ExecStartPre` now is the upstream NixOS Jellyfin pre-start script. - Made `nixosConfig.mediaLibrary.source` required instead of embedding the Lyra NAS IP as a reusable-module default. Lyra now sets `192.168.8.238:/volume1/media` explicitly. The 30-second mount timeout is intentional as a bounded startup wait; the deployed mount established successfully, while `hard,timeo=600,retrans=2` governs established NFS I/O. The former LSIO identity/environment handling is not required by the native service: native state ownership is managed by the NixOS module, UI-managed configuration was imported, and NVIDIA access and transcoding are declared in `jellyfin.nix`. Validation after the changes: full `nix flake check` passes; evaluated Lyra configuration preserves `Requires=["mnt-media.mount"]` and the same NAS export.
fix: align Jellyfin mount failure policy
All checks were successful
AI pull-request review / review (pull_request) Successful in 1m13s
Build and Push Attic Cache / test (pull_request) Successful in 1m42s
Build and Push Attic Cache / build (pull_request) Has been skipped
Build and Push Attic Cache / pin-lifecycle (pull_request) Has been skipped
Build and Push Attic Cache / remediate (pull_request) Has been skipped
Build and Push Attic Cache / notify-remediation (pull_request) Has been skipped
Build and Push Attic Cache / record-remediation-notify-failure (pull_request) Has been skipped
c9c905fd23
Author
Owner

Addressed the updated automated review blocker in c9c905f.

  • Removed nofail from the shared media mount so its failure policy now matches Jellyfin's hard dependency.
  • Kept the 30-second systemd mount timeout to bound an unavailable NAS.
  • Added an explicit comment documenting the safety policy: Jellyfin must not start and scan an empty local /mnt/media mountpoint when the NAS export is unavailable.

The Docker state migration was performed before the final MR configuration was committed. The imported native state is already running from /var/lib/jellyfin and /var/cache/jellyfin; the one-time importer was intentionally removed only after live startup, library, playback, and RTX 4070 transcoding validation succeeded.

Validation: full nix flake check passes. Evaluated Lyra configuration has no nofail option, preserves the explicit NAS export, and keeps Requires=["mnt-media.mount"] for Jellyfin.

Addressed the updated automated review blocker in `c9c905f`. - Removed `nofail` from the shared media mount so its failure policy now matches Jellyfin's hard dependency. - Kept the 30-second systemd mount timeout to bound an unavailable NAS. - Added an explicit comment documenting the safety policy: Jellyfin must not start and scan an empty local `/mnt/media` mountpoint when the NAS export is unavailable. The Docker state migration was performed before the final MR configuration was committed. The imported native state is already running from `/var/lib/jellyfin` and `/var/cache/jellyfin`; the one-time importer was intentionally removed only after live startup, library, playback, and RTX 4070 transcoding validation succeeded. Validation: full `nix flake check` passes. Evaluated Lyra configuration has no `nofail` option, preserves the explicit NAS export, and keeps `Requires=["mnt-media.mount"]` for Jellyfin.
Author
Owner

Reviewed the c9c905f automated result. No further code change is warranted for the remaining timeout observation.

The 30-second value is a maximum bounded wait, not an observed startup delay. Lyra and the NAS are on the same LAN, and repeated live activations mounted the export successfully without approaching the timeout. If the NAS is unavailable, delaying the required media mount and leaving Jellyfin stopped is preferable to starting Jellyfin against an empty directory. The latest review also confirms that this is internally consistent with the stated safety policy.

The reusable module has not yet been deployed on another host; Lyra is its canary. No other native Lyra service currently consumes /mnt/media. Existing Docker stacks retain their own mounts, while future native migrations can opt into this module explicitly.

Reviewed the `c9c905f` automated result. No further code change is warranted for the remaining timeout observation. The 30-second value is a maximum bounded wait, not an observed startup delay. Lyra and the NAS are on the same LAN, and repeated live activations mounted the export successfully without approaching the timeout. If the NAS is unavailable, delaying the required media mount and leaving Jellyfin stopped is preferable to starting Jellyfin against an empty directory. The latest review also confirms that this is internally consistent with the stated safety policy. The reusable module has not yet been deployed on another host; Lyra is its canary. No other native Lyra service currently consumes `/mnt/media`. Existing Docker stacks retain their own mounts, while future native migrations can opt into this module explicitly.
nimmo merged commit 4620968c23 into main 2026-08-24 21:52:17 +01:00
nimmo deleted branch feat/issue-30-native-jellyfin-prep 2026-08-24 21:52:18 +01:00
Sign in to join this conversation.
No reviewers
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
nimmo/nixos-config!190
No description provided.