← All Guides
intermediate

Homelab Automation: Scheduled Backups, Updates, and Monitoring

Set your homelab to back itself up, update its own containers, and tell you when something breaks. A practical automation stack using cron, Watchtower, Proxmox vzdump, and Uptime Kuma.

Budget Homelab ·
dockerautomationproxmoxbackupshomelab

A homelab you have to babysit is a homelab you will eventually neglect. The containers go stale, the backups quietly stop running, a disk fills up at 2am and you find out three days later when a service is already down. The fix is not discipline. It is automation. A well-set-up homelab backs itself up, keeps its own containers current, and messages you the moment something actually breaks, so the only time you log in is when there is a real decision to make.

This guide pulls the automation layer together into one place. It assumes you already have services running, so if you are still building the foundation, start with how to start a homelab and the best self-hosted apps for beginners first, then come back here. Everything below maps onto a normal budget setup like the one on my stack page: Proxmox on a mini PC, Docker containers, and a couple of cheap drives.

This guide contains no paid affiliate links. Hardware picks live in the linked storage and stack guides.

The Three Things Worth Automating

There are a hundred things you could automate. Three of them earn their keep immediately:

  1. Backups. The one task you cannot afford to do inconsistently.
  2. Updates. Stale images are the slow leak: missed bug fixes and unpatched security holes.
  3. Monitoring. Automation that runs silently is worthless. You need to know when a job fails or a service falls over.

Notice the order. Monitoring is last to set up but it is what makes the other two trustworthy. An automated backup you never verify is just a job that might be running. Build all three and they reinforce each other.

1. Scheduled Backups

Backups are layered. Proxmox handles the whole VM or container. A small script handles individual Docker volumes for faster, more granular restores. Off-site sync handles the disaster case.

Proxmox backups (vzdump)

Proxmox has a scheduler built in. Go to Datacenter, then Backup, then Add:

I run daily vzdump with 7-day retention. The oldest backup rolls off automatically, so storage stays bounded. The full mechanics of vzdump, including restoring from one, are covered in Proxmox snapshots and backups. The important word here is scheduled: set it once and stop thinking about it.

You want that backup target to be a separate physical drive. A cheap external SSD plugged into the mini PC is enough to get backups off the boot disk for a home setup, and the homelab storage options rundown covers which budget drives are actually worth buying.

Docker volume backups

vzdump captures the whole VM, but when one app’s config gets corrupted, restoring an entire VM to recover a single volume is heavy. A small per-volume backup script makes recovery surgical.

Create /opt/scripts/backup-volumes.sh:

#!/usr/bin/env bash
set -euo pipefail

BACKUP_DIR="/mnt/backups/docker-volumes"
DATE=$(date +%Y-%m-%d)
RETENTION_DAYS=14

mkdir -p "$BACKUP_DIR"

# One line per service: container name and the host path of its data
declare -A VOLUMES=(
  ["vaultwarden"]="/opt/vaultwarden/data"
  ["paperless"]="/opt/paperless/data"
  ["uptime-kuma"]="/opt/uptime-kuma/data"
)

for name in "${!VOLUMES[@]}"; do
  src="${VOLUMES[$name]}"
  echo "Backing up $name from $src"
  docker stop "$name"
  tar czf "$BACKUP_DIR/${name}-${DATE}.tar.gz" -C "$(dirname "$src")" "$(basename "$src")"
  docker start "$name"
done

# Prune archives older than the retention window
find "$BACKUP_DIR" -name '*.tar.gz' -mtime +"$RETENTION_DAYS" -delete

echo "Volume backup complete: $DATE"

Make it executable and test it once by hand:

chmod +x /opt/scripts/backup-volumes.sh
/opt/scripts/backup-volumes.sh

Stopping each container before archiving guarantees a consistent copy, especially for anything using SQLite, which does not like being read mid-write. The containers are down for a few seconds each. For homelab services that is fine. Schedule it overnight with cron:

crontab -e
# Docker volume backups, nightly at 2:30am
30 2 * * * /opt/scripts/backup-volumes.sh >> /var/log/volume-backup.log 2>&1

Off-site copies with restic

Local backups die with the building. The cheapest fix is restic pushing an encrypted copy to Backblaze B2. B2 runs about $0.006 per GB per month, so 50GB of critical data costs roughly $0.30 a month.

# Install restic
sudo apt install restic

# Set credentials (store these in your password manager, not in the script)
export B2_ACCOUNT_ID="your-key-id"
export B2_ACCOUNT_KEY="your-application-key"
export RESTIC_REPOSITORY="b2:your-bucket-name:homelab"
export RESTIC_PASSWORD="a-long-random-passphrase"

# Initialize the repo once
restic init

# Back up your local backup directory
restic backup /mnt/backups

Wrap that in /opt/scripts/restic-offsite.sh (with the exports inside the script, locked down to root-only read), then schedule a daily backup and a weekly prune:

# Off-site restic backup, nightly at 4am
0 4 * * * /opt/scripts/restic-offsite.sh >> /var/log/restic.log 2>&1

# Prune old off-site snapshots, Sundays at 5am
0 5 * * 0 restic forget --keep-daily 7 --keep-weekly 4 --prune >> /var/log/restic.log 2>&1

That is the 3-2-1 rule, automated: three copies (live, local, off-site), two media, one off-site. If you would rather sync between your own machines instead of paying a provider, the Syncthing guide covers a no-cloud alternative for the off-site leg.

2. Automated Updates

Two layers update on their own schedule: your Docker containers and the host operating system underneath them.

Containers with Watchtower

Watchtower watches your running containers and pulls newer images on a schedule. Full setup and the notification config are in the Watchtower guide. The short version:

services:
  watchtower:
    image: containrrr/watchtower:latest
    container_name: watchtower
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      - WATCHTOWER_CLEANUP=true
      - WATCHTOWER_SCHEDULE=0 0 4 * * *
    restart: unless-stopped

The rule I follow: let Watchtower update everything on rolling tags, and opt out the fragile, stateful containers so I update those deliberately. Add this label to anything you want to hold back, database containers especially:

    labels:
      - "com.centurylinklabs.watchtower.enable=false"

This is exactly why the volume backups above run first, at 2:30am, before Watchtower runs at 4am. If a 4am update breaks a service, last night’s volume archive is sitting right there.

The host with unattended-upgrades

Watchtower handles containers, but the Linux host they run on needs patches too. On a Debian or Ubuntu VM, unattended-upgrades applies security updates automatically:

sudo apt install unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades

That enables automatic security patching with no further babysitting. For Proxmox itself, I patch the host manually rather than unattended, because a hypervisor reboot takes everything down with it. Take a snapshot, run the updates, confirm the node comes back, then move on. Some things stay deliberate on purpose.

3. Monitoring and Alerts

Here is the part people skip, and it is the part that makes everything above trustworthy. A backup job that silently stopped running two weeks ago is worse than no backup, because you think you are covered.

Uptime Kuma for services

Uptime Kuma gives you a dashboard plus alerts for every service. Point an HTTP monitor at each one and configure a notification channel (Telegram, Discord, ntfy, email). When a service stops answering, you get a message in under a minute.

Push monitors as a dead man’s switch

The real trick for automation is the push monitor. Instead of Uptime Kuma checking your service, your scheduled job checks in with Uptime Kuma. If the check-in never arrives, you get alerted.

In Uptime Kuma, add a monitor of type Push. It gives you a URL. Then append a curl to the end of each backup script so it only pings on success:

# Last line of backup-volumes.sh, after the work succeeds
curl -fsS "https://uptime.yourdomain.com/api/push/AbCdEf123?status=up&msg=ok" > /dev/null

Set the monitor’s expected interval to a bit longer than the job’s schedule, say 26 hours for a daily job. If a backup fails partway, the script exits on set -e before reaching the curl, the ping never lands, and Uptime Kuma raises the alarm. This single pattern catches the failure mode that quietly ruins homelabs: a job that stopped working and told no one.

Tying It Together: A Weekly Schedule

Stacking everything on the same time slot is asking for an I/O traffic jam. Stagger the jobs so backups finish before updates start, and monitoring sits on top of all of it.

Time (daily)JobTool
2:30amDocker volume backupscron + tar
3:00amProxmox VM/LXC backupsvzdump
4:00amOff-site copy + container updatesrestic + Watchtower
ContinuousService and push monitoringUptime Kuma
Sundays 5amPrune old off-site snapshotsrestic forget

Every one of those jobs reports success to Uptime Kuma. If your phone is quiet, the homelab took care of itself last night. If it buzzes, something needs you, and you know exactly what.

For the backup-strategy thinking behind which tool does what, the homelab backup strategy breakdown compares Syncthing, restic, and Duplicati head to head.

What I Don’t Automate

Automation has a limit, and pretending otherwise is how people lose data.

Everything else, the boring nightly grind of copying files and pulling images, the machine does better than I would. Automation is not about doing more. It is about making sure the things that must happen actually happen, every single night, whether you remember them or not. A small UPS under the desk rounds it out, so a power blip during a 3am backup does not corrupt the very thing you were trying to protect.

Set this up once and your homelab stops being a pet you feed daily. It becomes infrastructure that runs itself and tells you when it needs you. That is the whole point of doing this on your own terms instead of renting it from someone else.