--- title: Jellyfin description: Install Jellyfin, a free and open-source media server, to stream your movies and TV shows from anywhere without a paid subscription. --- :ellipsis{left=0px width=40rem top=10rem blur=140px zIndex=60} # Jellyfin ::note 🎯 __Objectives:__ - Install Jellyfin - Access your media from outside your network :: [Jellyfin](https://jellyfin.org) is a free, open-source alternative to Plex and Emby. Unlike Plex, it has no paid tiers, no telemetry, and doesn't require an online account to use or manage your server: everything runs locally and stays yours. It covers the same basics: a media library with metadata and artwork, transcoding (including hardware acceleration), and apps for TV, Android, iOS, Windows, and macOS. As always, we'll use the [linuxserver.io image](https://docs.linuxserver.io/images/docker-jellyfin). ::note Unlike Plex, Jellyfin has no cloud relay: to access your server outside your local network, you must expose it yourself (see below), or use a VPN like [Wireguard](/serveex/security/wireguard). :: ## Install Jellyfin Folder structure: ```text [Directory tree] root ├── docker │ └── jellyfin │ ├── compose.yaml │ ├── .env │ └── config └── media ├── tvseries ├── movies └── library ``` Create the `movies`, `tvseries`, and `library` folders in `/media`: ```bash [Terminal] mkdir -p /media/movies /media/library /media/tvseries ``` Open Dockge, click `compose`, name the stack `jellyfin`, and add the following config: ```yaml [compose.yaml] --- services: jellyfin: image: lscr.io/linuxserver/jellyfin:latest container_name: jellyfin environment: - PUID=${PUID} - PGID=${GUID} - TZ=Europe/Paris volumes: - /docker/jellyfin/config:/config - /media:/media restart: unless-stopped devices: - /dev/dri:/dev/dri ports: - 8096:8096 ``` ::tip ✨ Add the Watchtower label to automate updates: ```yaml [compose.yaml] services: jellyfin: #... labels: - com.centurylinklabs.watchtower.enable=true ``` :: Find your PUID and GUID by running: ```bash [Terminal] id username ``` Fill in your `.env` file with the retrieved values, for example: ```properties [.env] PUID=1000 GUID=1000 ``` Deploy the stack. The local interface is available at `http://yourserverip:8096`. ::note The `/dev/dri` device is only needed for hardware-accelerated transcoding on Intel/AMD GPUs. Remove it if your server doesn't have one, or adapt it for an NVIDIA GPU following [linuxserver.io's documentation](https://docs.linuxserver.io/images/docker-jellyfin/#hardware-acceleration). :: ## Configure Jellyfin On first visit, Jellyfin walks you through a setup wizard: - Choose a display language and create your admin account. - Add a media library, pointing to `/media/movies` for movies and `/media/tvseries` for TV shows. - (Optional) Enable hardware acceleration in _Dashboard > Playback_ if you have a compatible GPU. And that's it! Add your media to `/media/movies` and `/media/tvseries` on your server, then install the Jellyfin app on your devices to watch locally or remotely. ::note If your media is stored on a network disk (e.g. NAS or external hard drive over the network), refer to the [Samba mount guide](/general/networking/samba) so Jellyfin can access it. :: ## Expose Jellyfin with Swag To access Jellyfin outside your local network, we'll expose it through Swag. ::note We assume you have the subdomain `jellyfin.mydomain.com` with a `CNAME` pointing to `mydomain.com` in your [DNS zone](/general/networking/dns). And of course, [unless you use Cloudflare Zero Trust](/serveex/security/cloudflare), your box's port `443` must be forwarded to your server's port `443` in [NAT rules](/general/networking/nat). :: Go to Dockge and edit SWAG's compose file by adding Jellyfin's network: ```yaml [compose.yaml] services: swag: container_name: # ... # ... networks: # Attach container to custom network # ... - jellyfin # Name of the declared network networks: # Define the custom network # ... jellyfin: # Declared network name name: jellyfin_default # Actual external network name external: true # Marks it as externally defined ``` Redeploy the stack and wait for SWAG to be fully operational. ::note Here we assume the Jellyfin network name is `jellyfin_default`. You can check the connection by visiting SWAG's dashboard at `http://yourserverip:81`. :: In the Swag folders, create the file `jellyfin.subdomain.conf`: ::tip{icon=""} ✨ __Tip:__ Use [File Browser](/serveex/files/file-browser) to navigate and edit files instead of using terminal commands. :: ```bash [Terminal] sudo nano /docker/swag/config/nginx/proxy-confs/jellyfin.subdomain.conf ``` Paste the following configuration: ```nginx [jellyfin.subdomain.conf] ## Version 2023/12/19 server { listen 443 ssl; listen [::]:443 ssl; server_name jellyfin.*; include /config/nginx/ssl.conf; client_max_body_size 0; # enable for ldap auth (requires ldap-location.conf in the location block) #include /config/nginx/ldap-server.conf; # enable for Authelia (requires authelia-location.conf in the location block) #include /config/nginx/authelia-server.conf; # enable for Authentik (requires authentik-location.conf in the location block) #include /config/nginx/authentik-server.conf; location / { # enable the next two lines for http auth #auth_basic "Restricted"; #auth_basic_user_file /config/nginx/.htpasswd; # enable for ldap auth (requires ldap-server.conf in the server block) #include /config/nginx/ldap-location.conf; # enable for Authelia (requires authelia-server.conf in the server block) #include /config/nginx/authelia-location.conf; # enable for Authentik (requires authentik-server.conf in the server block) #include /config/nginx/authentik-location.conf; include /config/nginx/proxy.conf; include /config/nginx/resolver.conf; set $upstream_app jellyfin; set $upstream_port 8096; set $upstream_proto http; proxy_pass $upstream_proto://$upstream_app:$upstream_port; } } ``` Press :kbd{value="Ctrl+O"}, then :kbd{value="Enter"} to save, and :kbd{value="Ctrl+X"} to exit. Wait a few minutes, then open `https://jellyfin.mydomain.com` in your browser. ::caution __If it fails:__ check your firewall rules. :: ::tip{icon=""} ✨ __Tip:__ You can protect this app with [TinyAuth](/serveex/security/tinyauth) and [Pocket ID](/serveex/security/pocket-id) using the reverse-proxy pattern from the TinyAuth guide, or with Authentik by opening `jellyfin.subdomain.conf` and uncommenting `include /config/nginx/authentik-server.conf;`{lang=nginx} and `include /config/nginx/authentik-location.conf;`{lang=nginx}. Don't forget to [create an application and provider in Authentik](/serveex/advanced/authentik#protecting-an-app-via-reverse-proxy). :: And you're done!