Search “emby vs jellyfin” and you get twenty pages of the same comparison chart. Both index your movies. Both pull posters. Both stream to a phone. Congratulations, you’ve learned nothing. Of course the basics match. These two shared a codebase until 2018, and nobody was ever arguing about whether they could display a poster.
The real split is money. One of these servers hands you hardware transcoding, Live TV, and unrestricted mobile playback for nothing. The other puts most of it behind a subscription, because what your life needs is one more of those. So the decision is narrower than the charts pretend. Is Emby worth renting?
I’ve run both on the same machine, against the same library folders, on the same TVs. Here’s what decided it, and what I’d make you check before you commit to either one.
I run Jellyfin 10.11.11 in an unprivileged Proxmox LXC on a Z690 motherboard with an i5-12600K CPU, under Proxmox VE 9.2.10. The container gets 4 cores, 4 GB of RAM, and /dev/dri passed through for Intel QuickSync. Emby got a real trial on that exact hardware rather than a five-minute skim: installed alongside Jellyfin, pointed at the same library folders, same clients, same players.
I’ll be clear about what that means, though. Jellyfin is what I run in production and have for years. Emby is what I ran beside it to make an informed decision.
The Short Answer, Before the Details
The decision, without the reasoning:
- You need hardware transcoding and your box has an Intel iGPU or a spare GPU. Run Jellyfin. Emby puts hardware acceleration behind Premiere. If you bought that chip for QuickSync, Premiere is a monthly fee to use a thing you already own.
- You want Live TV and DVR without another bill. Run Jellyfin. Live TV and DVR sit in Jellyfin’s base feature set. Emby’s full-featured equivalent sits on the Premiere side of the line, on Emby’s own list of what activation unlocks.
- Your household is non-technical and the living-room app experience decides whether they use it at all. Emby is worth the trial. Its client apps are noticeably more finished, and that’s not nothing when the people using the server aren’t the person who built it.
- You’re browser-only, at home, one stream at a time. Emby’s free tier will probably cover you. Test the exact clients you own before you believe that.
- You care about not depending on a vendor account. Jellyfin. It’s fully open source and needs no account for normal operation, which the Jellyfin documentation spells out plainly.
Everything after this is why.
That first bullet assumes you already own an Intel iGPU. If you’re still shopping for the box, a NUC-class mini PC is the cheap way to get QuickSync into the house.

What the 2018 Fork Actually Left Behind
Jellyfin forked from Emby in 2018, when Emby’s source went closed. Eight years later the family resemblance is still obvious once you get past the marketing. Library folder conventions, metadata handling, the general shape of the admin dashboard: drive one and you can drive the other without reading a word of documentation.
That resemblance pays off during a migration. When I moved between them, the library layout carried over untouched and the folder naming rules I’d already beaten into shape kept working. Nothing to redo.
What diverged was licensing, account dependence, and who gets to charge you. Emby became a commercial product with a paid unlock path. Jellyfin became a community project with nothing to unlock. Every practical difference in a 2026 jellyfin vs emby decision falls out of that one split.
There’s a fossil of the shared history sitting in your *arr stack right now. Open Radarr or Sonarr, go to Settings, then Connect. The notification target is called Emby/Jellyfin, singular, because one endpoint still feeds both. Eight years of divergence and the *arr apps still can’t be bothered to tell them apart. That shortcut hides a real trap, and I’ll come back to it in the troubleshooting section.
The Paywall Map: What Emby Premiere Gates
This is the table that should be at the top of every emby vs jellyfin article instead of a codec grid.
| Capability | Jellyfin | Emby |
|---|---|---|
| Hardware transcoding (QuickSync, NVENC, VA-API, AMF) | Free | Premiere |
| Live TV and DVR | Free | Premiere for the full feature |
| Native mobile and TV apps, unrestricted | Free | Free tier commonly limited; Premiere or an app unlock to remove the limit |
| Browser playback | Free | Free |
| Vendor account required | No | Emby Connect for some features and support flows |
| Plugins and customization | Open, community-driven | Curated and more productized |
| Source code | Fully open source | Closed since the 2018 split |
Sourcing note: The Premiere column is Emby’s own doing, and Emby’s Premiere page is the authoritative list of what activation unlocks. I’ve checked that list against what I saw running both servers, but vendors move lines whenever they feel like it. If you’re about to spend money on the strength of one row in my table, go read theirs.
Two honest caveats on top of that.
First, the behavior of Emby’s free tier on native apps is the most contradictory thing I hit while researching this post. The 2026 roundups say browser playback runs free while the native apps cut you off after a few minutes unless you subscribe, and that this is how most people find out it exists. I won’t dress that up as a citation. I couldn’t pin it to one authoritative page and the write-ups don’t agree with each other, which tells you something all by itself. Emby’s own documentation is the authority.
Better still, don’t read anything. Install Emby free, open it on the actual TV in your actual living room, and press play on a real file. Ten minutes and you’ll know more than every roundup combined, mine included.
Second, you’ll see “Emby free is good enough” repeated all over the internet. The more technical write-ups disagree, and so did my trial. Free is fine for kicking the tires. Nearly everything people mean when they say “home media server” lands on the Premiere side of the line.
Hardware Transcoding Is Where Emby vs Jellyfin Splits for Good
Here’s my decision, stated plainly. My server is an Intel box. QuickSync is the entire reason that hardware got bought. Emby puts hardware transcoding behind Premiere, which means the one feature my build exists to perform is the one I’d rent forever, on a machine I already paid for to do that exact job.
Jellyfin handed it over for free. That ended the evaluation.
This bites hardest on modest hardware, which describes most homelabs. Nothing is gated, so Jellyfin is far easier to justify on aging Intel iGPUs, small NUCs, and repurposed mini PCs where the entire point was offloading CPU work on the cheap. Buying a cheap mini PC and then subscribing to use its video engine is a special kind of backwards.

Passing an Intel iGPU Into an Unprivileged LXC
Fair warning: this subsection goes deeper than the rest of the post. User namespaces and ID mapping are the corner of Proxmox where confident people go quiet. If you’re not passing a GPU into an unprivileged container, skip ahead. No hard feelings.
Installing Jellyfin is the easy half. The fiddly half is handing a GPU to an unprivileged container without quietly reopening the hole that made it unprivileged in the first place. Start by looking at the render node on the Proxmox host:
ls -l /dev/dri
# crw-rw---- 1 root video 226, 0 Aug 9 09:12 card0
# crw-rw---- 1 root render 226, 128 Aug 9 09:12 renderD128
Before that config makes any sense, here’s the part most guides skip straight past.
Linux doesn’t care about usernames. It cares about numbers. Every file and every device node carries a numeric owner (the UID) and a numeric group (the GID). Those friendly names in ls -l are a lookup against /etc/passwd and /etc/group on whatever machine you happen to be standing on. The host keeps its own copy of those files. The container keeps another. Same number, different name, and nothing anywhere warns you they disagree.
That’s fine right up until the container goes unprivileged. An unprivileged LXC runs inside a user namespace, which shifts every ID in the container by a fixed offset out on the host. Container root, UID 0, shows up on the host as UID 100000. That shift is the whole security benefit: if something claws its way out of the container as root, the kernel sees an unprivileged nobody who owns nothing worth having.
The shift is also what breaks your GPU. On the host, /dev/dri/renderD128 belongs to group render, GID 104. Inside the container, GID 104 lands on host GID 100104, and host GID 100104 owns exactly nothing. So Jellyfin sits there staring at a device node it can see perfectly well and eats a permission denied on every single open. The device is present. The device is listed. The device is useless.
Note the group on renderD128 and its GID. On my host the render group is GID 104, and that number has to survive the trip into the container’s user namespace intact. These are the relevant lines from my Jellyfin container config at /etc/pve/lxc/102.conf:
unprivileged: 1
lxc.idmap: u 0 100000 1000
lxc.idmap: u 1000 1000 1
lxc.idmap: u 1001 101001 64535
lxc.idmap: g 0 100000 104
lxc.idmap: g 104 104 1
lxc.idmap: g 105 100105 896
lxc.idmap: g 1001 1001 1
lxc.idmap: g 1002 101002 64534
lxc.mount.entry: /dev/dri/ dev/dri/ none bind,optional,create=dir
lxc.cgroup2.devices.allow: c 226:128 rwm
lxc.cgroup2.devices.allow: c 226:0 rwm
lxc.mount.entry: /media/Storage/Movies media/Movies none bind,create=dir,ro 0 0
lxc.mount.entry: /media/Storage/Shows media/Shows none bind,create=dir,ro 0 0
Every lxc.idmap line reads the same way once you know the shape of it:
lxc.idmap: <u|g> <first ID inside container> <first ID on host> <how many IDs>
So the GID rows above say this:
| Line | Container GIDs | Host GIDs | What it does |
|---|---|---|---|
g 0 100000 104 | 0-103 | 100000-100103 | shifted, unprivileged |
g 104 104 1 | 104 | 104 | render group, mapped straight through |
g 105 100105 896 | 105-1000 | 100105-101000 | shifted, unprivileged |
g 1001 1001 1 | 1001 | 1001 | media group, mapped straight through |
g 1002 101002 64534 | 1002-65535 | 101002-165535 | shifted, unprivileged |
Read g 104 104 1 carefully, because it is the entire trick. One GID, 104, mapped to the same number on the host, exactly one ID wide, while everything on either side of it shifts up into the 100000+ range. That one line is what lets the container open the render node. The u rows do the same dance around UID 1000 so my media user lines up on both sides of the boundary.
Two things bite people here, and they bite hard.
First, the ranges have to tile the whole 0-65535 space with no gaps and no overlaps. Add my GID counts: 104 + 1 + 896 + 1 + 64534 = 65536. Drop a single ID and the container refuses to start. The error will not tell you which one you dropped. It never does.
Second, the host has to grant root permission to use the IDs you mapped straight through. Those go in /etc/subuid and /etc/subgid on the Proxmox host. Forget them and pct start fails, and the message won’t mention subuid either:
# /etc/subuid
root:1000:1
root:100000:65536
# /etc/subgid
root:104:1
root:1001:1
root:100000:65536
If counting ID ranges by hand sounds like a great way to spend an afternoon, by all means. I got tired of it and built a calculator instead.
Feed it the UIDs and GIDs you want passed through from the host and it returns the full lxc.idmap block for your container config, with the tiling worked out and the matching /etc/subuid and /etc/subgid lines you’ll need on the host.
The media binds are read-only on purpose. Jellyfin has never once needed write access to my library, and a read-only mount deletes an entire category of accident before it can happen. There’s a price: “save artwork into media folders” and NFO writing both have to be switched off in the library settings, so artwork lives in Jellyfin’s internal metadata store instead. I’ll take that trade every time. A media server that physically cannot rename my files is a media server I sleep well next to.
I followed the Proxmox forum guide for this combination, and wrote up my own QuickSync-in-an-unprivileged-LXC walkthrough if you want every step.
Proving the GPU Is Actually Doing the Work
A checkbox in the dashboard is not proof of anything. It’s a checkbox. It means someone clicked it. Go verify from inside the container:
# device visible and readable by the jellyfin user?
sudo -u jellyfin ls -l /dev/dri/renderD128
# does ffmpeg know about the QSV path?
/usr/lib/jellyfin-ffmpeg/ffmpeg -hwaccels
Then start a transcode and watch the log. Jellyfin writes the exact ffmpeg command line it ran to /var/log/jellyfin/:
grep -i "hwaccel\|qsv\|vaapi" /var/log/jellyfin/jellyfin*.log | tail -20
If you see -hwaccel qsv or a VA-API device argument, the iGPU is carrying the load. If the command line has no acceleration flags at all, you’re transcoding on the CPU and your cores will tell you so.
Emby vs Jellyfin Over Three Years, Not Monthly Price
Monthly pricing is designed to look small. That is its entire job. Stretch it across the life of a server and the number stops flattering everybody involved.
The figures I keep seeing in 2026 roundups are roughly $4.99 monthly, about $54 yearly, or around $119 lifetime for Emby Premiere. Those roundups are third-party, they lag, and they don’t all agree. Treat the table below as the shape of the decision rather than a quote. If you hand over money on the strength of my table instead of their page, that one’s on you.
| Option | 3-year cost |
|---|---|
| Jellyfin | $0 |
| Emby Premiere, monthly | roughly $180 |
| Emby Premiere, annual | roughly $162 |
| Emby Premiere, lifetime | roughly $119 one time |
Emby’s own pricing page holds the only numbers that count, and it’s the page to read before you pay. The takeaway survives any price change: Emby has a real paid tier and Jellyfin has nothing to sell you.
Now weigh that against what the money buys. If the lifetime option kills a recurring bill and the apps genuinely make your household happier, $119 once is defensible on a box you’ll run for a decade. I’d sign that. But if you’re buying Premiere purely to unlock hardware transcoding on an iGPU sitting in your house right now, you’re renting a feature you already bought.
Where Emby Earns Its Money: The Client Apps
Time to give Emby its due, because this is where the free option loses and I’m not going to pretend otherwise.
The client apps were plainly better on the living-room TV. Navigation felt finished. Artwork loaded when it was supposed to. The whole thing behaved like a commercial streaming app instead of a very capable open-source project that occasionally remembers it’s a very capable open-source project. Jellyfin’s strength is everything being available with nothing gated. The price of that is a client experience that swings device to device even when the server underneath is flawless.
That unevenness is a real cost, and here’s the uncomfortable part: you’re not the one who pays it. Whoever picks up the remote pays it. They don’t care which fork won an argument on GitHub in 2018, they care that the poster grid stuttered again. If your server exists to serve a household, put the apps ahead of the philosophy and test on the exact hardware in your house. Your TV’s built-in OS. Your streaming stick. Your phone. Not the ones in somebody’s review.
One thing shrinks the gap a lot: a capable client that direct-plays your files makes the server’s transcoding behavior irrelevant, because nothing gets transcoded at all. Broad codec and HDR support on the player turns a whole genre of “server problems” into non-events on either platform. Fix the endpoint and you fix arguments you were about to have about the server.

Remote Access: Emby Connect vs Running Your Own Front Door
Emby hands you Emby Connect as a convenience layer. Jellyfin expects you to bring your own remote access, usually a reverse proxy or a VPN. The philosophical difference is real and people have written a lot of forum posts about it. The practical difference is one afternoon.
My lab publishes Jellyfin at flix.example.com with no inbound port forwards on the home router at all. The path looks like this:
- A cheap VPS runs a public Caddy instance and a WireGuard server.
- The home router is a WireGuard client that dials out to the VPS and advertises the LAN.
- Public Caddy proxies
flix.example.comover the tunnel to the Jellyfin container at192.168.1.4:8096, in a single hop with streaming-tuned timeouts. - An internal Caddy serves the same hostname on the LAN with a real wildcard certificate, so the URL is identical inside and outside the house.
Nothing at home listens on the public internet. The attack surface is the VPS, which runs a default-drop firewall allowing only SSH, HTTP, HTTPS, and WireGuard, with Fail2Ban watching the SSH port and the Jellyfin login page.
Whichever server you pick, put fail2ban in front of it. Mine lives in /etc/fail2ban/jail.local, watches Jellyfin’s own auth log on ports 8096,8920, hands out 20-minute bans, keeps the LAN in ignoreip, and pushes a notification to my phone on every ban. Do I need a phone alert every time a bot in another hemisphere tries admin/admin? No. Do I like knowing? Yes. Exposing a media server to the internet with no brute-force protection is the homelab equivalent of leaving the garage door open with the keys in the car.
Metadata, Plugins, Live TV, and DVR
Metadata and library management are close enough to call even. Both fetch posters. Both build collections. Both care enormously more about your folder naming than about which logo is on the dashboard. When your metadata comes back wrong, go fix the naming before you go blame the software. It’s the naming. It’s almost always the naming.
Plugins favor tinkerers, and that favors Jellyfin. It’s the more open and modifiable platform. Emby’s ecosystem is more curated and more productized. Neither one wins on principle: a curated ecosystem hands you fewer sharp edges, an open one hands you the ability to fix the sharp edges yourself. Pick the failure mode you’d rather live with.
Live TV and DVR is the second clean split after transcoding. Jellyfin ships it. Emby’s full-featured equivalent sits on Emby’s own list of Premiere items. If a tuner and an EPG are central to your build, this decision writes itself and you can stop reading.
Running Both Against One Library Without Wrecking It
You can point both servers at the same media files. That’s what I did, and it’s the only honest way to compare them. Reading feature tables is not a comparison. Two rules kept it from ending badly.
Mount the library read-only for the server you’re evaluating. My binds are read-only for both, which means neither server can rename, move, or delete a file. Evaluate with a read-only mount and the worst case is a bad metadata match in a database you’re going to throw away anyway.
Expect to rebuild state, not migrate it. Media files are shared. Users, watch history, and some metadata state are not. Plan on:
- Recreating user accounts on the new server.
- Losing or manually rebuilding watch history (Trakt plugin can help with this).
- Re-signing-in every client device.
- Revalidating remote access and any reverse proxy rules.
- A full metadata rescan, which on a large library takes real time.
Here’s the sequencing that saved me grief: keep the current server running, stand the challenger up beside it on a different port, and don’t decommission anything until the household has used the new one for a couple of weeks. Media servers are judged by the people who watch on them, and their verdict takes longer than yours.
For those couple of weeks you need a client that isn’t the main TV, because nobody in the house will tolerate you A/B testing the living room every night. A cheap stick on a spare set gives you somewhere to install both apps and compare them.

Troubleshooting the Failures You’ll Actually Hit
Before the war stories, I’ll be straight about where they come from. Everything in this section is Jellyfin.
That’s not because Emby ran spotless. It’s because Jellyfin is what I run in production, and Emby is what I stood up next to it to make a call. Those two situations produce completely different kinds of knowledge.
A side-by-side trial tells you the truth about app polish, the paywall map, and whether your TV can play your files. What it can’t hand you is a database bug that takes two repair passes to clear, because that species of problem needs months of uptime to grow. So take this section for what it is. The scar tissue is Jellyfin-shaped because my production server is Jellyfin. And if you find an emby vs jellyfin comparison with perfectly symmetrical incident reports on both sides, ask how long the author ran each one. Then ask why the incidents came out so tidy.
None of what follows appears in a feature grid. All of it cost me an evening.
Transcoding Falls Back to CPU Without Telling You
Playback stutters, and only during a transcode. The host CPU pegs. Meanwhile the dashboard still shows hardware acceleration cheerfully enabled, because the dashboard is not where truth lives.
Grep the log for the ffmpeg command line exactly as above. No acceleration flags means no acceleration, whatever the UI wants to tell you.
Four things cause this. The render device isn’t readable by the service user. An unprivileged container is missing the render-group identity mapping (see the previous section, and my condolences). The driver packages never got installed inside the container. Or a client is demanding subtitle burn-in.
Burn-in is the sneaky one. It can shove a single stream onto a path your hardware pipeline can’t handle while acceleration keeps working perfectly for everything else in the library. You’ll swear the GPU died. The GPU is fine. One client asked for something unreasonable and Jellyfin quietly obliged.
The Library Goes Stale After a Storage Reboot
My media lives on a separate storage server and reaches the media container as read-only binds over NFS. Two consequences, and both hit Emby exactly the same way. These are NFS problems wearing a media-server costume, and NFS does not care what you run on top of it.
A storage reboot breaks the container. The bind mounts pin stale NFS handles. Restarting the service accomplishes nothing, as you’ll discover after restarting the service twice. Reboot the container:
pct reboot 102
inotify does not cross NFS. Imports written by Radarr and Sonarr from a different host are invisible to the realtime library monitor. Nothing is broken here. The monitor is watching a filesystem that never bothers to tell it anything. Two fixes, and you want both: point the *arr apps at the media server’s “Update Library” hook, and set a full library scan every 6 hours as a backstop under Dashboard, Scheduled Tasks, Scan Media Library. Belt and suspenders, because the hook will eventually fail on a night you’re not watching.
That hook is where the shared fork history bites. In Radarr and Sonarr, the connection type is Emby/Jellyfin, and two settings decide whether it works:
- Use SSL must be ON if you’re pointing at port 443 through a proxy. If it’s off, the *arr app sends plain HTTP to 443 and gets a 400 BadRequest.
- Send Notifications must be OFF when the target is Jellyfin. That toggle uses an Emby-only endpoint, and Jellyfin rejects it. “Update Library” is the part that matters.
Success is silent. A working notification writes nothing to the *arr log at Info level and only failures get recorded, a design decision I have feelings about. So don’t go looking for proof on the sending side. Confirm from the other end:
journalctl -u jellyfin | grep "will be refreshed"
You should see the library named and refreshed roughly 60 seconds after the import. That gap is the debounce window, not a failure.
Stale Items Play the Wrong Stream Layout
If an item plays but the subtitle or audio behavior is wrong after a file was replaced, the cached stream layout is stale. A normal metadata refresh won’t fix it, because the default refresh mode doesn’t re-probe the streams at all. You want Refresh metadata, then Replace all metadata. Anything less and Jellyfin keeps handing your clients the layout of a file that’s no longer on disk.
Frequently Asked Questions
โค Is Jellyfin really free forever?
โค Do I need Emby Premiere for hardware transcoding?
โค Can I migrate my Emby library to Jellyfin without rebuilding everything?
โค Which is better for a low-power mini PC or an old NAS?
โค Is Emby Connect the same thing as a reverse proxy?
The Verdict
Choose Jellyfin if you want the best three-year value and maximum self-hosted control. It costs nothing, gates nothing, and asks for no vendor account. On Intel hardware with QuickSync it does the one job most homelab media servers exist to do, for free. My own build landed there for exactly that reason and hasn’t moved since.
Choose Emby if you want its client polish and you’re at peace with paying to unlock the rest. If your household judges the server by the living-room app, and $119 once buys years of nobody complaining at you during dinner, that’s a rational trade. Pay for the apps. That’s the honest reason, and it beats any feature table anyone will show you.
What I’d actually go do:
- Install both. Point them at the same read-only library mount and run a real transcode on the TV your household uses. That single test answers more than this article can, and I wrote the article.
- If you land on Jellyfin and your host has an Intel iGPU, set up QuickSync in an unprivileged LXC next. It’s the largest performance win available to a media server, and now you know what the ID mapping is doing.
- Put a reverse proxy and fail2ban in front of anything before you expose it. Not after. Before.
- And when the metadata comes back wrong, go look at your folder names first. You already know how that sentence ends.
Sources
- Jellyfin documentation: https://jellyfin.org/docs/
- Emby Premiere page (emby.media/premiere.html), the authoritative list of Premiere-gated features and current pricing
- Proxmox VE documentation: https://pve.proxmox.com/pve-docs/
- Proxmox forum guide, Jellyfin: Remote Network Shares + HW Transcoding with Intel’s QSV + Unprivileged LXC: https://forum.proxmox.com/threads/guide-jellyfin-remote-network-shares-hw-transcoding-with-intels-qsv-unprivileged-lxc.142639/

