Projects / Self-Hosted Media Platform / Architecture appendix

Project appendix / public sanitized baseline

Media Platform Technical Snapshot

This document describes a private media platform architecture after moving the public edge to a VPS and adding request automation, curated Real-Debrid-backed media, manual link intake, filtering, and mount hardening.

Sanitization note

Sensitive details have been removed or generalized. This public version intentionally omits personal names, public IPs, private network addresses, secret paths, usernames, exact inventory counts, specific media titles, and direct administrative commands.

1. System Purpose

The platform is a private, always-on media system with three content paths:

  1. Local media stored on a home machine and mounted to the VPS over a private tunnel and read-only network filesystem.
  2. Automated Real-Debrid-backed media managed by Riven and exposed to Jellyfin through a curated virtual filesystem.
  3. Manual Real-Debrid links submitted through a small web portal and converted into curated .strm entries.

The user-facing flow is:

Jellyfin
  -> local libraries when the home media source is online
  -> automated Real-Debrid/Riven libraries from the VPS
  -> manual Real-Debrid movies/shows from curated links

Jellyseerr
  -> normal request flow
  -> Riven automation

Manual intake portal
  -> paste supported Real-Debrid links
  -> generate curated .strm files
  -> refresh Jellyfin
  -> filter noisy extras

The raw Real-Debrid/Zurg mount remains backend-only. It is deliberately not mounted into Jellyfin.

2. Public Services

Only the user-facing web applications are exposed publicly through HTTPS:

media.example.com     -> reverse proxy -> Jellyfin
request.example.com   -> reverse proxy -> Jellyseerr
manual.example.com    -> reverse proxy -> manual intake portal

Not public:

  • Riven API
  • Zurg
  • Postgres
  • raw Real-Debrid WebDAV/FUSE mount
  • Docker daemon
  • local service ports
  • administrative SSH

3. High-Level Topology

Client device
  |
  | HTTPS
  v
VPS reverse proxy
  |
  +-- Jellyfin
  |     +-- local media mount from home machine
  |     +-- curated Riven library
  |     +-- curated manual Real-Debrid library
  |
  +-- Jellyseerr
  |     -> Riven polls requests
  |
  +-- manual intake portal
        -> Real-Debrid API
        -> curated .strm files
        -> Jellyfin refresh
        -> manual filter

The VPS is the public edge node. The home machine is only a local media source and is not required for Real-Debrid/Riven playback.

4. Core Stack

The stack uses:

  • Caddy as the HTTPS reverse proxy.
  • Docker Compose for service orchestration.
  • Jellyfin for playback and library browsing.
  • Jellyseerr for requests.
  • Riven for automated Real-Debrid acquisition and curated VFS exposure.
  • Postgres for Riven persistence.
  • Zurg plus rclone for backend Real-Debrid/WebDAV access.
  • Systemd units and timers for stack lifecycle, mounts, cleanup, and maintenance.

Core containers:

jellyfin
jellyseerr
riven
riven-db
zurg

Local-only bindings are used for application ports. Public traffic reaches the applications through the reverse proxy only.

5. Jellyfin Library Model

Jellyfin should consume only curated paths:

local media mount
curated Riven library
curated manual Real-Debrid library

Library intent:

Local Movies       -> local movies folder
Local Shows        -> local shows/anime folders
Real-Debrid        -> curated Riven movies/shows
Manual RD Movies   -> curated manual movie .strm files
Manual RD Shows    -> curated manual show .strm files

Do not add the raw Real-Debrid/Zurg mount to Jellyfin. That path is backend/debug storage only.

6. Why Raw Real-Debrid Is Hidden From Jellyfin

Raw Real-Debrid/Zurg is too broad and remote-backed for direct Jellyfin scanning.

Problems observed:

  • Jellyfin attempted to probe many raw remote files.
  • Trickplay and chapter image extraction created unnecessary remote I/O.
  • Some ffmpeg/probe jobs became slow or stuck.
  • Library scans became less stable.

Solution:

  • Keep the raw Real-Debrid mount backend-only.
  • Let Riven expose a curated library.
  • Let the manual intake flow create curated .strm files.
  • Configure Jellyfin so metadata, images, subtitles, and generated artifacts are stored in Jellyfin config/cache rather than written to mounted media paths.

7. Manual Real-Debrid Intake

The manual portal accepts only supported Real-Debrid direct links and turns them into curated Jellyfin entries.

It is deliberately narrow:

  • It validates submitted links.
  • It calls the Real-Debrid API.
  • It writes curated .strm files.
  • It writes sidecar metadata.
  • It runs the manual filter.
  • It triggers a Jellyfin scan.

It is not a shell runner and should not expose general command execution.

Authentication should sit in front of the portal, for example with reverse-proxy basic authentication or another access-control layer. Passwords, usernames, API tokens, and secret file paths should never be committed into public docs.

8. Manual Filtering

Bulk Real-Debrid folder imports can include noisy extras such as creditless openings/endings, trailers, samples, interviews, and other non-primary videos. These often appear as broken or unwanted Jellyfin items.

The manual filter moves likely extras out of Jellyfin-visible storage while preserving enough metadata to restore or audit them later:

  • .strm file information
  • sidecar metadata
  • original Real-Debrid source link
  • filename and size metadata
  • Real-Debrid resolution metadata

The filter can periodically recheck quarantined items. If a link later resolves to a normal usable movie or episode filename instead of an extra filename, it can be restored to the visible manual library.

9. Riven And 451 Dampening

Observed issue:

Riven can repeatedly hit Real-Debrid 451 Infringing Torrent responses. This can trigger cooldown and circuit-breaker storms, making the backend look stuck even when the stack is otherwise healthy.

The 451 cleaner:

  • scans recent Riven logs
  • records repeated blocked hashes
  • reports affected cooldown titles
  • restarts Riven only when thresholds are exceeded
  • rate-limits restarts to avoid restart loops

It does not:

  • delete media
  • expose Riven publicly
  • mount raw Real-Debrid into Jellyfin
  • guarantee that blocked sources become playable

Meaning of HTTP 451 in this context:

Real-Debrid refused a specific candidate hash. It is not a Jellyfin, reverse proxy, Docker, or VPS routing failure.

10. Home Media Mount Hardening

The local media library is mounted from a home machine over a private tunnel and read-only network filesystem.

Problem solved:

The mount could block hard when the home machine, private tunnel, or NFS path was slow or stale. This caused Jellyfin source errors and made health scripts hang.

Design:

  • mount on demand
  • keep the mount read-only
  • do not block VPS boot if the home machine is offline
  • fail reads faster instead of blocking indefinitely
  • keep Real-Debrid/Riven playback independent from the home machine

Operational meaning:

  • If the home machine is off, local media may appear unavailable.
  • Jellyfin should still run.
  • Riven/Real-Debrid media should remain available from the VPS.

11. Request Flow

Normal request path:

User opens request portal
  -> requests a movie or show
  -> Riven polls the request service
  -> Riven scrapes release candidates
  -> Riven checks Real-Debrid availability
  -> accepted media is exposed through the curated Riven library
  -> Jellyfin is refreshed
  -> user watches in Jellyfin

Riven state semantics:

  • Scraped: candidates were found, but media has not necessarily landed.
  • Indexed: metadata/index state exists; for shows this may be parent-level and not proof every episode landed.
  • Completed: media should be landed and visible.
  • PartiallyCompleted: some files landed.
  • Ongoing: still processing or waiting.

Manual Real-Debrid semantics:

  • A link appears in Jellyfin only after a .strm exists in the curated manual library and Jellyfin has scanned it.
  • Quarantined extras are intentionally not visible in Jellyfin.
  • Expired direct links may need to be refreshed through the private maintenance workflow.

12. Jellyfin Safety Settings

Remote-backed and read-only libraries should avoid unnecessary writes and heavy extraction jobs.

Recommended library settings:

Disable realtime monitoring on remote/virtual paths.
Disable chapter image extraction.
Disable trickplay image extraction unless explicitly needed.
Do not save local metadata into mounted media paths.
Do not save subtitles into mounted media paths.

Reason:

  • The local media mount is read-only.
  • Riven and Real-Debrid paths are virtual or remote-backed.
  • Metadata should live in Jellyfin config/cache, not in mounted media folders.

13. Service Health Expectations

Healthy baseline:

  • reverse proxy is active
  • Docker is active
  • media stack service is active
  • Real-Debrid backend mount service is active
  • Jellyfin is healthy
  • Jellyseerr is running
  • Riven is running
  • Riven database is healthy
  • Zurg is healthy

During a scan, posters and thumbnails may appear inconsistent until Jellyfin finishes metadata work and the UI/cache settles.

14. Known Cosmetic Issues

Poster Or Thumbnail Flicker

Observed behavior:

  • Entering a library and backing out to the home screen can cause thumbnails to disappear briefly.
  • Reloading the page restores them.

Likely causes:

  • Jellyfin web UI lazy image loading/cache behavior.
  • Active library scan.
  • Metadata/images still being written into Jellyfin's internal cache.

Suggested action:

Leave it alone unless it persists after scans are idle.

Scan-Time Metadata Weirdness

During active scans, shows may temporarily look incomplete or badly grouped.

Guideline:

Do not judge library correctness until the scan is idle.

15. Common Private Operations

The private runbook includes commands for:

  • checking service health
  • restarting the whole stack
  • restarting only Riven
  • restarting only Jellyfin
  • refreshing Jellyfin libraries
  • watching Riven logs
  • checking whether host-visible files are also visible inside the Jellyfin container
  • refreshing local media mounts
  • refreshing expired Real-Debrid links

Those commands are intentionally omitted from this public version because they reveal hostnames, users, paths, IPs, service names, and administrative habits. Keep them in a private operations document instead.

16. Troubleshooting Model

Request Does Not Appear In Riven

Check whether the request service has pending items and whether Riven is polling successfully.

Interpretation:

  • No fetched items: request service has nothing pending that Riven sees.
  • One or more fetched items: Riven saw the request.
  • Streams found: scraping worked.
  • Downloaded through Real-Debrid: acquisition worked.
  • 451 Infringing Torrent: Real-Debrid rejected that candidate hash.

Item Appears In Riven But Not Jellyfin

Compare the curated Riven library as seen from the host and from inside the Jellyfin container.

If the host sees files but Jellyfin does not, Jellyfin may need to be restarted or recreated so the current FUSE mount is rebound into the container.

Jellyfin Scan Looks Stuck

Check scheduled task state and logs.

Expected after adding content:

Scan Media Library Running

Avoid repeatedly triggering full scans while one is already running.

If raw Real-Debrid paths appear in ffmpeg or probe commands, that is a regression. Jellyfin should not see the raw Real-Debrid mount.

Local Media Not Loading Remotely

Check:

  • home machine is on
  • private tunnel is up
  • network filesystem export is active
  • VPS can read the local media mount
  • Jellyfin library paths point only to intended local folders

Remember:

Remote playback of local media depends on home upload and private tunnel stability. Riven/Real-Debrid playback does not depend on the home machine.

17. Design Boundaries

  • Do not expose Riven publicly.
  • Do not expose Zurg publicly.
  • Do not expose Postgres publicly.
  • Do not expose Docker publicly.
  • Do not mount raw Real-Debrid/Zurg storage into Jellyfin.
  • Do not print or commit Real-Debrid, Jellyfin, database, or reverse-proxy secrets.
  • Keep manual intake narrow.
  • Keep Jellyfin on curated paths only.
  • Avoid enabling heavy Jellyfin extraction features on remote-backed libraries without a clear reason.
  • Avoid letting Jellyfin write metadata into mounted media paths.
  • Avoid adding many extra Jellyfin libraries without a clear browsing benefit.

18. Why The Design Looks This Way

Why Use A VPS

The VPS provides:

  • always-on public endpoint
  • stable domain and TLS
  • reverse proxy
  • request and automation services even when the home machine is off
  • Real-Debrid/Riven media availability independent of local storage
  • cleaner mobile and remote access

The home machine is no longer the public service host. It is only a local media source.

Why Use One Curated Real-Debrid Library

A smaller Jellyfin library surface is easier to browse and maintain.

The preferred model is:

Local Movies
Local Shows
Real-Debrid
Manual RD Movies
Manual RD Shows

This avoids exposing raw backend storage while still keeping content discoverable.

Why Local Anime Can Live Under Local Shows

Anime is TV-style episodic content. Treating it as part of the local shows library avoids maintaining too many separate Jellyfin libraries while still allowing normal browsing and filtering.

19. Working Definition

The platform is considered working when:

  1. Public Jellyfin loads through HTTPS.
  2. Public request UI loads through HTTPS.
  3. Manual intake portal loads through HTTPS and requires authentication.
  4. Jellyfin sees the curated local, Riven, and manual libraries.
  5. Jellyfin does not see raw Real-Debrid/Zurg storage.
  6. Riven polls the request service.
  7. Riven can complete at least one request through Real-Debrid.
  8. Completed Riven items appear in the curated Riven library.
  9. Manual Real-Debrid links can become visible through curated .strm files.
  10. Local media is visible while the home machine is online.
  11. Services start at VPS boot.

20. Future Work

  • let scans settle and verify poster stability
  • improve request ranking rules if needed
  • add subtitle management later
  • add an advanced exact-release picker
  • consider a dedicated always-on home server or NAS if local media should be available 24/7 without relying on the desktop