Jellyfin - Selfhosted Media Server
Jellyfin - Selfhosted Media Server
Jellyfin is the volunteer-built media solution that puts you in control of your media. Stream to any device from your own server, with no strings attached. Your media, your server, your way.
I also run JellyPlex to sync watch states between Plex and Jellyfin
Since this uses media from my NAS i have simply decided to run this as a Docker Container on UnRAID on my NAS instead of having it as part of my kubernetes cluster to remove the need to access the NAS media remotely
Kubernetes Manifest
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: jellyfin
app.kubernetes.io/instance: jellyfin
app.kubernetes.io/name: jellyfin
name: jellyfin
namespace: jellyfin
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: jellyfin
template:
metadata:
labels:
app: jellyfin
app.kubernetes.io/name: jellyfin
spec:
nodeSelector:
nas: "true"
containers:
- image: jellyfin/jellyfin
name: jellyfin
ports:
- containerPort: 8096
name: web
protocol: TCP
env:
- name: TZ
value: Europe/London
volumeMounts:
- mountPath: "/media"
readOnly: false
name: smb
- mountPath: "/config"
readOnly: false
name: config
- mountPath: /dev/dri
name: dri
securityContext:
privileged: true
volumes:
- name: smb
persistentVolumeClaim:
claimName: pvc-jellyfin-smb
- name: config
persistentVolumeClaim:
claimName: jellyfin
- name: dri
hostPath:
path: /dev/dri
---
apiVersion: v1
kind: Service
metadata:
labels:
app: jellyfin
name: jellyfin
namespace: jellyfin
spec:
ports:
- name: web-tcp
port: 8096
protocol: TCP
targetPort: 8096
- name: web-udp
port: 8096
protocol: UDP
targetPort: 8096
selector:
app: jellyfin
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: jellyfin
namespace: jellyfin
annotations:
kubernetes.io/ingress.class: traefik-external
gethomepage.dev/href: "https://jellyfin.f9.casa"
gethomepage.dev/enabled: "true"
gethomepage.dev/description: Media Server
gethomepage.dev/group: Media
gethomepage.dev/icon: jellyfin.png
gethomepage.dev/name: Jellyfin
gethomepage.dev/widget.type: jellyfin
gethomepage.dev/widget.url: "http://jellyfin.jellyfin:8096"
gethomepage.dev/widget.key: "[REDACTED]"
spec:
entryPoints:
- websecure
routes:
- match: Host(`jellyfin.f9.casa`)
kind: Rule
services:
- name: jellyfin
port: 8096
middlewares:
- name: default-headers
namespace: default
tls:
secretName: f9-casa-tls
Docker Compose
Note that as I use Docker Swarm I am making use of https://github.com/allfro/device-mapping-manager to pass through dev’s to swarm containers by faking a volume, this container then detects that fake volume and maps it as a device
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
version: '3.9'
services:
jellyfin:
image: lscr.io/linuxserver/jellyfin:latest
hostname: jellyfin
container_name: jellyfin
privileged: true
networks:
- traefik-public
environment:
- TZ=Europe/London
- JELLYFIN_PublishedServerUrl=jellyfin.f9.casa
- PUID=1000
- PGID=1000
volumes:
- /srv/cephfs/docker/appdata/jellyfin:/config
- /srv/media:/media
- /dev/dri:/dev/dri
ports:
- 8096:8096
deploy:
labels:
- "traefik.enable=true"
- "traefik.http.routers.jellyfin.rule=Host(`jellyfin.f9.casa`)"
- "traefik.http.services.jellyfin.loadbalancer.server.port=8096"
- "traefik.http.routers.jellyfin.entrypoints=websecure"
- "traefik.http.routers.jellyfin.tls=true"
- "traefik.http.routers.jellyfin.tls.certresolver=letsencrypt"
- homepage.group=Media
- homepage.name=Jellyfin
- homepage.icon=jellyfin.png
- homepage.href=https://jellyfin.f9.casa
- homepage.description=Media server
- homepage.siteMonitor=http://jellyfin:8096
- homepage.widget.type=jellyfin
- homepage.widget.url=http://jellyfin:8096
- homepage.widget.key=[REDACTED]
mode: replicated
placement:
constraints:
- node.labels.gpu == true
jellyplex:
image: luigi311/jellyplex-watched:latest
hostname: jellyplex
container_name: jellyplex
privileged: false
networks:
- traefik-public
environment:
- SYNC_FROM_PLEX_TO_JELLYFIN=true
- SYNC_FROM_JELLYFIN_TO_PLEX=true
- DRYRUN=true
- DEBUG=false
- DEBUG_LEVEL=info
- SLEEP_DURATION=3600
- LOGFILE=/tmp/log.log
- 'USER_MAPPING={ "[REDACTED]": "Scott" }'
- PLEX_BASEURL=http://plex:32400
- PLEX_TOKEN=[REDACTED]
- PLEX_SERVERNAME=F9
- SSL_BYPASS=true
- JELLYFIN_BASEURL=http://jellyfin:8096
- JELLYFIN_TOKEN=[REDACTED]
deploy:
mode: replicated
networks:
traefik-public:
external: true
Reset Jellyfin Password
sqlite3 jellyfin.db
1
UPDATE Users SET Password = '$PBKDF2-SHA512$iterations=120000$C8B9B58B72BC7B864DE42F8819FBA024$7B6BC38F2E7635C4E1B93FA9C997479548497EAD1F298FF47C3398D369F47874C6B2504D1F59F84A88116954AD0795DBAB06AB3DC7DB22FCE470B82E183A48DA' WHERE Username = 'XXX';
This post is licensed under CC BY 4.0 by the author.