Jellyfin DevOps: Automate Updates, Healthchecks, and Monitoring (2026)

Jellyfin DevOps: Automate Updates, Healthchecks, and Monitoring (2026)

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 ConceptHomelab Implementation
Continuous DeploymentWatchtower auto-updates containers
Infrastructure as CodeDocker Compose version-controlled in Git
Dependency ManagementRenovate Bot for image version PRs
Health MonitoringDocker healthchecks + JellyWatch
AlertingNtfy / Apprise notifications
Backup & RecoveryAutomated 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

ContainerStrategyReason
JellyfinPin to minor versionMajor updates need manual testing
Radarr / SonarrAuto-updateStable, frequent small fixes
CaddyAuto-updateSecurity patches important
Watchtower itselfAuto-updateMeta

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.

JellyWatchTry JellyWatch — Your Jellyfin companion, everywhere.
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

Never displayed publicly.
0 / 2000 · Supports limited Markdown: **bold**, *italic*, `code`, [link](url), lists, > quote.