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:
- Local media stored on a home machine and mounted to the VPS over a private tunnel and read-only network filesystem.
- Automated Real-Debrid-backed media managed by Riven and exposed to Jellyfin through a curated virtual filesystem.
- Manual Real-Debrid links submitted through a small web portal and converted into curated
.strmentries.
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 extrasThe 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 portalNot 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 filterThe 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
zurgLocal-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 libraryLibrary 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 filesDo 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
.strmfiles. - 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
.strmfiles. - 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:
.strmfile 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 JellyfinRiven 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
.strmexists 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 RunningAvoid 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 ShowsThis 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:
- Public Jellyfin loads through HTTPS.
- Public request UI loads through HTTPS.
- Manual intake portal loads through HTTPS and requires authentication.
- Jellyfin sees the curated local, Riven, and manual libraries.
- Jellyfin does not see raw Real-Debrid/Zurg storage.
- Riven polls the request service.
- Riven can complete at least one request through Real-Debrid.
- Completed Riven items appear in the curated Riven library.
- Manual Real-Debrid links can become visible through curated
.strmfiles. - Local media is visible while the home machine is online.
- 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