Introduction
Proxmox VE will happily back up your VMs on a schedule you set once and never think about again. Snapshots don't get that treatment. Open the web interface, click on a VM, and you'll find a Snapshots tab with a Take Snapshot button — and that's it. No calendar, no recurring job, no "every night at 2 AM" checkbox anywhere in sight.
That surprises a lot of people coming from VMware, where scheduled snapshots have been a checkbox feature for years. In Proxmox VE, if you want a snapshot taken automatically before every risky change, or a rolling set of nightly checkpoints on a VM you tinker with a lot, you have to build it yourself with a couple of command-line tools and a cron job.
The good news: it's a short script, not a project. This walkthrough covers exactly how to do it on Proxmox VE 9.2, running on Debian 13.5 "Trixie," though the commands themselves haven't changed meaningfully since 7.x.
What You Will Learn
- Why Proxmox VE schedules backups but not snapshots, and what that distinction actually means
- The
qmandpctcommands that create, list, and remove snapshots - How to write a small script that takes a snapshot and automatically deletes the oldest ones
- How to run that script on a schedule with cron, and confirm it actually fired
- Which storage types support snapshots in the first place — quite a few common setups don't
- The errors you're most likely to hit and how to work through them
What Is This Feature?
A snapshot in Proxmox VE is a saved state of a VM's or container's disk at one exact moment. Roll back to it later, and everything written since — new files, package updates, a botched config change — disappears, and the disk looks exactly like it did when the snapshot was taken.
That's different from a backup. A vzdump backup, whether it lands on local storage or a Proxmox Backup Server, is a full standalone copy stored somewhere else. A snapshot lives inside the same storage as the VM's disk and depends on it being intact; if that storage dies, the snapshots go with it. Snapshots are also fast to create, usually a second or two, because they don't copy any data up front — they just start tracking changes from that point on.
Which storage types can even do this depends on how the disk is stored. ZFS is a copy-on-write filesystem that Proxmox can use for VM storage, and that copy-on-write design is exactly what makes ZFS snapshots cheap and near-instant. LVM-thin works similarly: it only allocates disk space as data is actually written, and that same mechanism is what lets it track a snapshot's changes separately from the live disk. Ceph RBD is Proxmox's clustered storage system, where a VM's disk is spread across multiple nodes instead of sitting on just one — it supports snapshots too, and since it's shared storage, those snapshots work no matter which node the VM happens to be running on.
What this tutorial adds isn't a new Proxmox feature. It's automation glue: a shell script that calls the same qm snapshot and qm delsnapshot commands you'd type by hand, fired on a timer by cron, the job scheduler that's shipped with Linux for decades. Nothing here changes how snapshots work under the hood — it just removes the part where you have to remember to click a button.
Why Would You Use It?
Say you run a Home Assistant VM that you tinker with constantly — new integrations, YAML edits, the occasional add-on that breaks the whole automation engine. Taking a manual snapshot every single time you touch it is tedious enough that most people give up on it after a week. A cron job removes that friction entirely. The safety net is just there, always current, without you doing anything.
It's also useful when you want a short rolling history instead of one "before I started" checkpoint. A handful of nightly snapshots let you jump back a few hours or days if you notice a problem that wasn't obvious right away — a slow memory leak, a corrupted database row, a guest-side cron job that went sideways three days ago and nobody caught it.
One honest caveat, and it matters: this is not a backup strategy. If you're protecting against a failed disk or a dead node, snapshots won't save you — they live on the exact same storage as the disk they're tracking. Keep using vzdump or Proxmox Backup Server for real disaster recovery, and treat scheduled snapshots as a short-term undo button, not a replacement for backups.
Prerequisites
- A working Proxmox VE 9.x host (everything here also works on 8.x — the
qm/pctcommands haven't changed) - At least one VM or LXC container — a lightweight, kernel-sharing alternative to a full VM — whose disk sits on a storage type that supports snapshots
- Root shell access to the Proxmox host, either through the Shell button in the web GUI or over SSH
- Basic comfort editing a file with
nano, and a rough idea of what cron does - Some free space on the storage backing the VM — snapshots grow as data changes, and a nearly full ZFS pool or thin pool will bite you here
Not every storage type plays along, and it's worth checking before you build anything on top of it:
| Storage Type | Snapshots Supported? | Notes |
|---|---|---|
| ZFS (local) | Yes | Copy-on-write by design, no size penalty until data actually changes |
| LVM-thin | Yes | Local to the node; the default under local-lvm on most installs |
| Ceph RBD | Yes | Works across the whole cluster since Ceph is shared storage |
| Directory storage with qcow2 disks | Yes | Support comes from the qcow2 format itself, not the directory |
| Plain LVM (thick) | No | No copy-on-write layer, so there's nothing to track changes with |
| Directory storage with raw disks | No | The raw format carries no snapshot metadata |
| Passed-through physical disk or LUN | No | Proxmox has no visibility into the storage underneath |
Step-by-Step Tutorial
Step 1: Confirm Your Storage Actually Supports Snapshots
Before automating anything, take one manual snapshot from the GUI. Click your VM in the left-hand tree, open the Snapshots tab, and click Take Snapshot. If the button is grayed out, or the task fails immediately with something like "snapshot feature is not available," your disk is on plain LVM or a raw-format directory disk. Move it to ZFS, LVM-thin, or Ceph RBD first — none of the automation below will work until you do.
Step 2: Find Your VM or Container ID
Every guest in Proxmox VE has a numeric ID, shown next to its name in the left-hand tree. You can also list them from the shell:
qm list
For containers, use the equivalent command:
pct list
Note the ID of the guest you want to snapshot automatically. This tutorial uses 100 as the example VMID.
Step 3: Write the Snapshot Script
Log into the Proxmox host's shell — the Shell button under the node in the GUI, or SSH as root — and create a new script:
nano /usr/local/bin/snapshot-vm100.sh
Paste this in:
#!/bin/bash
VMID=100
KEEP=7
SNAPNAME="auto-$(date +%Y%m%d-%H%M)"
qm snapshot "$VMID" "$SNAPNAME" --description "Automated snapshot via cron"
qm listsnapshot "$VMID" \
| grep -oP 'auto-\d{8}-\d{4}' \
| sort \
| head -n -"$KEEP" \
| while read -r OLDSNAP; do
qm delsnapshot "$VMID" "$OLDSNAP"
done
Save it — Ctrl+O, Enter, then Ctrl+X in nano — and make it executable:
chmod +x /usr/local/bin/snapshot-vm100.sh
Step 4: Test It Before You Trust It to Cron
Run it by hand once:
/usr/local/bin/snapshot-vm100.sh
Then confirm the snapshot actually landed:
qm listsnapshot 100
You should see a new entry named something like auto-20260824-1430. Run the script by hand a few more times — every snapshot needs a unique name, and the timestamp handles that automatically — and confirm that once you pass seven snapshots, the oldest one gets removed on its own. This is the step people skip, and it's the one that saves you from discovering your pruning logic is broken at 2 AM when a thin pool fills up instead of during a calm afternoon test.
Step 5: Schedule It With Cron
Open the root crontab:
crontab -e
Add a line to run the script every night at 2 AM:
0 2 * * * /usr/local/bin/snapshot-vm100.sh >> /var/log/snapshot-vm100.log 2>&1
The five fields before the command are minute, hour, day of month, month, and day of week — 0 2 * * * means "2:00 AM, every day." Redirecting output to a log file matters more than it looks: if qm snapshot ever fails quietly in the background, you'll have a record of what happened instead of just noticing weeks later that snapshots stopped appearing.
Save and exit. Cron picks up the new job immediately, with no service restart needed.
Doing the Same Thing for an LXC Container
Containers use pct instead of qm, but the pattern is identical:
#!/bin/bash
CTID=101
KEEP=7
SNAPNAME="auto-$(date +%Y%m%d-%H%M)"
pct snapshot "$CTID" "$SNAPNAME" --description "Automated snapshot via cron"
pct listsnapshot "$CTID" \
| grep -oP 'auto-\d{8}-\d{4}' \
| sort \
| head -n -"$KEEP" \
| while read -r OLDSNAP; do
pct delsnapshot "$CTID" "$OLDSNAP"
done
Save this as /usr/local/bin/snapshot-ct101.sh, make it executable, test it the same way, then add its own line to the crontab. Keep VM and container scripts separate rather than looping over a mixed list of IDs — when something eventually fails, you want the log to tell you immediately whether it was a VM or a container, not make you guess.
Commands Explained
qm snapshot <vmid> <name> creates a new snapshot of a VM's disks, and its RAM as well if you add --vmstate 1. Without that flag, you get a disk-only, crash-consistent snapshot — restoring it is a lot like recovering from a sudden power loss, which is fine for most workloads and considerably faster to create.
qm listsnapshot <vmid> prints every snapshot for that VM as a small tree, since snapshots can technically be layered on top of one another. Each entry shows a snapshot's name, and current marks where the VM actually sits right now.
qm delsnapshot <vmid> <name> permanently removes one snapshot. It merges that snapshot's changes into whichever point comes next in the chain, so deleting an old one doesn't disturb anything newer.
qm rollback <vmid> <name> is the command you'll actually reach for in an emergency. It discards every change made since that snapshot was taken and puts the VM back exactly where it was, stopping it first if it's currently running.
pct snapshot, pct listsnapshot, pct delsnapshot, and pct rollback are the same four operations for LXC containers, just swapping a VMID for a CTID.
date +%Y%m%d-%H%M generates a sortable timestamp like 20260824-1430. The year-month-day-hour-minute order matters here: it means a plain alphabetical sort is also a chronological sort, which is exactly what the pruning logic in the script relies on.
head -n -7 is easy to misread the first time you see it. It doesn't mean "the first 7 lines" — the leading minus sign flips it to mean "everything except the last 7 lines." That's the whole trick behind keeping the newest snapshots and only feeding the older ones to delsnapshot.
Common Errors
"snapshot feature is not available" — the storage backing the disk doesn't support snapshots at all. Plain LVM and raw-format directory disks both hit this. The fix is to move the disk to ZFS, LVM-thin, or Ceph RBD, not to keep retrying the same command.
"VM is locked (snapshot)" — a previous snapshot, backup, or rollback task is still running, or crashed mid-operation, right as your cron job tried to fire. Check qm status <vmid> first. If nothing is actually running and the lock is stale, clear it with qm unlock <vmid> — but only after you've confirmed that, since force-unlocking a genuinely active operation can corrupt the disk.
A storage-full failure during qm snapshot — the exact wording varies by storage backend, but the cause is the same either way: the ZFS pool or LVM-thin pool backing the disk ran out of space to track new changes. Free up space, shrink the retention count, or move the disk to bigger storage before running the script again.
Cron job silently never runs — usually not an error at all, just a missing execute bit or a path problem. Confirm the script is executable with ls -l /usr/local/bin/snapshot-vm100.sh, and if the script calls anything by a bare name, remember that cron's environment doesn't source your interactive shell's profile, so a working command in your terminal can still fail with "command not found" under cron.
Troubleshooting
Start by checking whether cron fired at all. On Debian 13, that means looking at the system journal:
journalctl -u cron --since "1 hour ago"
You should see a line referencing your script around the scheduled time. If there's nothing there, the crontab entry itself is the problem — double check the syntax with crontab -l, since a single misplaced asterisk will make the whole line silently invalid.
If cron did fire but the snapshot didn't appear, check the log file you redirected output to:
cat /var/log/snapshot-vm100.log
Whatever qm snapshot printed when it failed will be sitting right there. From there, run the script manually as root — errors that get swallowed under cron's minimal environment often show up clearly when you run the same command interactively.
Keep an eye on actual disk usage too, especially in the first couple of weeks:
zfs list
or, for LVM-thin:
lvs
Watching how fast a pool fills up tells you whether your KEEP value is realistic for that particular VM, or whether it needs to come down.
Best Practices
Set your KEEP count based on the busiest day that VM is likely to see, not an average day — a database VM under heavy write load will consume far more snapshot overhead per day than a quiet DNS server will.
Only use --vmstate 1 when you genuinely need RAM captured, such as right before an in-place OS upgrade. It briefly pauses the VM and needs enough free RAM and swap on the node to write out the state file, which isn't something you want happening on every nightly run.
Give automated snapshots a clear, consistent name prefix — this tutorial uses auto- — so a manually created snapshot named something like before-migration never gets swept up by the pruning loop by accident.
Avoid scheduling a VM's snapshot script at the same minute as its Proxmox Backup Server job. Both want an exclusive lock on the guest, and running them back to back is simpler to reason about than debugging an occasional lock collision.
Test a rollback occasionally, on a VM where it doesn't matter if something goes wrong. It's a lot better to learn that qm rollback behaves exactly as you expect during a quiet afternoon than to find out otherwise during an actual incident.
Frequently Asked Questions
Can I schedule snapshots from the Proxmox GUI instead of cron?
Not as of Proxmox VE 9.2. The GUI can schedule vzdump backup jobs, but there's no built-in scheduler for snapshots — cron, or another external scheduler, is the only route right now.
Do snapshots slow the VM down?
Taking one is nearly instant and won't be noticeable. Living with several active snapshots on ZFS or LVM-thin adds a small amount of write overhead from tracking changes as deltas, but it's rarely enough to notice on a typical homelab workload.
What happens if I snapshot a VM while it's running?
Proxmox takes it live by default, with no downtime — the disk state is crash-consistent rather than perfectly clean, similar to a sudden reboot. Add --vmstate 1 if you also want RAM captured, which does briefly pause the VM.
Does this work on a cluster, not just a single node?
Yes, as long as the storage supports it across nodes. Ceph RBD works cluster-wide by nature, while ZFS and LVM-thin snapshots stay local to whichever node the VM's disk actually lives on.
Is this the same thing as Proxmox Backup Server's retention settings?
No. PBS retention prunes full backup copies stored separately from the VM. This script prunes snapshots that live inside the same storage as the VM's disk — a different mechanism, doing a different job.
Conclusion
There's a reasonable argument that Proxmox should just ship a snapshot scheduler at this point, given how often it comes up on the forums. Until that happens, a ten-line script and one crontab entry get you there just as reliably — and you end up understanding exactly what's happening instead of trusting a checkbox you can't see inside.
Start with a conservative KEEP value on a VM you don't mind experimenting on, watch how much space a week of snapshots actually uses, and adjust from there. Once it's running, the safety net is just quietly there every time you need it, which for most people turns out to be more often than they expected.