Fast Emby / Jellyfin libraries with .strm (recommended)¶
If your library scans are slow, this is the setup you want.
Instead of mounting ClearStreamer and letting your media server read every file's header
over the network, you sync a tiny local library of .strm shortcut files (plus the
matching posters and artwork). Your media server scans that local folder — which is fast,
because nothing crosses the network during a scan — and the actual video only streams
from your CDN node when someone presses Play.
Why this is faster
A mounted library makes the scanner open every file over a high-latency link, so
a full scan of a large catalog can take hours or days. With a local .strm library
the scanner only touches small local files, so the same catalog scans in minutes.
Playback is unchanged — it still streams direct from your CDN node.
How it works¶
ClearStreamer (origin) Your server
────────────────────── ─────────────────────────────
.strm + artwork ── rclone sync ──▶ /library ◀── Emby / Jellyfin scans this
(tiny files) (fast, all local)
Press Play ─▶ CDN node ─▶ video streams
(only the bytes you watch)
- A
.strmfile is a one-line text file containing the streaming URL of one video. Emby, Jellyfin, Plex, and Silo all understand.strmnatively and treat it like a playable video. - The
.strmfiles and artwork are pre-generated for you on the ClearStreamer side, so your server never has to probe videos or download metadata to build the library. - Each
.strmis addressed to your own CDN node as you download it, so playback goes straight to your node with nothing for you to configure or rewrite. - Only
.strmstubs and images live on your disk. The video files never do — they stream from your CDN node on demand. - Alongside the stubs you get the full local metadata set:
.nfodetails, posters, fanart, season art, episode thumbnails, and subtitles. That's what lets your server build a complete-looking library with internet metadata turned off.
Mount or .strm? Pick one per library
The rclone mount and
this .strm method are two ways to reach the same content. Use .strm when scan
speed matters (large libraries, low-powered servers, high-latency links). Use a
mount when you want the raw files to appear as real files on disk. Don't point
two libraries at the same content with both methods — pick one per library.
Before you start¶
You need:
- Your S3 endpoint, access key, and secret key (from your welcome email) and your whitelist IP already set — see Endpoint & credentials.
- rclone installed on the server that runs Emby/Jellyfin — see S3 client examples.
- Local disk space. The
.strmstubs are tiny, but the artwork is not. Budget for the artwork, not the videos.
How much disk you need
The .strm files are a few hundred bytes each; posters, fanart, episode
thumbnails, and subtitles are the bulk. The whole library is roughly 390 GiB
across ~8.9 million files — a rounding error next to the video itself, but not
nothing. Approximate size per section:
| Section | Size |
|---|---|
television |
~215 GiB |
movies |
~150 GiB |
xxx |
~14 GiB |
sports |
~3 GiB |
courses |
~2 GiB |
If disk is tight, sync only the sections you want — each is a separate top-level
folder, so you can pull movies and television and skip the rest. To check a
subtree yourself:
rclone size --fast-list zendrive:strm-tree/movies
Plan for the file count, not the byte count
390 GiB is small, but 8.9 million files is not. The first sync takes hours, and it is limited by per-file round-trips rather than bandwidth. Make sure the destination filesystem is comfortable with millions of small files (ext4/XFS/ZFS are fine; avoid exFAT and most network shares).
Step 1 — Configure rclone¶
If you already mount ClearStreamer, you already have this remote and can skip ahead. Otherwise
create ~/.config/rclone/rclone.conf (zendrive here is just the remote's name —
use whatever you called yours):
[zendrive]
type = s3
provider = Other
access_key_id = YOUR_ACCESS_KEY
secret_access_key = YOUR_SECRET_KEY
endpoint = https://YOUR-ENDPOINT/
force_path_style = true
Verify it works:
rclone lsd zendrive:strm-tree
You should see one folder per section (courses, movies, sports, television,
xxx).
Your .strm library location
The examples use the strm-tree bucket. If the bucket or path for your service
differs, it's listed in your welcome email / client area — substitute it wherever
you see zendrive:strm-tree.
Step 2 — Sync the .strm library to local disk¶
Pull the pre-generated .strm + artwork tree to a local folder. Use sync so your local
copy stays an exact mirror (titles removed upstream are removed locally too):
rclone sync zendrive:strm-tree /library \
--ignore-size \
--fast-list \
--transfers 64 --checkers 64 \
--use-server-modtime \
--max-delete 50000 \
--retries 3 --low-level-retries 10 \
--stats 2m --stats-one-line
| Flag | Why |
|---|---|
--ignore-size |
Required — the sync fails without it. See the warning below. |
--fast-list |
Reads the tree in batched LIST calls (one per ~1000 objects) instead of one call per directory — essential on a tree this large and deep. Costs ~1 KB RAM per object, so budget ~8 GB… see the note below if that's too much. |
--transfers 64 --checkers 64 |
Millions of small files, so round-trips (not bandwidth) are the limit. Lower these if your server struggles. |
--use-server-modtime |
Keeps each file's real date so "Recently Added" and date sorting work, and it's what the sync compares on. Never use --no-modtime — see the warning on the clients page. |
--max-delete 50000 |
Safety cap: if something upstream looks catastrophically smaller than it should, the sync aborts instead of emptying your library. |
--retries / --low-level-retries |
Survives transient network blips on a long sync. |
--ignore-size is required, not optional
Each .strm file is personalised as it's delivered: the playback URL inside it is
rewritten to point at your CDN node. That makes the delivered file a slightly
different length than the catalog listing advertises, and rclone treats a size
mismatch as a corrupt transfer:
ERROR : corrupted on transfer: sizes differ 142 vs 145
--ignore-size tells rclone to compare modification time instead of size, which
is the correct check here. Leave it out and every .strm fails to transfer; use
--size-only and every file re-downloads on every run, forever.
sync deletes local files that are gone upstream
rclone sync makes the destination match the source, which means it deletes
local files no longer in the library. That's what you want for a mirror, but point it
at a dedicated folder (like /library), never a folder that also holds your own
files. If you'd rather only ever add files, use rclone copy instead.
If --fast-list uses too much memory
--fast-list holds the listing in RAM (~1 KB per object, so ~8 GB for the full
library). On a smaller box, just drop the flag — rclone then walks the tree
directory by directory, which uses far less memory at the cost of more requests and
a longer run. Syncing one section at a time keeps both bounded.
The first sync takes hours — it's ~8.9 million files, and it's round-trip bound, not bandwidth bound. Later syncs only move what changed and finish far faster.
Keep it updated automatically (systemd)¶
Don't run the sync by hand — install it as a systemd timer so your library stays fresh on its own. This is the setup we recommend everyone use.
1. The sync script — wrapped in a lock so a long run can never be started twice:
#!/usr/bin/env bash
# /usr/local/bin/zendrive-strm-sync.sh
set -uo pipefail
LOG=/var/log/zendrive-strm-sync.log
DST=/library
# Refuse to start if the previous run is still going.
exec 9>/tmp/zendrive-strm-sync.lock
if ! flock -n 9; then
echo "$(date -Is) SKIP: previous sync still running" >>"$LOG"
exit 0
fi
echo "$(date -Is) START" >>"$LOG"
/usr/bin/rclone sync zendrive:strm-tree "$DST" \
--ignore-size \
--fast-list \
--transfers 64 --checkers 64 \
--use-server-modtime \
--max-delete 50000 \
--retries 3 --low-level-retries 10 \
--stats 2m --stats-one-line \
--log-file "$LOG" --log-level INFO
echo "$(date -Is) END rc=$?" >>"$LOG"
Make it executable: sudo chmod +x /usr/local/bin/zendrive-strm-sync.sh
2. The service — a one-shot job that runs that script. Run it as the same user your media server runs as so the files it writes are readable by Emby/Jellyfin, and keep it gentle on the box while a large sync runs:
# /etc/systemd/system/zendrive-strm-sync.service
[Unit]
Description=Sync ClearStreamer .strm library to local disk
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
# Run as your media-server account so /library is readable by Emby/Jellyfin.
# rclone reads that user's ~/.config/rclone/rclone.conf.
User=emby
Group=emby
# Be a good neighbour: never starve the media server of CPU or disk I/O.
Nice=10
IOSchedulingClass=best-effort
IOSchedulingPriority=6
ExecStart=/usr/local/bin/zendrive-strm-sync.sh
# A full reconcile of the whole library legitimately runs for many hours.
# Set this too low and systemd kills the job mid-run — see the warning below.
TimeoutStartSec=20h
3. The timer — starts a run 20 minutes after boot, then 6 hours after each run
finishes. Persistent=true means a run missed while the machine was off fires as soon
as it's back:
# /etc/systemd/system/zendrive-strm-sync.timer
[Unit]
Description=Periodic ClearStreamer .strm library sync
[Timer]
OnBootSec=20min
OnUnitInactiveSec=6h
Persistent=true
RandomizedDelaySec=10min
[Install]
WantedBy=timers.target
Don't shorten the interval or the timeout
These two settings look conservative and aren't. A full reconcile of the library
walks ~8.9 million objects and can run for many hours — long enough that an
hourly timer would simply stack runs on top of each other, and a short
TimeoutStartSec makes systemd kill the job partway through, every time, so the
library silently stops updating.
OnUnitInactiveSec (not OnUnitActiveSec) is deliberate: it measures the gap from
when the last run finished, so runs can never overlap regardless of how long one
takes. RandomizedDelaySec just avoids every server hitting the endpoint on the
same tick.
4. Enable it:
sudo systemctl daemon-reload
sudo systemctl enable --now zendrive-strm-sync.timer
5. Check it's working:
systemctl list-timers zendrive-strm-sync.timer # when it last ran / runs next
systemctl status zendrive-strm-sync.service # result of the last run
journalctl -u zendrive-strm-sync.service -n 50 # sync output
sudo systemctl start zendrive-strm-sync.service # force a run right now
Match User= / Group= to your media server
Replace emby with the account Emby or Jellyfin actually runs as (Docker users: run
the sync as the same UID/GID you pass the container, or write to a volume it can
read). If rclone can't find its config, add --config /home/emby/.config/rclone/rclone.conf
to the ExecStart line.
Picking up the new files
After each sync, your media server's own scheduled library scan finds the changes — no extra step needed. To have new content appear within seconds instead of on the next scan, pair this with ZenLocalPoller, which reacts to library changes as they happen.
Step 3 — Add the library in your media server¶
Point Emby or Jellyfin at the local folder — not at a mount. The settings below make the scan fast and stop the server from trying to fetch things over the network.
- Settings → Library → Add Media Library.
- Content type:
Moviesfor a movies folder,Showsfor TV. Add one library per content type, each pointing at the matching subfolder (e.g./library/movies,/library/television). - Folders: add the local path (e.g.
/library/movies). - Open the library's options and set:
- Enable real time monitoring: OFF (you rescan after each sync instead).
- Metadata downloaders (internet): uncheck them all. The artwork and
.nfofiles are already in the folder, so the server reads titles and images locally and never calls TMDb/TVDb. - Metadata readers: make sure Nfo is enabled and first.
- Save artwork/metadata into media folders: OFF (the folder is a read-only mirror; let the sync own it).
- Chapter image extraction: OFF (extracting chapter images would read the video over the network).
- Save. Emby scans the folder.
- Dashboard → Libraries → Add Media Library.
- Content type:
MoviesorShows. Add one library per content type, each pointing at the matching subfolder (e.g./library/movies,/library/television). - Folders: add the local path (e.g.
/library/movies). - In the library settings:
- Enable real time monitoring: OFF.
- Metadata downloaders (Movie/Series/Season/Episode): uncheck them all so
Jellyfin uses the local
.nfoand artwork instead of the internet. - Metadata savers: leave Nfo as the reader; don't enable "Save artwork into media folders."
- Chapter image extraction: OFF.
- Save. Jellyfin scans the folder.
Keep internet metadata off for the fastest, most reliable scans
The projection already ships posters, fanart, and .nfo details, so internet
downloaders add nothing but network calls and rate-limit delays. If you specifically
want the server to fetch extra metadata online you can turn a downloader back on,
but expect slower scans.
Step 4 — Scan and verify¶
- The scan starts automatically when you add the library, or trigger it from the library menu.
- Play a title. It should start streaming from your CDN node within a second or two.
Nothing was downloaded to your disk to make that happen — the
.strmpointed the player straight at the CDN.
Large libraries: titles appear in minutes, the scan bar finishes later
On a very large library the items become browsable and playable within a few minutes, but the scan progress bar can keep moving for a good while afterward as the server finishes its internal bookkeeping over hundreds of thousands of items. This is normal — the library is fully usable while it finishes. It only happens on the first scan; later incremental scans are quick.
Troubleshooting¶
| Symptom | Fix |
|---|---|
corrupted on transfer: sizes differ on every .strm |
You're missing --ignore-size. Add it to the sync command — see Step 2. |
| Every file re-downloads on every run | You're using --size-only. Replace it with --ignore-size so rclone compares modification time. |
| Sync never finishes / library stops updating | The run is being killed partway through. Check TimeoutStartSec is generous (20h) and that the timer uses OnUnitInactiveSec, not OnUnitActiveSec. |
| A title won't play | Confirm your whitelist IP is current — playback still goes through your CDN node, which is IP-locked. See Updating your whitelist IP. |
| Scan is slow / server is probing videos | You likely left an internet metadata downloader or chapter image extraction on, or you pointed the library at a mount instead of the synced folder. Recheck Step 3. |
| "Recently Added" / dates look wrong | Make sure the sync uses --use-server-modtime (not --no-modtime). |
| Titles missing after an upstream change | Re-run the sync (Step 2). Remember sync mirrors deletions; copy only adds. |
| Sync aborted with a max-delete error | --max-delete did its job and stopped an unexpectedly large removal. Check the log to see what it wanted to delete before re-running. |
| Disk filling up | Artwork is the bulk. Sync fewer sections, or drop the largest ones. Check with rclone size --fast-list zendrive:strm-tree/<section>. |
out of memory during the sync |
--fast-list needs ~1 KB per object. Drop the flag, or sync one section at a time. |
See also¶
- Using Silo, Plex, Emby, or Jellyfin — overall media-server guidance
- S3 client examples — rclone setup and the mount alternative
- How live updates work — keeping the library fresh