For whatever it is worth to you I’ve hardened my jellyfin docker compose this way. I’m obscuring some things in this that are local to my network.
The container is read only. It mounts the media read only. It should be fairly isolated from getting privileges it shouldn’t have. The only things lost in this setup are the ability to delete media from a jellyfin client (something I never do) and the ability to manipulate files directly stored with the media. I use bazarr for subtitles so don’t need jellyfin to download them and I don’t keep trickplay or any other jellyfin metadata related files alongside the media. I feel pretty confident with this that even if the jellyfin codebase had a serious 0 day that the worst I’d lose is metadata and server setup, and important config files I have backing up to another machine.
I’m sure there’s things I’ve overlooked with this setup so I’m very open to being told there’s a better way to do something.
services:jellyfin:image:jellyfin/jellyfin:latestcontainer_name:jellyfinrestart:unless-stoppedmac_address:OBSCURED# see macvlan note belowhostname:jellyfinuser:${PUID}:${PGID}group_add:-"992"# my render group idsecurity_opt:-no-new-privileges:truecap_drop:-ALLread_only:truetmpfs:-/tmp:exec,nosuid,nodev# exec some plugins can write to /tmp, no-nosuid,nodev for securitynetworks:macvlan:ipv4_address:OBSCUREDhealthcheck:test: [ "CMD", "curl", "-fsS", "http://127.0.0.1:8096/health" ]
interval:1mtimeout:10sretries:3start_period:30senvironment:-TZ=${TZ}-JELLYFIN_PublishedServerUrl=OBSCURED-JELLYFIN_DATA_DIR=/config/data# consolidates these dirs under /config, migrated from linuxserver.io setup-JELLYFIN_CONFIG_DIR=/config-JELLYFIN_LOG_DIR=/config/log-JELLYFIN_CACHE_DIR=/config/cache-HOME=/configvolumes:-./jellyfin:/config-${MEDIA}:/media:rodevices:-/dev/dri/renderD128:/dev/dri/renderD128# isolate to just the renderD128 devicedevice_cgroup_rules:-'c 226:128 rw'# further limits access for the renderD128 devicenetworks:# I went with macvlan to preserve mdns for local discovery, otherwise you need to use host level networkingmacvlan:driver:macvlandriver_opts:parent:OBSCURED# Replace with host network interface name (find via: ip route | grep default)ipam:config:-subnet:OBSCURED/24# Your local LAN network CIDRgateway:OBSCURED# Your local router IP address
For whatever it is worth to you I’ve hardened my jellyfin docker compose this way. I’m obscuring some things in this that are local to my network.
The container is read only. It mounts the media read only. It should be fairly isolated from getting privileges it shouldn’t have. The only things lost in this setup are the ability to delete media from a jellyfin client (something I never do) and the ability to manipulate files directly stored with the media. I use bazarr for subtitles so don’t need jellyfin to download them and I don’t keep trickplay or any other jellyfin metadata related files alongside the media. I feel pretty confident with this that even if the jellyfin codebase had a serious 0 day that the worst I’d lose is metadata and server setup, and important config files I have backing up to another machine.
I’m sure there’s things I’ve overlooked with this setup so I’m very open to being told there’s a better way to do something.
services: jellyfin: image: jellyfin/jellyfin:latest container_name: jellyfin restart: unless-stopped mac_address: OBSCURED # see macvlan note below hostname: jellyfin user: ${PUID}:${PGID} group_add: - "992" # my render group id security_opt: - no-new-privileges:true cap_drop: - ALL read_only: true tmpfs: - /tmp:exec,nosuid,nodev # exec some plugins can write to /tmp, no-nosuid,nodev for security networks: macvlan: ipv4_address: OBSCURED healthcheck: test: [ "CMD", "curl", "-fsS", "http://127.0.0.1:8096/health" ] interval: 1m timeout: 10s retries: 3 start_period: 30s environment: - TZ=${TZ} - JELLYFIN_PublishedServerUrl=OBSCURED - JELLYFIN_DATA_DIR=/config/data # consolidates these dirs under /config, migrated from linuxserver.io setup - JELLYFIN_CONFIG_DIR=/config - JELLYFIN_LOG_DIR=/config/log - JELLYFIN_CACHE_DIR=/config/cache - HOME=/config volumes: - ./jellyfin:/config - ${MEDIA}:/media:ro devices: - /dev/dri/renderD128:/dev/dri/renderD128 # isolate to just the renderD128 device device_cgroup_rules: - 'c 226:128 rw' # further limits access for the renderD128 device networks: # I went with macvlan to preserve mdns for local discovery, otherwise you need to use host level networking macvlan: driver: macvlan driver_opts: parent: OBSCURED # Replace with host network interface name (find via: ip route | grep default) ipam: config: - subnet: OBSCURED/24 # Your local LAN network CIDR gateway: OBSCURED # Your local router IP address