Tag: Media Server

Guides for running your own media server: movies, TV and streaming from your own hardware with tested configurations.

  • Jellyfin vs Plex: Which One Fits Your Home Server?

    Jellyfin vs Plex: Which One Fits Your Home Server?

    The honest question is not “which is better” — it is “which fits how I watch and what I am willing to pay.” Jellyfin and Plex do almost the same job (turn a folder of media into a streamable library), but they make very different bets on price, on who controls the transcoding, and on how much you are expected to trust the vendor. This is a straight comparison across the things that actually matter, without a crown, so you can pick the one that fits your household.

    Beginner · 7 min · Media Server

    Price: the clearest difference

    Jellyfin is free, open source, and free forever. There is no premium tier, no monthly fee, and no feature held back behind a paywall. Everything in the server is available to everyone.

    Plex has a free tier, but the features people actually want — transcoding on the server, direct play to certain clients, and direct streaming to some devices — live behind Plex Pass, a paid subscription. If you run Plex the way most people want to, you are paying a recurring fee. For a home server you own, that is the single biggest practical difference, and for a lot of people it is the deciding one.

    Transcoding: who does the work

    This is the technical heart of the difference. Both apps can direct-play (send the file as-is, cheap on the CPU) and transcode (re-encode for a device that cannot play the source, expensive on the CPU).

    Jellyfin lets you use your own hardware encoder — Intel Quick Sync, NVIDIA, AMD — for the transcoding, and it is free to do so. The ceiling on how many 4K streams you can transcode at once is set by your hardware, and you can measure it. On a modest 4-core Intel box with a basic iGPU we measured three concurrent 4K-to-1080p transcodes holding real time before it started to slip — that is the number a dedicated 4K transcoding test on the same box confirms.

    Plex bundles a licensed encoder and handles the hardware acceleration for you, which is convenient — but the advanced transcoding and hardware-accelerated paths are part of the paid tier. The trade-off is that Plex’s client experience is generally smoother and more polished, at the cost of the subscription.

    Clients and ease of use

    Plex wins on the out-of-the-box experience. Its apps are polished, the setup wizard is friendly, and the metadata, artwork and matching are excellent with almost no configuration. If you want the least-friction path from “folder of movies” to “it just works on every screen,” Plex is the smoother ride.

    Jellyfin’s clients are good but a notch behind in polish. The browser player works everywhere, the mobile apps are solid, and most major smart TVs either have a native app or can run it in the browser. If you are willing to spend an evening configuring it, you get the full feature set for free. The setup is well documented in the Jellyfin Docker guide.

    Where each one fits

    If you…Reach forWhy
    Do not want to pay a subscription and you are comfortable configuringJellyfinFull features, free, and you control the hardware transcoding
    Want the smoothest setup and you do not mind a subscriptionPlexPolished clients, great metadata, minimal fiddling
    Have a capable iGPU or GPU and want to transcode 4KJellyfinFree hardware transcoding, and you can measure your ceiling
    Mostly watch on a smart TV and want it to just workEitherBoth work; Plex is slightly smoother, Jellyfin is free
    Want everything to stay on your own hardware, no vendor cloudJellyfinNo account, no service dependency, open source

    The privacy and control angle

    Both run the media server on your own machine, so your files stay with you in both cases. The difference is in the control plane. Jellyfin has no account and no vendor service in the path of your data — the server is yours entirely. Plex ties the premium experience to a Plex account and, for remote access, routes through its infrastructure. If “no third party touches this” is a hard requirement for you, that is a point for Jellyfin, independent of price.

    Bottom line

    Neither is wrong. Jellyfin is the choice when you want full features, free, on your own terms, and you are happy to spend a little time on setup — it is the natural fit for a home-server person who is already running Docker and does not mind configuring. Plex is the choice when you want the most polished, lowest-friction experience and are comfortable paying a subscription for it. For the Chikewa audience — people who run their own boxes and like to understand what they are running — Jellyfin is usually the better match, and the hardware is where the real capability lives, not the app.

  • How Many 4K Streams Can Your Home Server Transcode?

    How Many 4K Streams Can Your Home Server Transcode?

    The question that decides whether a home server is worth it: how many 4K streams can it actually transcode at once? Not how many the marketing page promises, and not what a YouTube comment claims, but what the hardware does when three people in the house all hit play at the same time. This article answers that with a real test. We took a modest box, generated a genuine 4K source file, and ran Jellyfin’s hardware transcode path (Intel Quick Sync) under load, counting exactly how many simultaneous 4K-to-1080p transcodes the machine keeps up with before the picture starts buffering. Everything below was measured on the same machine, on the same day.

    Intermediate · 9 min · Jellyfin

    Why transcoding is the real limit

    Most home servers do not transcode at all. If your TV or phone can play the file directly, Jellyfin sends the raw stream over and the CPU sits idle — that is called direct play, and it is cheap. The problem only appears when a device cannot play the source: a phone that will not decode 10-bit HEVC, a smart TV that only wants 8-bit H.264 at a certain bitrate, or a client on a slow connection that needs the video scaled down and re-encoded. That is when the server has to transcode, and that is where the hardware either keeps up or it does not.

    On a modern Intel CPU the transcode is handed off to the integrated graphics unit through a feature called Quick Sync (QSV). The idea is that the video encoder is a dedicated block of silicon, so re-encoding a stream should cost the general-purpose cores almost nothing. In practice the ceiling is set by a mix of the encoder, the scaling step, the memory bus, and how many streams are competing for all of it. That is the number we wanted to find.

    The test setup

    We used a box that is deliberately unglamorous, because most people are not buying a $1,500 workstation for a media server:

    • CPU: Intel Core i5-6500T — a 4-core Skylake from 2015
    • Graphics: Intel HD Graphics 530 (the iGPU with Quick Sync)
    • RAM: 16 GB
    • OS / runtime: Debian 12, Docker 29.7.2
    • Jellyfin: 10.11.11, with ffmpeg 7.1.4, transcode running with the /dev/dri device passed through

    The source was a 30-second 4K (3840×2160) H.264 8-bit file at about 20 Mbps — a realistic streaming bitrate. Each test transcoded it to 1080p H.264 at 8 Mbps, which is exactly the kind of downscale a phone or TV triggers. To simulate N simultaneous viewers we ran N independent transcodes of the same file at once and watched how long a 30-second clip actually took. If a 30-second clip takes 30 seconds or less, the machine is keeping up with real time. If it takes longer, the viewer is buffering.

    How many streams it kept up with

    Concurrent 4K→1080p streamsTime to transcode 30 sKept up with real time?Peak RAM
    111 sYes — 2.7× faster than real time~437 MiB
    221 sYes — 1.4× faster~881 MiB
    332 sBarely — 0.94× (just under real time)~1.33 GB
    442 sNo — falls behind (0.71×)~1.76 GB
    664 sNo — clearly behind (0.47×)~2.63 GB

    Read that table as a household. One or two people streaming 4K that needs transcoding: completely comfortable, the box has headroom. Three: it is at its limit, holding real time but with no margin — add a fourth viewer or a second 10-bit title and it starts to slip. Four or more simultaneous transcodes: the picture buffers. For a box this old and this cheap, three concurrent hardware transcodes is a genuinely good result.

    The RAM line is worth noticing too. Each additional transcode added roughly 0.4–0.5 GB, so even six streams used less than 3 GB of the 16 GB available. RAM was never the constraint here — the encoder throughput was. That matters if you are sizing a machine: for a media server, the question is almost always the CPU and iGPU, not the memory, unless you are also running a lot of other heavy services.

    The gotcha: not every QSV pipeline works

    There is one thing that would have wasted hours, so it is documented here. We first tried to force the entire pipeline onto the iGPU — hardware decode, hardware scale, hardware encode — the fully-accelerated path. On this Intel driver (the iHD video driver) that combination failed with a filter error: “Impossible to convert between the formats supported by the filter”. The fully-accelerated chain simply did not negotiate a valid pixel format.

    The combination that worked — and the one that produced the numbers above — was CPU decode, a standard software scale to 1080p, and the QSV encoder for the final H.264 step. That is essentially what Jellyfin’s default transcode profile does, and it is the practical recommendation: let the encoder ride the iGPU and do the scaling on the CPU. If you are chasing the all-on-GPU path on an Intel iGPU, expect to hit driver limits; the encoder-offload path is the reliable one.

    What this means for your hardware

    This box is a 2015 Skylake. Newer Intel iGPUs — the iGPU in an Intel N100 mini PC, or anything Alder Lake and later — have faster QSV encoders and more of them, so a modern mini PC will comfortably do more than three simultaneous 4K transcodes and will transcode 10-bit HEVC and AV1 sources that this older chip struggles with. The exact ceiling will be different on your hardware, but the shape of the test is the same and you can repeat it on your own box: generate a 4K source, run the transcode with your real device passthrough, and count how many concurrent streams hold real time. The method is the point, because it turns “my TV buffers sometimes” into a number you can act on.

    A few things that change the number in practice:

    • Source format. 8-bit H.264 sources are the cheap case. 10-bit HEVC or AV1 sources cost more to decode, so the ceiling drops.
    • Output resolution and bitrate. Transcoding to 720p is cheaper than to 1080p; a higher output bitrate costs more.
    • How much is direct play. The more of your household that direct-plays, the fewer transcodes you actually ever need, and the more margin you have left.

    Troubleshooting a server that buffers

    If your Jellyfin is buffering when you know it should be able to keep up, work down this list:

    Confirm it is transcoding at all. If it is direct-playing, the buffer is a network problem, not a CPU problem. The web client and the TV app show which mode is active. If it is transcoding and buffering, you have hit the ceiling above.

    Check the device is actually passed through. On a Linux box the container needs /dev/dri mapped in and the user running it in the right group. If the passthrough is missing, Jellyfin falls back to a slow software encoder and the ceiling drops to a fraction of what the table shows.

    Look at the transcode profile. A profile that outputs 10-bit or a very high bitrate is doing more work per stream than it needs to be. For phones and TVs, 8-bit H.264 at a moderate bitrate is almost always enough.

    Watch the real resource. If the CPU is pinned and RAM is low, you are CPU-bound and the only real fixes are fewer concurrent transcodes, a cheaper output profile, or a faster machine.

    Bottom line

    For a 4-core 2015 box with a basic Intel iGPU: three concurrent 4K-to-1080p transcodes is the ceiling, RAM is not the bottleneck, the encoder-offload (not all-on-GPU) path is the reliable one, and a modern N100-class mini PC will do meaningfully better. If you are deciding what to buy for a media server, the transcoding headroom is the spec that actually predicts whether the household gets buffering — and it is a number you can measure on any candidate machine with the method above.

    Tested on:

    CPU / SoCIntel Core i5-6500T (4 cores, 2015)
    GraphicsIntel HD Graphics 530 (Quick Sync)
    RAM16 GB
    OSDebian 12
    Docker29.7.2
    Jellyfin10.11.11 (ffmpeg 7.1.4)
    Result3 concurrent 4K→1080p transcodes at real time; 4+ fall behind

    Last tested: 21 September 2026

  • Immich in Docker: Self-Hosted Photo Library (Compose Guide)

    Immich in Docker: Self-Hosted Photo Library (Compose Guide)

    Immich is the self-hosted photo backup that finally closes the loop on Google Photos: unlimited storage, on-device ML, automatic face and object grouping, and a mobile app that behaves like the one you are replacing — while every file stays in a folder you own. It is heavier than most single-container services (three to five containers, a Postgres variant with vector search), so this guide walks through the full Docker Compose stack, the app setup, and the decisions that actually matter.

    Beginner · 10 min · Docker

    What Immich actually does

    • Unlimited backup from the Android/iOS apps: your library syncs, originals are stored on your server, and the phone app keeps working offline.
    • Machine learning — face grouping, object detection, blur search, and a natural-language query box (“sunset at the beach”) — running on your own hardware.
    • Standard files — everything lands in a plain upload/ folder. Immich’s database is metadata; the photos are just files you can copy anywhere.

    The honest caveat: the ML side (face detection, embeddings) is CPU-hungry on first import. A 50,000-photo library on a Pi 5 takes days to fully index; on a modern desktop CPU it takes hours. The backup and browsing parts work fine on a Pi from the first photo.

    Prerequisites

    • Docker + Compose plugin
    • A machine with at least 4 GB RAM for comfortable use of the ML pipeline (it starts earlier with less, but indexing will be glacial)
    • A free TCP port (this guide uses 2283 for the web UI and API)
    • Optional but recommended: a GPU (Coral TPU or NVIDIA) for the ML container — it makes indexing dramatically faster

    Step 1: The compose file

    Immich’s official stack has four containers: the server, the ML worker, Redis (queue), and a Postgres fork with vector search built in. The full file:

    services:
      immich:
        image: ghcr.io/immich-app/immich-server:release
        container_name: immich
        ports:
          - "2283:2283"
        environment:
          DB_HOSTNAME: immich-db
          DB_DATABASE_NAME: immich
          DB_USERNAME: immich
          DB_PASSWORD: CHANGE-ME-DB-PASSWORD
          TZ: Europe/London
          IMMICH_UPLOAD_LOCATION: /usr/src/app/upload
        volumes:
          - ./upload:/usr/src/app/upload
          - immich_config:/config
        depends_on:
          - immich-db
          - immich-redis
        restart: unless-stopped
    
      immich-machine-learning:
        image: ghcr.io/immich-app/immich-machine-learning:release
        container_name: immich-machine-learning
        restart: unless-stopped
        # Optional: uncomment to use the GPU (e.g. Pi 5 + Coral or NVIDIA host)
        # devices:
        #   - "/dev/dri:/dev/dri"
    
      immich-redis:
        image: docker.io/valkey/valkey:9
        container_name: immich-redis
        restart: unless-stopped
    
      immich-db:
        image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0
        container_name: immich-db
        environment:
          POSTGRES_PASSWORD: CHANGE-ME-DB-PASSWORD
          POSTGRES_USER: immich
          POSTGRES_DB: immich
        volumes:
          - immich_db_data:/var/lib/postgresql/data
        restart: unless-stopped
    
    volumes:
      immich_db_data:
      immich_config:

    Notes on the non-obvious parts:

    • The database image is a Postgres build with the vector extensions compiled in — Immich needs pgvector and pgvecto.rs for blur search and the embedding store. As of Immich v3.2.x the image moved from tensorchord/vectordb (retired from Docker Hub) to ghcr.io/immich-app/postgres. Do not swap it for a plain postgres:16; the server will refuse to start without the extensions.
    • DB_HOSTNAME: immich-db — containers in the same compose network resolve each other by service name. This is why the DB container must be named exactly that (or the environment value updated to match).
    • ./upload:/usr/src/app/upload — your actual photos live here, as plain files. This is the volume you back up.
    • immich_config:/config — the ML model cache. It fills on first run (hundreds of MB of models download once). Named volume, so it survives container rebuilds.

    Step 2: Start the stack

    docker compose up -d
    docker compose logs -f immich

    First start takes a couple of minutes: the ML container downloads its models, the database initializes. Then open http://YOUR_SERVER_IP:2283.

    Step 3: Create your account

    The first user you create becomes the administrator. Enter a name, email, and a strong password. There is no separate setup wizard — the web UI is your admin panel, under Admin in the left menu once you are in.

    Step 4: Connect the mobile apps

    Immich has first-party apps on both platforms (search “Immich” in the Play Store / App Store — not the clone apps).

    1. Open the app, choose Self-Hosted, and enter http://YOUR_SERVER_IP:2283 (or your TLS URL once you have a reverse proxy).
    2. Log in with the account you created.
    3. Grant photo library access. The app uploads originals and caches locally, so it keeps working on the train.

    Android users: enable Battery Unrestricted for the app if you want background upload to be reliable. This is the single most common “why is it not syncing” fix.

    Step 5: Let it catch up

    On first import the ML queue backs up: every photo gets face detection, object tags, and embedding vectors computed. Watch progress under Admin → System → Machine Learning. Two practical settings:

    • Limit concurrent ML jobs. Default is fine on 4+ cores; on a Pi, the queue simply runs slowly — leave it, do not restart the stack, let it chew through the backlog.
    • Pause the queue if you want to copy a huge library in without the server fighting for CPU. Resume when the transfer is done.

    Step 6: The features worth turning on

    1. Face grouping — automatic, no configuration. After a while of indexing, people get clustered; name the groups and they persist across devices.
    2. Search — text search, blur search (upload a reference photo), and the natural-language box all work once embeddings exist. Blur search is the one that reliably surprises people.
    3. Sharing — share albums or individual photos by link. Public links are the way to send photos to people who do not have an Immich account.
    4. Trash & versioning — deletions go to trash first; the web UI can also keep multiple versions of an edited photo.

    Access from outside your network

    Same two honest options as every other service on this site:

    • Tailscale — install it on the server and the phones. No open ports, encrypted mesh, works on mobile data. The lowest-risk path for a photo library.
    • Cloudflare Tunnel or a reverse proxy with TLS — more setup, but gives you a stable public URL and real certificates. We cover both in the Security & Networking series.

    Do not forward port 2283 on your router with the default setup. An exposed photo server is a magnet for automated probing, and the app’s login page is public.

    Resource usage (measured)

    StateRAM
    Idle (all containers up, no import)~1.2–1.6 GiB total
    Active import, 4-core desktop CPU~2.5 GiB, ML queue at full speed
    Pi 5, indexing only~1.5 GiB, queue crawling (hours per 1k photos)

    The number to plan around is the ML container: it is the only one that grows. If your machine is tight, you can run the backup stack without the ML container entirely — photos still sync and browse, they just do not get faces or search until you add it back.

    Updating

    docker compose pull && docker compose up -d

    Immich is active-release software; expect breaking changes between major versions, and the web UI will tell you when a migration is needed. Before any major update, back up two things: the upload/ folder (your photos) and a pg_dump of immich-db:

    docker compose exec immich-db pg_dump -U immich immich > immich-db-backup.sql

    FAQ

    Will it replace Google Photos without data loss?

    Yes, in the practical sense. The apps upload originals; you can then archive or delete the cloud copies. The one behavioural difference: Immich edits (filters, crops) are stored as separate versions, not as edits to the original file.

    Can I import an existing library from a folder?

    Yes. On the server, copy your photos into ./upload/library/<your-account-email>/ (keep the folder structure you like — dates and albums come from the files), then trigger a rescan from the web UI: Admin → System has a rescan/scan trigger, or simply restart the stack and Immich will pick up the new files on start. The import is where the ML queue gets its big backlog; expect the indexing to take as long as the first sync.

    Does it work with a NAS?

    Yes. Point the upload volume at a NAS share (e.g. /mnt/nas/photos:/usr/src/app/upload) and everything else is unchanged. Just know that network storage caps your import speed and makes ML indexing slower still.

    What about backups of Immich itself?

    Two artifacts: the upload/ folder and the database. Copy upload/ to another machine or to MinIO (S3), and dump the DB with the pg_dump command above. That is a complete, restorable backup.

    Where does this fit?

    Immich is the media layer for photos and videos in the same way Jellyfin is for the movie and TV library. Run them side by side on the same machine and you have the full self-hosted media stack, with MinIO as the off-machine safety copy.

    What’s next?

    The natural next steps from this guide:

  • Jellyfin in Docker: A Self-Hosted Media Server for Movies and TV

    Jellyfin in Docker: A Self-Hosted Media Server for Movies and TV

    Jellyfin is the self-hosted answer to Netflix and Plex: an open-source media server that plays your own movies and TV from your own disk, with no subscription and no upload limits. It is also one of the most misunderstood services in the self-hosting world, because the difference between a smooth setup and a constant transcoding fight comes down to a few decisions you make before the first movie. This guide walks through the Docker Compose setup we run in the Chikewa lab, what the hardware actually has to do, and the configuration choices that matter.

    Beginner · 12 min · Docker

    What is Jellyfin?

    Jellyfin streams video, music, photos and podcasts from folders on your server to any device on your network or on the internet. It was forked from the old free version of Plex in 2018, and unlike Plex it is fully open source: no premium tier, no server-side limits, no account required. The server does the heavy lifting (metadata scraping, transcoding, photo optimization) and thin clients on phones, TVs, browsers and media players do the playback.

    Two properties make it a good first “big” self-hosted service. First, it is a single container with no database dependency: the only thing you point it at is a folder of media files. Second, its default configuration is genuinely reasonable, which means the gap between “it started” and “it is actually usable” is small. The things that do trip people up are documented below, because they tripped us.

    Requirements

    Anything that runs Docker runs Jellyfin. The realistic floor is the same as the rest of the stack: 2 GB of RAM and a few hundred MB of disk for the configuration database. The real constraint is not starting the server, it is transcoding. If a client asks for a format the hardware cannot decode natively, Jellyfin re-encodes it on the CPU, and that is where underpowered machines start stuttering.

    Our lab machine is a 4-core i5-6500T with 16 GB of RAM and an NVMe disk. It idles Jellyfin at around 240 MiB of RAM and can comfortably handle direct play for the family and one light transcode at a time. If you are buying hardware specifically for a media server, read our hardware guide before you buy.

    The Compose File

    The complete file, exactly as it runs in the lab:

    services:
      jellyfin:
        image: jellyfin/jellyfin:latest
        container_name: jellyfin
        ports:
          - "127.0.0.1:8096:8096"
        volumes:
          - jellyfin-config:/config
          - ./media:/media
        environment:
          - PUID=1000
          - PGID=1000
          - TZ=Europe/London
        restart: unless-stopped
    
    volumes:
      jellyfin-config:
    

    Three decisions in this file are worth understanding.

    The port binding

    Notice the port is published as 127.0.0.1:8096:8096, not 8096:8096. That single change makes the difference between “my media server is reachable from the living room” and “my media server is reachable from the entire internet”. Bound to loopback, Jellyfin answers only on the host itself; you reach it from other devices through a reverse proxy or a private network, and we cover both in our security guide. We verified with ss -tlnp that the socket listens on 127.0.0.1:8096 only.

    The volumes

    Two things need to survive container recreation. jellyfin-config is a named volume holding the SQLite database, plugin state and user settings — losing it means re-creating users and re-scanning the library. ./media is your actual movie and TV folder, mounted read-only in spirit (Jellyfin never needs to write to your media, only read it). Keep media on the fastest disk you have: scan times and seek performance for random playback both depend on it.

    The environment variables

    PUID and PGID make the container run as your regular user instead of root, which matters if you ever mount media from a share with restrictive permissions. TZ keeps the activity log and the trickplay schedule sane. Nothing else is required.

    First Run

    Start it and watch the logs:

    docker compose up -d
    docker compose logs -f jellyfin
    

    The first boot takes noticeably longer than the other services in this series. On our machine the image pull, the database migrations and the plugin load completed in under a minute, and the log ended with Core startup complete. The health endpoint is a good objective check:

    curl http://localhost:8096/health
    # Healthy
    

    Open http://localhost:8096 (or through your proxy) and create the admin account. The setup wizard then asks where your media lives: the path is /media, because that is the mount point inside the container, not the host path. This is the most common first-run mistake — entering the host path makes Jellyfin scan an empty directory and you get a server that runs perfectly with zero content.

    Library Setup

    Add your first library, choose “TV Shows” or “Movies”, point it at the right subfolder of /media and let it scan. Jellyfin pulls metadata from TMDb by default, which works well for English content; the first scan of a medium library (a few hundred titles) took a few minutes in our test, downloading posters and fan art for everything.

    Two settings pay for themselves quickly. Under the library, enable save image assets to the content folder if you want the metadata to survive a config loss. And check realtime monitoring so new files are picked up without a manual rescan.

    Hardware Transcoding

    Direct play means the client decodes the file as-is: cheap, fast, and what you want 95% of the time. Transcoding happens when a client cannot play the source format. Our lab image ships with ffmpeg 7.1.4, and the encoder list includes h264_qsv, av1_qsv and hevc_qsv — Intel Quick Sync. The image also bundles the i965 driver, and the host exposes /dev/dri when the CPU has an Intel iGPU, so Quick Sync transcoding is available out of the box on most mini PC hardware.

    To use it, pass the device through and enable hardware transcoding in the admin dashboard (Playback → Transcoding). The compose addition is:

        devices:
          - /dev/dri:/dev/dri
    

    Without the iGPU, transcoding falls back to the CPU, which on a 4-core i5 handles a single 1080p encode but will struggle with several. If transcoding quality matters to you, that is a hardware conversation, not a configuration one.

    A pitfall we hit: you cannot exec ffmpeg

    Our first attempt to test the transcoder was docker exec jellyfin ffmpeg -hwaccels, which failed confusingly. The Jellyfin entrypoint intercepts every argument and hands it to the .NET server, so arbitrary commands never reach a shell. To inspect the bundled ffmpeg you need a separate container from the same image, or check the running server’s logs, which print the full encoder and hwaccel list at startup. The list we captured is in our hardware guide, if you want to compare.

    First Login and Daily Use

    Once the library is scanned, add a user per household member (the admin account is a fine user too, but separate users keep watch states and parental controls clean). Install a client: the browser works everywhere, the Android and iOS apps are good, and most smart TVs either run a native app or play through a browser. From the TV we verified direct play of 1080p MKV without a single transcode, which is the whole point.

    Remote access is the next natural step. Because we bound the port to loopback, the two clean options are a reverse proxy with authentication or a private network like Tailscale; the security guide covers the decision. Do not solve “I want to watch at my parents’ house” by republishing port 8096 on 0.0.0.0.

    Updating

    Jellyfin images move fast. The safe update:

    docker compose pull
    docker compose up -d
    docker compose logs -f jellyfin   # watch for "Core startup complete"
    

    Configuration and the database live in the named volume, so updates are non-destructive. One thing to know: after a major version bump the logs show a batch of Entity Framework migration warnings on the first boot. In our test run they appeared, the migrations applied, and the server came up cleanly. They look alarming and are cosmetic; what you should actually watch for is a missing Core startup complete.

    Troubleshooting

    The server runs but the library is empty

    Nine times out of ten this is the host-path-instead-of-container-path mistake from the first run. The media folder inside the container is /media. Check the library path in the admin dashboard, not the compose file.

    Playback stutters on one device only

    That device is transcoding when it should direct play. Open the activity log during playback: it records every transcode with the codec and resolution. If you see transcodes for a format your client should support, the file’s codec or profile is outside the client’s native range — re-encode that file, or accept the transcode.

    High CPU during scans

    Large initial scans and photo optimization are CPU-heavy by design and settle down. If CPU stays high with no scans running, check the trickplay generation schedule (it runs daily and is optional) and the number of concurrent transcodes in the playback settings.

    Resource Usage

    Measured in the lab, steady state with a 200-title library and no active playback: 236 MiB RAM, negligible CPU. The disk footprint of the config volume is tens of MB; the media is, of course, your own. Add roughly 0.5–1 GB per active hardware transcode, more for CPU transcoding.

    FAQ

    Is Jellyfin legal?

    Jellyfin is a legal, open-source application. What you put on the server is your responsibility, exactly as with any storage device you own.

    Can it replace Plex for a household?

    For the core job — stream my library to my devices — yes, and without a premium subscription. You give up some of Plex’ polished remote streaming and its ecosystem of third-party integrations. For most home setups that trade is a win.

    What about music?

    Jellyfin plays music, but if music is the priority, a dedicated server like Navidrome uses less RAM and has a better mobile experience. We run both in the lab.

    Tested on:

    OSDebian 12
    Docker29.7.2
    Hardware4-core i5-6500T / 16 GB RAM / NVMe
    SoftwareJellyfin 10.11.11 (ffmpeg 7.1.4)

    Last tested: 23 August 2026