Proxmox Backup Strategy: Git + HDD + Google Drive

May 30, 2026Tutorials

A complete walkthrough for setting up automated, multi-layered backups for your Proxmox home server — config files to GitHub, LXC snapshots to HDD, and offsite copies to Google Drive.

Proxmox Backup Strategy: Git + HDD + Google Drive

Running a home server is great until something breaks and you realize you never backed it up. This guide walks through a complete, three-layer backup strategy for a Proxmox host running LXC containers:

  1. Git backup — Proxmox config files versioned on a private GitHub repo
  2. HDD backup — Full LXC container snapshots stored on a local HDD
  3. GDrive offsite — Latest backup per container synced to Google Drive

Everything is automated via cron and designed to survive a full Proxmox reinstall.


System Reference

PropertyValue
Proxmox host IP192.168.0.150
SSH userroot
HDD mount path/mnt/hdd
Backup storage path/mnt/hdd/proxmox-backups

Containers in scope

VMIDNameBackup Type
151img-serverAutomated (weekly)
152caddyAutomated (weekly)
153jellyfinAutomated (weekly)
154uptimekumaAutomated (weekly)
155forgeManual one-time only
156nexusManual one-time only

Containers 155 and 156 are stopped and only need a one-time snapshot — they won't be part of the automated schedule.


Part 1 — Git Backup of Config Files

What we're backing up and why

Proxmox stores all of its configuration — VM/LXC definitions, storage, network, users — under /etc/pve/ and a few other /etc/ files. These are plain text files, which makes them perfect for version control.

The idea here is simple: copy these files into a local git repo, commit any changes, and push to a private GitHub repo. If your host ever dies or you reinstall Proxmox, you can pull this repo and have your entire config back in seconds.

Files we track:

  • /etc/pve/ — all Proxmox configs (LXC configs, storage definitions, cluster state)
  • /etc/network/interfaces — network bridge/VLAN config
  • /etc/hosts
  • /etc/hostname

Step 1.1 — SSH in and install Git

ssh root@192.168.0.150
apt update && apt install git -y

Then set your git identity:

git config --global user.name "Your Name"
git config --global user.email "your@email.com"

Step 1.2 — Generate an SSH key for GitHub

We'll create a dedicated SSH key for this backup job rather than reusing any existing key. This keeps things clean and lets you revoke it independently if needed.

ssh-keygen -t ed25519 -C "proxmox-backup" -f ~/.ssh/github_backup -N ""
cat ~/.ssh/github_backup.pub

Copy the output and add it to your GitHub account: GitHub → Settings → SSH and GPG Keys → New SSH Key

Now configure SSH on the Proxmox host to use this key for GitHub:

cat >> ~/.ssh/config << 'EOF'
Host github.com
    HostName github.com
    User git
    IdentityFile ~/.ssh/github_backup
    IdentitiesOnly yes
EOF
chmod 600 ~/.ssh/config

Test it:

ssh -T git@github.com

Expected output:

Hi seemantshekhar43! You've successfully authenticated, but GitHub does not provide shell access.

Step 1.3 — Initialize the local config repo

Create the directory that will hold our backed-up configs, initialize a git repo, and point it at your GitHub remote. Create the private GitHub repo first if you haven't already (e.g. homelab-config-backup).

mkdir -p /opt/homelab-config-backup
cd /opt/homelab-config-backup
git init
git remote add origin git@github.com:YOUR_GITHUB_USERNAME/homelab-config-backup.git

Add a .gitignore to make sure private keys and certificates don't get committed:

cat > .gitignore << 'EOF'
*.key
*.pem
authkey
priv/
EOF

Step 1.4 — Create the backup script

This script copies the config files into the repo, stages everything, and only commits + pushes if something actually changed. That way you don't end up with empty commits polluting the history.

cat > /usr/local/bin/git-config-backup.sh << 'EOF'
#!/bin/bash

REPO_DIR="/opt/homelab-config-backup"
LOG="/var/log/git-config-backup.log"
DATE=$(date '+%Y-%m-%d %H:%M:%S')

echo "[$DATE] Starting config backup" >> $LOG

# Copy configs into repo
cp -r /etc/pve "$REPO_DIR/"
cp /etc/network/interfaces "$REPO_DIR/"
cp /etc/hosts "$REPO_DIR/"
cp /etc/hostname "$REPO_DIR/"


# Copy scripts into repo
cp /usr/local/bin/git-config-backup.sh "$REPO_DIR/scripts/"
cp /usr/local/bin/lxc-backup.sh "$REPO_DIR/scripts/"
cp /usr/local/bin/gdrive-sync.sh "$REPO_DIR/scripts/"

cd "$REPO_DIR"

git add -A

# Only commit if there are changes
if git diff --cached --quiet; then
    echo "[$DATE] No changes to commit" >> $LOG
else
    git commit -m "config backup: $DATE"
    git push origin main >> $LOG 2>&1
    echo "[$DATE] Pushed to GitHub successfully" >> $LOG
fi
EOF

chmod +x /usr/local/bin/git-config-backup.sh

Step 1.5 — Do the initial push

cd /opt/homelab-config-backup
git add -A
git commit -m "initial config backup"
git branch -M main
git push -u origin main

Note: If you initialized git and your branch is called master, the git branch -M main renames it. You can skip this if your repo is already on main.


Part 2 — LXC Backups to Local HDD

What we're doing here

vzdump is Proxmox's built-in backup tool. It can snapshot a running container (using the snapshot mode) and write a compressed archive to a storage target. We're going to register our HDD as a Proxmox storage backend, then write a script that dumps LXCs 151–154 to it weekly, keeping the two most recent backups per container.

Step 2.1 — Register the HDD as a backup storage target

First, add the HDD directory to Proxmox's storage config and create the dump subdirectory (Proxmox writes backups there by convention):

pvesm add dir hdd-backup --path /mnt/hdd/proxmox-backups --content backup
mkdir -p /mnt/hdd/proxmox-backups/dump

You can verify it appeared in the Proxmox web UI under Datacenter → Storage.

Step 2.2 — Create the LXC backup script

The script loops over each container ID, runs vzdump, then prunes old backups so only the two most recent archives per VMID are kept. The --mode snapshot flag means the container doesn't need to be stopped.

cat > /usr/local/bin/lxc-backup.sh << 'EOF'
#!/bin/bash

STORAGE="hdd-backup"
BACKUP_DIR="/mnt/hdd/proxmox-backups/dump"
LOG="/var/log/lxc-backup.log"
DATE=$(date '+%Y-%m-%d %H:%M:%S')
VMIDS="151 152 153 154"
KEEP=2

echo "[$DATE] Starting LXC backup" >> $LOG

for VMID in $VMIDS; do
    echo "[$DATE] Backing up VMID $VMID" >> $LOG
    vzdump $VMID \
        --storage $STORAGE \
        --mode snapshot \
        --compress zstd \
        --quiet 1 >> $LOG 2>&1

    if [ $? -eq 0 ]; then
        echo "[$DATE] VMID $VMID backup complete" >> $LOG
    else
        echo "[$DATE] ERROR: VMID $VMID backup failed" >> $LOG
    fi

    # Prune old backups — keep only last $KEEP for this VMID
    ls -t "$BACKUP_DIR"/vzdump-lxc-${VMID}-*.tar.zst 2>/dev/null | \
        tail -n +$((KEEP + 1)) | \
        xargs -r rm -v >> $LOG 2>&1
done

echo "[$DATE] LXC backup complete" >> $LOG
EOF

chmod +x /usr/local/bin/lxc-backup.sh

Step 2.3 — Manual one-time backup for stopped containers

For VMIDs 155 and 156 (which are stopped), we use --mode stop instead of snapshot. Run these once manually:

vzdump 155 --storage hdd-backup --mode stop --compress zstd
vzdump 156 --storage hdd-backup --mode stop --compress zstd

Sample output for VMID 155:

INFO: starting new backup job: vzdump 155 --storage hdd-backup --mode stop --compress zstd
INFO: Starting Backup of VM 155 (lxc)
INFO: Backup started at 2026-05-30 13:50:04
INFO: status = stopped
INFO: backup mode: stop
INFO: CT Name: forge
INFO: creating vzdump archive '/mnt/hdd/proxmox-backups/dump/vzdump-lxc-155-2026_05_30-13_50_04.tar.zst'
INFO: Total bytes written: 672266240 (642MiB, 132MiB/s)
INFO: archive file size: 196MB
INFO: Finished Backup of VM 155 (00:00:48)
INFO: Backup job finished successfully

These one-time backups are not part of the automated schedule.


Part 3 — Offsite Sync to Google Drive

Why bother with Google Drive?

Local HDD backups are great, but they won't survive a house fire or a drive failure that takes the HDD with it. Syncing the latest backup per container to Google Drive gives you an offsite copy without paying for S3 or Backblaze.

We use rclone for this — it's a Swiss Army knife for cloud storage sync and has excellent Google Drive support.

Step 3.1 — Install rclone

apt install rclone -y

Step 3.2 — Configure the Google Drive remote

Run the interactive config and follow the prompts:

rclone config

Key choices:

  • Select n for new remote
  • Name it gdrive
  • Storage type: 18 (Google Drive)
  • Leave client_id and client_secret blank
  • Scope: 1 (full access)
  • Leave all other fields blank
  • Advanced config: n

For the OAuth step — if you're on a headless server (which Proxmox is), you have two options:

Option A (easier): Say Y to auto config — rclone will print a local URL like http://127.0.0.1:53682/auth?.... From your local machine, open an SSH tunnel:

# Run this on your LOCAL machine (not the Proxmox host)
ssh -L 53682:localhost:53682 root@192.168.0.150

Then open the URL in your browser, authorize, and the terminal on the Proxmox host will receive the code automatically.

Option B: Say N to auto config — rclone will give you a URL to open in any browser, and you paste the code back manually.

After authorizing, confirm with y and quit config with q.

Verify and create the target folder:

rclone lsd gdrive:
rclone mkdir gdrive:proxmox-backups

Step 3.3 — Create the GDrive sync script

This script finds the most recent backup file for each container on the HDD, uploads it to Google Drive, and deletes any older backups for that container from Drive. The result: exactly one backup per container in GDrive at all times.

cat > /usr/local/bin/gdrive-sync.sh << 'EOF'
#!/bin/bash

BACKUP_DIR="/mnt/hdd/proxmox-backups/dump"
GDRIVE_DIR="gdrive:proxmox-backups"
LOG="/var/log/gdrive-sync.log"
DATE=$(date '+%Y-%m-%d %H:%M:%S')
VMIDS="151 152 153 154"

echo "[$DATE] Starting GDrive sync" >> $LOG

for VMID in $VMIDS; do
    LATEST=$(ls -t "$BACKUP_DIR"/vzdump-lxc-${VMID}-*.tar.zst 2>/dev/null | head -1)

    if [ -z "$LATEST" ]; then
        echo "[$DATE] No backup found for VMID $VMID, skipping" >> $LOG
        continue
    fi

    FILENAME=$(basename "$LATEST")
    echo "[$DATE] Syncing $FILENAME to GDrive" >> $LOG

    rclone copy "$LATEST" "$GDRIVE_DIR" \
        --transfers 1 \
        --drive-chunk-size 128M \
        --log-file "$LOG" \
        --log-level INFO

    # Delete older backups for this VMID from GDrive (keep only latest)
    rclone lsf "$GDRIVE_DIR" --files-only | \
        grep "vzdump-lxc-${VMID}-" | \
        grep -v "$FILENAME" | \
        while read OLD; do
            echo "[$DATE] Removing old GDrive backup: $OLD" >> $LOG
            rclone delete "$GDRIVE_DIR/$OLD" >> $LOG 2>&1
        done
done

echo "[$DATE] GDrive sync complete" >> $LOG
EOF

chmod +x /usr/local/bin/gdrive-sync.sh

Part 4 — Scheduling with Cron

Now we tie it all together. The schedule is staggered intentionally: LXC backups run first, then the GDrive sync picks up the freshly written files two hours later.

TaskScheduleTime
LXC backup to HDDWednesday + Sunday2:00 AM
GDrive syncWednesday + Sunday4:00 AM
Git config backupMonday + Thursday + Saturday3:00 AM
crontab -e

Add these lines:

# LXC backup to HDD — Wed and Sun at 2AM
0 2 * * 0,3 /usr/local/bin/lxc-backup.sh

# GDrive sync — Wed and Sun at 4AM (2 hours after backup starts)
0 4 * * 0,3 /usr/local/bin/gdrive-sync.sh

# Git config backup — Mon, Thu, Sat at 3AM
0 3 * * 1,4,6 /usr/local/bin/git-config-backup.sh

Confirm the crontab was saved:

crontab -l

Part 5 — Version the Scripts Themselves

This is a small but important step. We copy the three backup scripts into the git repo so they're versioned and recoverable. If you ever rebuild the server, you can pull the scripts directly from GitHub instead of recreating them from memory.

mkdir -p /opt/homelab-config-backup/scripts
cp /usr/local/bin/git-config-backup.sh /opt/homelab-config-backup/scripts/
cp /usr/local/bin/lxc-backup.sh /opt/homelab-config-backup/scripts/
cp /usr/local/bin/gdrive-sync.sh /opt/homelab-config-backup/scripts/
cd /opt/homelab-config-backup
git add scripts/
git commit -m "add backup scripts"
git push origin main

From here on, every time git-config-backup.sh runs, it does git add -A which will automatically pick up any modifications to the scripts directory as well.


Part 6 — Test Everything Manually

Before trusting cron to do this overnight, run each script once and check the logs.

# Test git config backup
/usr/local/bin/git-config-backup.sh
cat /var/log/git-config-backup.log

Expected log output:

[2026-05-30 15:49:50] Starting config backup
[2026-05-30 15:49:50] Pushed to GitHub successfully
# Test LXC backup
/usr/local/bin/lxc-backup.sh
cat /var/log/lxc-backup.log

Expected:

[2026-05-30 15:50:34] Starting LXC backup
[2026-05-30 15:50:34] Backing up VMID 151
[2026-05-30 15:50:34] VMID 151 backup complete
...
[2026-05-30 15:50:34] LXC backup complete
# Test GDrive sync (run after the LXC backup completes)
/usr/local/bin/gdrive-sync.sh
cat /var/log/gdrive-sync.log

The GDrive sync will take a while depending on container sizes — rclone logs progress every minute. VMID 151 in the test run was ~5.4 GiB and took about 3.5 minutes. When it's done you should see something like:

[2026-05-30 16:17:25] GDrive sync complete

Verify files landed on GDrive:

rclone lsf gdrive:proxmox-backups
vzdump-lxc-151-2026_05_30-15_50_34.tar.zst
vzdump-lxc-152-2026_05_30-15_52_34.tar.zst
vzdump-lxc-153-2026_05_30-15_52_58.tar.zst
vzdump-lxc-154-2026_05_30-15_54_15.tar.zst

Verify files on HDD:

ls -lh /mnt/hdd/proxmox-backups/dump/

And open your GitHub repo in a browser to confirm the config files and scripts are committed.


Log File Reference

LogPath
LXC backup/var/log/lxc-backup.log
GDrive sync/var/log/gdrive-sync.log
Git config backup/var/log/git-config-backup.log

To tail any log in real time:

tail -f /var/log/lxc-backup.log

Restore Playbook

Restore a single LXC from HDD

# List what's available
ls /mnt/hdd/proxmox-backups/dump/

# Restore (swap out the VMID and filename)
pct restore 153 /mnt/hdd/proxmox-backups/dump/vzdump-lxc-153-YYYY_MM_DD-HH_MM_SS.tar.zst \
    --storage local-lvm \
    --start

Restore a single LXC from Google Drive

# Pull down from GDrive first
rclone copy gdrive:proxmox-backups/vzdump-lxc-153-*.tar.zst /mnt/hdd/proxmox-backups/dump/

# Then restore as above
pct restore 153 /mnt/hdd/proxmox-backups/dump/vzdump-lxc-153-*.tar.zst \
    --storage local-lvm \
    --start

Full host recovery (fresh Proxmox install)

If you're starting from scratch after a reinstall:

  1. Re-add the HDD storage so Proxmox can see existing backups:
pvesm add dir hdd-backup --path /mnt/hdd/proxmox-backups --content backup
  1. Restore network config from GitHub:
git clone git@github.com:YOUR_USERNAME/homelab-config-backup.git /tmp/config-restore
cp /tmp/config-restore/interfaces /etc/network/interfaces
systemctl restart networking
  1. Restore each container:
pct restore 151 /mnt/hdd/proxmox-backups/dump/vzdump-lxc-151-*.tar.zst --storage local-lvm
pct restore 152 /mnt/hdd/proxmox-backups/dump/vzdump-lxc-152-*.tar.zst --storage local-lvm
pct restore 153 /mnt/hdd/proxmox-backups/dump/vzdump-lxc-153-*.tar.zst --storage local-lvm
pct restore 154 /mnt/hdd/proxmox-backups/dump/vzdump-lxc-154-*.tar.zst --storage local-lvm
  1. Start all containers:
for VMID in 151 152 153 154; do pct start $VMID; done
  1. Re-authorize rclone for Google Drive (OAuth tokens don't survive a reinstall):
rclone config
# Follow Part 3, Step 3.2 again

What You End Up With

After following this guide, your backup posture looks like this:

  • GitHub — full Proxmox config history, committed 3× per week, recoverable in seconds
  • Local HDD — 2 rolling LXC snapshots per container, backed up 2× per week
  • Google Drive — 1 offsite copy per container, synced every backup run

Three independent failure modes means you'd have to lose all three simultaneously to truly be stuck. For a home server, that's a pretty good place to be.