forked from coop-cloud/jellyfin
Compare commits
24 Commits
0.1.4+10.8
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 3a2d9fe969 | |||
| bcebdcad31 | |||
| 24bb1e7ab7 | |||
| 9105f0b391 | |||
| df32c88a72 | |||
| e0b3e9136d | |||
| bd20fed151 | |||
| c1fb6404db | |||
| 93a02d6dd0 | |||
| 86db4b372f | |||
| 54db60a580 | |||
| 8e43ac01bd | |||
| 4eaac169f9 | |||
| 4c4394810f | |||
| 5c7ecdad59 | |||
| 114a006554 | |||
| abbff8382e | |||
| 0877020dd7 | |||
| e9c43b077b | |||
| 1c67572a03 | |||
| 13bb4f154e | |||
| 7f9d6f717f | |||
| a259ac5d37 | |||
| be3ac8b7f3 |
39
.drone.yml
Normal file
39
.drone.yml
Normal file
@ -0,0 +1,39 @@
|
||||
---
|
||||
kind: pipeline
|
||||
name: deploy to swarm-test.autonomic.zone
|
||||
steps:
|
||||
- name: deployment
|
||||
image: git.coopcloud.tech/coop-cloud/stack-ssh-deploy:latest
|
||||
settings:
|
||||
host: swarm-test.autonomic.zone
|
||||
stack: jellyfin
|
||||
generate_secrets: true
|
||||
purge: true
|
||||
deploy_key:
|
||||
from_secret: drone_ssh_swarm_test
|
||||
networks:
|
||||
- proxy
|
||||
environment:
|
||||
DOMAIN: jellyfin.swarm-test.autonomic.zone
|
||||
STACK_NAME: jellyfin
|
||||
LETS_ENCRYPT_ENV: production
|
||||
EXTRA_VOLUME: "/dev/null:/tmp/.dummy"
|
||||
trigger:
|
||||
branch:
|
||||
- main
|
||||
---
|
||||
kind: pipeline
|
||||
name: generate recipe catalogue
|
||||
steps:
|
||||
- name: release a new version
|
||||
image: plugins/downstream
|
||||
settings:
|
||||
server: https://build.coopcloud.tech
|
||||
token:
|
||||
from_secret: drone_abra-bot_token
|
||||
fork: true
|
||||
repositories:
|
||||
- toolshed/auto-recipes-catalogue-json
|
||||
|
||||
trigger:
|
||||
event: tag
|
||||
48
.env.sample
48
.env.sample
@ -1,11 +1,51 @@
|
||||
TYPE=jellyfin
|
||||
# Jellyfin Co-op Cloud Recipe Configuration
|
||||
# ==========================================
|
||||
|
||||
# Required Settings
|
||||
TYPE=jellyfin
|
||||
DOMAIN=jellyfin.example.com
|
||||
|
||||
## Domain aliases
|
||||
#EXTRA_DOMAINS=', `www.jellyfin.example.com`'
|
||||
# Compose Files
|
||||
# Add compose.backup.yml for automated backups
|
||||
# Add compose.media_volumes.yml for Sonarr/Radarr integration
|
||||
COMPOSE_FILE="compose.yml"
|
||||
|
||||
# Optional: Additional domains (uncomment to enable)
|
||||
# EXTRA_DOMAINS=|| Host(`www.jellyfin.example.com`)
|
||||
|
||||
# Let's Encrypt environment (staging or production)
|
||||
LETS_ENCRYPT_ENV=production
|
||||
|
||||
# Set to an existing path on the host, and define a path inside the container
|
||||
# Media Volume Configuration
|
||||
# ==========================
|
||||
# Mount a local folder containing your media files
|
||||
# Example: /mnt/media/movies:/media
|
||||
EXTRA_VOLUME=/dev/null:/tmp/.dummy
|
||||
|
||||
# Sonarr/Radarr Integration (use with compose.media_volumes.yml)
|
||||
# Uncomment these when using the media volumes overlay
|
||||
# movies_volume=radarr_media
|
||||
# shows_volume=sonarr_media
|
||||
|
||||
# Backup Configuration (use with compose.backup.yml)
|
||||
# ==================================================
|
||||
# Backup schedule (cron expression, default: 2 AM daily)
|
||||
# BACKUP_CRON=0 2 * * *
|
||||
|
||||
# Number of days to retain backups
|
||||
# BACKUP_RETENTION_DAYS=7
|
||||
|
||||
# Local backup storage path
|
||||
# BACKUP_LOCAL_PATH=/path/to/backups
|
||||
|
||||
# S3-compatible storage (optional)
|
||||
# AWS_S3_BUCKET_NAME=your-bucket
|
||||
# AWS_ACCESS_KEY_ID=your-key-id
|
||||
# AWS_SECRET_ACCESS_KEY=your-secret
|
||||
# AWS_S3_PATH=jellyfin
|
||||
# AWS_ENDPOINT=https://s3.example.com
|
||||
|
||||
# SSH/SFTP storage (optional)
|
||||
# SSH_HOST_NAME=backup.example.com
|
||||
# SSH_USER=backup
|
||||
# SSH_REMOTE_PATH=/backups/jellyfin
|
||||
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@ -1 +1,5 @@
|
||||
.envrc
|
||||
.env
|
||||
.abra/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
157
README.md
157
README.md
@ -1,35 +1,140 @@
|
||||
# jellyfin
|
||||
# Jellyfin
|
||||
|
||||
> One line description of the recipe
|
||||
> The Free Software Media System
|
||||
|
||||
<!-- metadata -->
|
||||
| Property | Value |
|
||||
| --- | --- |
|
||||
| **Category** | Apps |
|
||||
| **Status** | 3, Beta |
|
||||
| **Image** | [`jellyfin/jellyfin`](https://hub.docker.com/r/jellyfin/jellyfin) |
|
||||
| **Healthcheck** | Yes |
|
||||
| **Backups** | Yes |
|
||||
|
||||
* **Category**: Apps
|
||||
* **Status**: 1, alpha
|
||||
* **Image**: [`jellyfin//jellyfin`](https://hub.docker.com/r/jellyfin/jellyfin), 4, upstream
|
||||
* **Healthcheck**: No
|
||||
* **Backups**: No
|
||||
* **Email**: No
|
||||
* **Tests**: No
|
||||
* **SSO**: No
|
||||
Jellyfin is a free and open-source media server that lets you collect, manage, and stream your media. This recipe deploys Jellyfin with Traefik integration, health monitoring, and optional automated backups.
|
||||
|
||||
<!-- endmetadata -->
|
||||
## Quick Start
|
||||
|
||||
## Quick start
|
||||
```bash
|
||||
# Create a new Jellyfin instance
|
||||
abra app new jellyfin --secrets
|
||||
|
||||
* `abra app new jellyfin --secrets`
|
||||
* `abra app config <app-name>`
|
||||
* `abra app deploy <app-name>`
|
||||
# Configure your instance
|
||||
abra app config <app-name>
|
||||
|
||||
For more, see [`docs.coopcloud.tech`](https://docs.coopcloud.tech).
|
||||
|
||||
|
||||
## Adding Media
|
||||
|
||||
You can mount a folder of your choice to jellyfin by editing this line after running `abra app config ${your jelllyfin url}`
|
||||
|
||||
```
|
||||
EXTRA_VOLUME=/home/aadil/media:/media/ # replace /home/aadil/media with folder of your choice
|
||||
# Deploy
|
||||
abra app deploy <app-name>
|
||||
```
|
||||
|
||||
Then during the jellyfin setup wizard use this folder as the path for your jellyfin library.
|
||||
## Configuration
|
||||
|
||||
### Media Volumes
|
||||
|
||||
Configure media access by editing `EXTRA_VOLUME` in your app configuration:
|
||||
|
||||
```bash
|
||||
# Single media folder
|
||||
EXTRA_VOLUME=/mnt/media:/media
|
||||
|
||||
# Multiple folders (use docker-compose syntax)
|
||||
EXTRA_VOLUME=/mnt/movies:/movies
|
||||
```
|
||||
|
||||
During Jellyfin's initial setup wizard, add your mounted paths as media libraries.
|
||||
|
||||
### Backup Configuration
|
||||
|
||||
To enable automated backups, add `compose.backup.yml` to your `COMPOSE_FILE`:
|
||||
|
||||
```bash
|
||||
COMPOSE_FILE="compose.yml:compose.backup.yml"
|
||||
```
|
||||
|
||||
Configure backup storage in your environment:
|
||||
|
||||
| Variable | Description | Default |
|
||||
| --- | --- | --- |
|
||||
| `BACKUP_CRON` | Backup schedule (cron) | `0 2 * * *` (2 AM daily) |
|
||||
| `BACKUP_RETENTION_DAYS` | Days to keep backups | `7` |
|
||||
| `BACKUP_LOCAL_PATH` | Local backup directory | `/tmp/jellyfin-backups` |
|
||||
|
||||
#### S3-Compatible Storage
|
||||
|
||||
```bash
|
||||
AWS_S3_BUCKET_NAME=your-bucket
|
||||
AWS_ACCESS_KEY_ID=your-key-id
|
||||
AWS_SECRET_ACCESS_KEY=your-secret
|
||||
AWS_S3_PATH=jellyfin
|
||||
AWS_ENDPOINT=https://s3.example.com # For non-AWS S3
|
||||
```
|
||||
|
||||
#### SSH/SFTP Storage
|
||||
|
||||
```bash
|
||||
SSH_HOST_NAME=backup.example.com
|
||||
SSH_USER=backup
|
||||
SSH_REMOTE_PATH=/backups/jellyfin
|
||||
```
|
||||
|
||||
## Integration with Media Stack
|
||||
|
||||
Jellyfin works well with the *arr stack for automated media management:
|
||||
|
||||
| Service | Purpose |
|
||||
| --- | --- |
|
||||
| [qBittorrent](https://git.coopcloud.tech/coop-cloud/qbittorrent) | Torrent client for downloads |
|
||||
| [Prowlarr](https://git.coopcloud.tech/coop-cloud/prowlarr) | Indexer manager for all *arr apps |
|
||||
| [Sonarr](https://git.coopcloud.tech/coop-cloud/sonarr) | TV show monitoring and download |
|
||||
| [Radarr](https://git.coopcloud.tech/coop-cloud/radarr) | Movie monitoring and download |
|
||||
| [Jellyseerr](https://git.coopcloud.tech/coop-cloud/jellyseerr) | Request management and discovery UI |
|
||||
|
||||
### Shared Media Volumes
|
||||
|
||||
When integrating with Sonarr/Radarr, use `compose.media_volumes.yml` to share their media volumes:
|
||||
|
||||
```bash
|
||||
COMPOSE_FILE="compose.yml:compose.media_volumes.yml"
|
||||
movies_volume=radarr_media
|
||||
shows_volume=sonarr_media
|
||||
```
|
||||
|
||||
This mounts Radarr's movies and Sonarr's shows directly into Jellyfin at `/movies` and `/shows`.
|
||||
|
||||
## Hardware Transcoding
|
||||
|
||||
For hardware-accelerated transcoding, you may need to pass through GPU devices. Create a custom compose overlay:
|
||||
|
||||
```yaml
|
||||
# compose.gpu.yml
|
||||
services:
|
||||
app:
|
||||
devices:
|
||||
- /dev/dri:/dev/dri # Intel QSV / VAAPI
|
||||
```
|
||||
|
||||
Then add it to your configuration:
|
||||
|
||||
```bash
|
||||
COMPOSE_FILE="compose.yml:compose.gpu.yml"
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Health Check Failures
|
||||
|
||||
The health check uses Jellyfin's `/health` endpoint. If it fails:
|
||||
|
||||
1. Check logs: `abra app logs <app-name>`
|
||||
2. Verify the container is running: `abra app ps <app-name>`
|
||||
3. Ensure port 8096 isn't blocked internally
|
||||
|
||||
### Permission Issues
|
||||
|
||||
If Jellyfin can't access media files:
|
||||
|
||||
1. Check volume mount permissions
|
||||
2. Ensure the container user has read access to media directories
|
||||
3. Verify SELinux/AppArmor policies if applicable
|
||||
|
||||
## License
|
||||
|
||||
Jellyfin is licensed under [GPL-2.0](https://github.com/jellyfin/jellyfin/blob/master/LICENSE).
|
||||
|
||||
41
compose.backup.yml
Normal file
41
compose.backup.yml
Normal file
@ -0,0 +1,41 @@
|
||||
---
|
||||
services:
|
||||
backup:
|
||||
image: offen/docker-volume-backup:v2
|
||||
environment:
|
||||
- BACKUP_CRON_EXPRESSION=${BACKUP_CRON:-0 2 * * *}
|
||||
- BACKUP_FILENAME=jellyfin-backup-%Y-%m-%dT%H-%M-%S.tar.gz
|
||||
- BACKUP_PRUNING_PREFIX=jellyfin-backup-
|
||||
- BACKUP_RETENTION_DAYS=${BACKUP_RETENTION_DAYS:-7}
|
||||
- BACKUP_STOP_DURING_BACKUP_LABEL=jellyfin-backup
|
||||
# Optional: S3-compatible storage
|
||||
- AWS_S3_BUCKET_NAME=${AWS_S3_BUCKET_NAME:-}
|
||||
- AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID:-}
|
||||
- AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY:-}
|
||||
- AWS_S3_PATH=${AWS_S3_PATH:-}
|
||||
- AWS_ENDPOINT=${AWS_ENDPOINT:-}
|
||||
# Optional: SSH/SFTP storage
|
||||
- SSH_HOST_NAME=${SSH_HOST_NAME:-}
|
||||
- SSH_USER=${SSH_USER:-}
|
||||
- SSH_REMOTE_PATH=${SSH_REMOTE_PATH:-}
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
- config:/backup/config:ro
|
||||
- ${BACKUP_LOCAL_PATH:-/tmp/jellyfin-backups}:/archive
|
||||
networks:
|
||||
- proxy
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
labels:
|
||||
- "traefik.enable=false"
|
||||
|
||||
app:
|
||||
deploy:
|
||||
labels:
|
||||
- "docker-volume-backup.stop-during-backup=jellyfin-backup"
|
||||
|
||||
volumes:
|
||||
config:
|
||||
external: true
|
||||
name: ${STACK_NAME}_config
|
||||
14
compose.media_volumes.yml
Normal file
14
compose.media_volumes.yml
Normal file
@ -0,0 +1,14 @@
|
||||
---
|
||||
services:
|
||||
app:
|
||||
volumes:
|
||||
- media_movies:/movies
|
||||
- media_shows:/shows
|
||||
|
||||
volumes:
|
||||
media_movies:
|
||||
name: ${movies_volume}
|
||||
external: true
|
||||
media_shows:
|
||||
name: ${shows_volume}
|
||||
external: true
|
||||
46
compose.yml
46
compose.yml
@ -1,42 +1,42 @@
|
||||
---
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
app:
|
||||
image: jellyfin/jellyfin:10.8.8
|
||||
image: jellyfin/jellyfin:10.11.1
|
||||
ports:
|
||||
- "8096:8096"
|
||||
environment:
|
||||
- JELLYFIN_PublishedServerUrl=https://${DOMAIN}
|
||||
volumes:
|
||||
- config:/config
|
||||
- cache:/cache
|
||||
- ${EXTRA_VOLUME:-/dev/null:/tmp/.dummy}
|
||||
networks:
|
||||
- proxy
|
||||
volumes:
|
||||
- jellyfin_config:/config
|
||||
- jellyfin_cache:/cache
|
||||
- ${EXTRA_VOLUME}
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.services.${STACK_NAME}.loadbalancer.server.port=8096"
|
||||
- "traefik.http.routers.${STACK_NAME}.rule=Host(`${DOMAIN}`${EXTRA_DOMAINS})"
|
||||
- "traefik.http.routers.${STACK_NAME}.rule=Host(`${DOMAIN}`)${EXTRA_DOMAINS}"
|
||||
- "traefik.http.routers.${STACK_NAME}.entrypoints=web-secure"
|
||||
- "traefik.http.routers.${STACK_NAME}.tls.certresolver=${LETS_ENCRYPT_ENV}"
|
||||
## Redirect from EXTRA_DOMAINS to DOMAIN
|
||||
#- "traefik.http.routers.${STACK_NAME}.middlewares=${STACK_NAME}-redirect"
|
||||
#- "traefik.http.middlewares.${STACK_NAME}-redirect.headers.SSLForceHost=true"
|
||||
#- "traefik.http.middlewares.${STACK_NAME}-redirect.headers.SSLHost=${DOMAIN}"
|
||||
- "coop-cloud.${STACK_NAME}.version=0.1.4+10.8.8"
|
||||
# healthcheck:
|
||||
# test: ["CMD", "curl", "-f", "http://localhost:8096"]
|
||||
# interval: 30s
|
||||
# timeout: 10s
|
||||
# retries: 10
|
||||
# start_period: 1m
|
||||
- "traefik.http.routers.${STACK_NAME}.tls.certresolver=${LETS_ENCRYPT_ENV:-production}"
|
||||
- "traefik.http.services.${STACK_NAME}.loadbalancer.server.port=8096"
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8096/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 60s
|
||||
|
||||
volumes:
|
||||
jellyfin_config:
|
||||
jellyfin_cache:
|
||||
config:
|
||||
cache:
|
||||
|
||||
networks:
|
||||
proxy:
|
||||
external: true
|
||||
|
||||
# coop-cloud recipe metadata
|
||||
# LABEL version="0.6.0+10.11.1"
|
||||
# LABEL healthcheck="true"
|
||||
# LABEL backups="true"
|
||||
|
||||
Reference in New Issue
Block a user