Jellyfin DevOps: Automate Your Homelab (2026)
Most Jellyfin admins set up their stack once and manually update it every few months - if they remember. A DevOps approach changes that: your stack updates itself, monitors itself, and alerts you when something needs attention.
This guide applies real DevOps practices to a Jellyfin homelab.
What "DevOps for Homelab" Actually Means
In a professional context, DevOps covers CI/CD, infrastructure as code, monitoring, and automated recovery. For a Jellyfin homelab, the practical equivalent is:
| DevOps Concept | Homelab Implementation |
|---|---|
| Continuous Deployment | Watchtower auto-updates containers |
| Infrastructure as Code | Docker Compose version-controlled in Git |
| Dependency Management | Renovate Bot for image version PRs |
| Health Monitoring | Docker healthchecks + JellyWatch |
| Alerting | Ntfy / Apprise notifications |
| Backup & Recovery | Automated config backups with cron |
1. Infrastructure as Code: Git-Track Your Stack
The first DevOps principle: everything in version control.
mkdir ~/homelab && cd ~/homelab
git init
Your docker-compose.yml is your infrastructure definition. Commit every change:
git add docker-compose.yml
git commit -m "feat: add Bazarr for subtitle automation"
When something breaks after an update, git diff shows exactly what changed. git revert rolls it back.
Recommended structure
~/homelab/
docker-compose.yml
.env # secrets (add to .gitignore)
.env.example # template without secrets
configs/
Caddyfile
prometheus.yml
scripts/
backup.sh
healthcheck.sh
2. Automated Container Updates: Watchtower
Watchtower monitors your running containers and automatically pulls new image versions.
services:
watchtower:
image: containrrr/watchtower:latest
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
- WATCHTOWER_SCHEDULE=0 0 4 * * * # 4 AM daily
- WATCHTOWER_CLEANUP=true # remove old images
- WATCHTOWER_NOTIFICATIONS=ntfy
- WATCHTOWER_NOTIFICATION_NTFY_URL=http://ntfy:80/watchtower
- WATCHTOWER_ROLLING_RESTART=true # restart one at a time
restart: unless-stopped
What to auto-update vs pin
| Container | Strategy | Reason |
|---|---|---|
| Jellyfin | Pin to minor version | Major updates need manual testing |
| Radarr / Sonarr | Auto-update | Stable, frequent small fixes |
| Caddy | Auto-update | Security patches important |
| Watchtower itself | Auto-update | Meta |
To pin Jellyfin and exclude it from Watchtower:
jellyfin:
image: jellyfin/jellyfin:10.11
labels:
- "com.centurylinklabs.watchtower.enable=false"
3. Dependency Management: Renovate Bot
If your stack is in a Git repo (GitHub, Gitea, self-hosted), Renovate Bot automatically opens pull requests when new image versions are available - before Watchtower deploys them.
This gives you a review step for major updates.
renovate.json
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["config:base"],
"docker-compose": {
"enabled": true
},
"packageRules": [
{
"matchDatasources": ["docker"],
"matchPackageNames": ["jellyfin/jellyfin"],
"automerge": false,
"reviewers": ["your-username"]
},
{
"matchDatasources": ["docker"],
"matchPackageNames": ["linuxserver/radarr", "linuxserver/sonarr"],
"automerge": true
}
]
}
Renovate opens a PR like: "Update jellyfin/jellyfin from 10.11.5 to 10.11.7" - you review the changelog, merge, Watchtower deploys.
4. Docker Healthchecks
Healthchecks tell Docker (and your monitoring tools) whether a container is actually working - not just running.
jellyfin:
image: jellyfin/jellyfin:latest
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8096/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
restart: unless-stopped
radarr:
image: linuxserver/radarr:latest
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:7878/ping"]
interval: 30s
timeout: 5s
retries: 3
start_period: 30s
restart: unless-stopped
jellyseerr:
image: fallenbagel/jellyseerr:latest
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:5055/api/v1/status"]
interval: 15s
timeout: 3s
retries: 3
start_period: 20s
restart: unless-stopped
With restart: unless-stopped and a healthcheck, Docker automatically restarts unhealthy containers.
Check container health
docker ps --format "table {{.Names}}\t{{.Status}}"
Output:
NAMES STATUS
jellyfin Up 14 days (healthy)
radarr Up 14 days (healthy)
sonarr Up 14 days (healthy)
caddy Up 14 days (healthy)
5. Automated Backups
A DevOps backup strategy runs automatically, retains multiple versions, and alerts on failure.
#!/bin/bash
# /home/user/homelab/scripts/backup.sh
BACKUP_DIR="/backups/homelab"
DATE=$(date +%Y%m%d-%H%M)
RETENTION_DAYS=14
NTFY_URL="http://localhost:8090/homelab-alerts"
mkdir -p "$BACKUP_DIR"
# Stop Jellyfin for consistent backup
docker stop jellyfin
# Backup all config volumes
tar -czf "$BACKUP_DIR/jellyfin-$DATE.tar.gz" /path/to/jellyfin/config
tar -czf "$BACKUP_DIR/radarr-$DATE.tar.gz" /path/to/radarr/config
tar -czf "$BACKUP_DIR/sonarr-$DATE.tar.gz" /path/to/sonarr/config
docker start jellyfin
# Cleanup old backups
find "$BACKUP_DIR" -name "*.tar.gz" -mtime +$RETENTION_DAYS -delete
# Notify success
curl -s -d "Backup completed: $DATE" "$NTFY_URL" > /dev/null
echo "Backup done: $DATE"
Add to crontab:
# Every Sunday at 3 AM
0 3 * * 0 /home/user/homelab/scripts/backup.sh >> /var/log/homelab-backup.log 2>&1
6. Alerting Pipeline
Combine Watchtower + healthcheck failures + backup results into one notification channel.
services:
ntfy:
image: binwiederhier/ntfy:latest
volumes:
- ./ntfy/cache:/var/cache/ntfy
ports:
- "8090:80"
restart: unless-stopped
Subscribe to homelab-alerts on your phone via the Ntfy Android app. Every update, backup, and container restart sends a notification.
7. The Self-Healing Stack
With all pieces in place, your stack becomes self-healing:
Container crashes
→ Docker detects unhealthy
→ Docker restarts container
→ Ntfy sends alert to your phone
→ JellyWatch shows session recovery
New image available
→ Renovate opens PR
→ You review changelog
→ Merge PR
→ Watchtower deploys at 4 AM
→ Ntfy confirms update
Sunday 3 AM
→ Backup script runs
→ Configs archived with 14-day retention
→ Ntfy confirms backup success
Complete docker-compose.yml
services:
caddy:
image: caddy:2-alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./configs/Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
restart: unless-stopped
jellyfin:
image: jellyfin/jellyfin:10.11
volumes:
- ./jellyfin/config:/config
- /path/to/media:/media:ro
devices:
- /dev/dri:/dev/dri
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8096/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
labels:
- "com.centurylinklabs.watchtower.enable=false"
restart: unless-stopped
radarr:
image: linuxserver/radarr:latest
environment:
- PUID=1000
- PGID=1000
volumes:
- ./radarr/config:/config
- /path/to/media/movies:/movies
- /path/to/downloads:/downloads
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:7878/ping"]
interval: 30s
timeout: 5s
retries: 3
restart: unless-stopped
sonarr:
image: linuxserver/sonarr:latest
environment:
- PUID=1000
- PGID=1000
volumes:
- ./sonarr/config:/config
- /path/to/media/tv:/tv
- /path/to/downloads:/downloads
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8989/ping"]
interval: 30s
timeout: 5s
retries: 3
restart: unless-stopped
jellyseerr:
image: fallenbagel/jellyseerr:latest
volumes:
- ./jellyseerr/config:/app/config
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:5055/api/v1/status"]
interval: 15s
timeout: 3s
retries: 3
start_period: 20s
restart: unless-stopped
watchtower:
image: containrrr/watchtower:latest
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
- WATCHTOWER_SCHEDULE=0 0 4 * * *
- WATCHTOWER_CLEANUP=true
- WATCHTOWER_NOTIFICATIONS=ntfy
- WATCHTOWER_NOTIFICATION_NTFY_URL=http://ntfy:80/homelab-alerts
- WATCHTOWER_ROLLING_RESTART=true
restart: unless-stopped
ntfy:
image: binwiederhier/ntfy:latest
volumes:
- ./ntfy/cache:/var/cache/ntfy
ports:
- "8090:80"
restart: unless-stopped
volumes:
caddy_data:
FAQ
Is Watchtower safe for a production Jellyfin server? For Jellyfin itself, pin the version and exclude it from Watchtower. For supporting services (Radarr, Sonarr, Caddy), auto-update is generally safe.
Does Renovate work with self-hosted Gitea? Yes. Renovate supports Gitea, Forgejo, GitHub, and GitLab.
What if a healthcheck fails but the container is actually fine? Tune the start_period - some containers take longer to initialize. Jellyfin needs at least 60 seconds on first start.
Can JellyWatch detect container restarts? JellyWatch detects when Jellyfin goes offline and comes back - you get a push notification for both events.
Your stack is automated - now monitor it from your pocket. Download JellyWatch on Google Play - real-time session monitoring, server health, and push alerts for your Jellyfin homelab.




Comments
No comments yet. Be the first to share your thoughts.
Leave a comment