Automated hot/cold tiered storage for a self-hosted Jellyfin media server.
Media files you haven't watched in a while are moved to cheap cold storage and recalled on-demand when you press play — all without breaking Jellyfin paths or requiring any manual management.
- Archives unwatched files to any rclone-supported backend (cloud storage, NAS, SFTP, …) based on configurable inactivity thresholds
- Recalls cold files automatically when playback starts, swapping the symlink for the real file
- Cleans up cold storage when items are deleted from the Jellyfin library
- Tags items in Jellyfin so users can see at a glance what's cold
Jellyfin paths never change. A symlink at the original location keeps everything working during and after the transition between hot and cold.
Hot storage Cold storage
(fast, local) (cheap, remote)
/media/movies/Dune/
Dune.mkv ──────► rclone upload ──► remote:Cold/movies/Dune/Dune.mkv
│
└── replaced by symlink ──► /mnt/cold/movies/Dune/Dune.mkv
▲
Jellyfin streams via local mount
On playback, the listener detects the symlink, downloads the file in the background (or blocks, depending on your config), and swaps the symlink for the real file. The cold copy is deleted after a successful recall.
HotColdJelly uses rclone as its cold storage abstraction. Any backend rclone supports works out of the box:
- Amazon S3 (and S3-compatible: Wasabi, Cloudflare R2, MinIO…)
- Backblaze B2
- Google Drive
- SFTP / SSH
- Local or network paths (NAS over SMB/NFS)
- And many more
Configure your backend once in rclone.conf, point RCLONE_REMOTE at it — HotColdJelly doesn't care what's behind it.
Cold storage must also be accessible as a local mount (WebDAV, NFS, FUSE, etc.) so that Jellyfin can stream from it via symlinks without needing to recall first.
| Container | Role |
|---|---|
listener |
Webhook server — handles recall on playback and cleanup on deletion |
archive |
Archival runner — scans hot dirs, applies thresholds, uploads to cold |
- How it works
- Docker setup
- Cold mount setup
- Dev setup
- Webhook setup
- Helper scripts
- Configuration reference
- Infrastructure
- Docker + Docker Compose
- Jellyfin with the Webhook plugin
- rclone configured with at least one remote
- Cold storage mounted locally (WebDAV, NFS, FUSE, or equivalent)