Jellyfin Server Setup with Docker, Caddy, and Tailscale
This guide will walk you through the process of setting up a Jellyfin server using Docker, Caddy, and Tailscale. This setup allows you to access your server from anywhere in the world while keeping your media private and secure.
Table of Contents
- Directory Structure
- Understanding the Folders
- Setup Instructions
- Setup for
jellyfin-local
- Setup for
jellyfin-tailscale
- Running the Server
- Troubleshooting
Directory Structure
The layout of the current folder is as follows:
.
├── README.md
├── jellyfin-local
│ └── docker-compose.yaml
├── jellyfin-server
│ ├── cache
│ └── config
└── jellyfin-tailscale
├── caddy
│ ├── Caddyfile
│ ├── config
│ └── data
├── docker-compose.yaml
└── tailscale
└── varlib
jellyfin-local/docker-compose.yaml
services:
jellyfin:
image: jellyfin/jellyfin:latest
container_name: "jellyfin-l"
user: 1000:1000
ports:
- 127.0.0.1:8096:8096 # enable this for localhost access only - recommended for insecure networks
#- 8096:8096 # enable this for LAN access only
volumes:
# you do not have to use the same local filepaths that I do for volume mapping in the containers,
# but you do have to make sure whatever filepath you use is mapped to the correct filepath in the container
- ~/Jellyfin/jellyfin-server/config:/config
- ~/Jellyfin/jellyfin-server/cache:/cache
- ~/Documents/Jellyfin/Movies:/Movies:ro
- ~/Documents/Jellyfin/Shows:/Shows:ro
restart: unless-stopped
jellyfin-tailscale/docker-compose.yaml
networks:
# network created via docker
# all other containers are also on it
proxy-network:
name: "proxy-network"
volumes:
# shared volumes any container in the same docker-compose file can access
# used to share the tailscaled.sock file with caddy
sock_volume:
# you do not have to use the same local filepaths that I do for volume mapping in the containers,
# but you do have to make sure whatever filepath you use is mapped to the correct filepath in the container
services:
jellyfin:
image: jellyfin/jellyfin
container_name: "jellyfin-ts"
user: 1000:1000
networks:
- proxy-network
volumes:
- ~/Jellyfin/jellyfin-server/config:/config
- ~/Jellyfin/jellyfin-server/cache:/cache
# ro means read only, we don't want jellyfin accidentally deleting our files
- ~/Documents/Jellyfin/Movies:/Movies:ro
- ~/Documents/Jellyfin/Shows:/Shows:ro
restart: unless-stopped
caddy:
image: caddy
container_name: "caddy"
hostname: caddy
networks:
# caddy is in the network with the other containers
- proxy-network
depends_on:
# wait for tailscale to boot
# to communicate to it using the tailscaled.sock
- tailscale
ports:
- "80:80"
- "443:443"
- "443:443/udp"
volumes:
- ~/Jellyfin/jellyfin-tailscale/caddy/Caddyfile:/etc/caddy/Caddyfile
- ~/Jellyfin/jellyfin-tailscale/caddy/data:/data
- ~/Jellyfin/jellyfin-tailscale/caddy/config:/config
# get socket tailscale created in the shared volume and share it with caddy
# caddy expects the socket to be at /var/run/tailscale/tailscaled.sock
- sock_volume:/var/run/tailscale
restart: unless-stopped
tailscale:
container_name: tailscaled
image: tailscale/tailscale
network_mode: host
# tailscale sets new machine names to the OS hostname
# docker-desktop is the default hostname for docker
# if you modify this and recreate the container, the machine name will be updated automatically
# make sure this matches the machine name you set in the Caddyfile
hostname: jellyfin
cap_add:
- NET_ADMIN
- NET_RAW
volumes:
# saves container state after container is recreated
# used varlib because var folder isn't needed locally
- ~/Jellyfin/jellyfin-tailscale/tailscale/varlib:/var/lib
# containerized version of tailscale uses /tmp/tailscaled.sock
# binds the socket to a docker volume so it can be accessed by other containers
# this can't be a local directory because the socket is created by the container
- sock_volume:/tmp
environment:
# if you add a command key, it will override environment key variables with default values!
# info: https://tailscale.com/kb/1282/docker#ts_socks5_server
# set the authkey to reusable when generating it from tailscale
- TS_AUTHKEY=tskey-auth-xxxxxxxxxxxxxxxxxxxx
# prevents a new machine from being added each time the container is restarted
- TS_STATE_DIR=/var/lib/tailscale
# https://tailscale.com/kb/1112/userspace-networking
- TS_USERSPACE_NETWORKING=userspace-networking
restart: unless-stopped
# Steps
#1. run the command: docker-compose up -d
#2. run the command: docker exec tailscaled tailscale --socket /tmp/tailscaled.sock cert <machine-name>.<tailnet-name>.ts.net
#3. remove the auth key from docker-compose.yaml, save the file, you can keep the container running
#4. run the command: docker-compose up -d
# Filepath
# .
# ├── caddy
# │ ├── Caddyfile
# │ ├── config
# │ └── data
# ├── docker-compose.yaml
# └── tailscale
# └── varlib
jellyfin-tailscale/caddy/Caddyfile
# make sure the machine name is the same as the hostname of the tailscale container in docker-compose.yml
<machine-name>.<tailnet-name>.ts.net {
reverse_proxy jellyfin:8096
}
# set to loopback address so it works on the host machine
127.0.0.1 {
reverse_proxy jellyfin:8096
}
To verify that your folder is structured correctly, use the tree
command:
$ tree
Understanding the Folders
The Jellyfin setup consists of three main folders:
-
jellyfin-server
: Contains all the information your Jellyfin server needs to run, excluding media files. Changes made to the server will be saved here. -
jellyfin-local
: Contains the Docker Compose file that runs your server, configured to restrict access to your machine or local network. This setup is ideal for situations where Tailscale connectivity is unavailable. -
jellyfin-tailscale
: Contains the Docker Compose file that runs your server and allows connections only from devices on your Tailnet. It uses a reverse proxy through Caddy for HTTPS connections.
Important: Both
jellyfin-tailscale
andjellyfin-local
interact withjellyfin-server
. To avoid conflicts, do not run these two Docker containers simultaneously.
Setup Instructions
Follow these steps to set up your environment:
-
Docker Setup
- Install Docker Desktop on your system.
- For Linux users, follow the instructions here.
-
Tailscale Setup
- Register a Tailscale account. A free account supports up to 100 devices.
- Download and install Tailscale on your server and clients.
- Enable MagicDNS and HTTPS in the Tailscale admin console.
- Authenticate your server with Tailscale and share access with friends.
Setup for jellyfin-local
This section guides you through setting up jellyfin-local
.
Steps
-
Open the Docker Compose File
Navigate to thejellyfin-local
directory and opendocker-compose.yaml
. -
Navigate to Jellyfin Volumes
Locateservices: jellyfin: volumes:
. -
Understand File Paths
The section contains file paths split by a colon (:
). The part before the colon is the file’s location on your machine; after is its location in the Docker container. -
Replace Local File Paths
Update local file paths as follows:- ~/Jellyfin/jellyfin-server/config:/config - ~/Jellyfin/jellyfin-server/cache:/cache - ~/Documents/Jellyfin/Movies:/Movies:ro - ~/Documents/Jellyfin/Shows:/Shows:ro
Setup for jellyfin-tailscale
This guide walks you through setting up jellyfin-tailscale
.
Steps
-
Open the Docker Compose File
Navigate to thejellyfin-tailscale
directory and opendocker-compose.yaml
. -
Configure Jellyfin Volumes
Underservices: jellyfin: volumes:
, replace local file paths:- ~/Jellyfin/jellyfin-server/config:/config - ~/Jellyfin/jellyfin-server/cache:/cache - ~/Documents/Jellyfin/Movies:/Movies:ro - ~/Documents/Jellyfin/Shows:/Shows:ro
-
Configure Caddy Volumes
Underservices: caddy: volumes:
:- ~/Jellyfin/jellyfin-tailscale/caddy/Caddyfile:/etc/caddy/Caddyfile - ~/Jellyfin/jellyfin-tailscale/caddy/data:/data - ~/Jellyfin/jellyfin-tailscale/caddy/config:/config
-
Configure Tailscale Volumes
Underservices: tailscale: volumes:
:- ~/Jellyfin/jellyfin-tailscale/tailscale/varlib:/var/lib
-
Set Hostname
Underservices: tailscale: hostname:
, set a hostname (e.g.,jellyfin
). -
Set Tailscale Authentication Key
Underservices: tailscale: environment:
replaceTS_AUTHKEY
with your key from Tailscale admin console. -
Setup Caddyfile:
- Navigate to
jellyfin-tailscale/caddy
and openCaddyfile
. - Replace
<machine-name>
with your hostname. - Replace
<tailnet-name>
with your tailnet name from Tailscale admin console.
- Navigate to
Running the Server
Jellyfin-Local
- Navigate to the
jellyfin-local
directory in your terminal. - Execute the command:
docker-compose up -d
- Access your server at
http://localhost:8096
. - Stop this server before running
jellyfin-tailscale
.
Jellyfin-Tailscale
- Navigate to the
jellyfin-tailscale
directory in your terminal: - Execute:
docker-compose up -d
- Check connection status in Tailscale admin console.
- Generate a certificate:
docker exec tailscaled tailscale --socket /tmp/tailscaled.sock cert <machine-name>.<tailnet-name>.ts.net
- Comment out or remove authentication key line in
docker-compose.yaml
. - Remake Docker container:
docker-compose up -d
- Access your server at
https://<machine-name>.<tailnet-name>.ts.net
.
Troubleshooting
- Check File Paths: Ensure all file paths in both
docker-compose.yaml
files are correct. - Check Docker Logs: Review logs in Docker Desktop if connection issues arise.
- Verify Tailscale Key: Ensure authentication key is correct and set to reusable.
- Check Network Connection: Confirm stable network connection.
- Inspect Docker Services: Use:
All services should be in an “Up” state.docker-compose ps
- Check Caddyfile Configuration: Ensure hostname and tailnet name match those in Tailscale admin console.
- Restart Docker Services:
Followed by:docker-compose down
docker-compose up -d
- USB Passthrough Issues: Note that Docker Desktop does not support USB passthrough; consider using colima for that purpose.