Backing Up a vpsFree VPS via Snapshots
This tutorial covers downloading vpsFree snapshot backups using vpsfreectl, both manually and as an automated cron job — including a couple of gotchas that aren’t obvious from the official docs.
1. Prerequisites #
Make sure your client tools are up to date. Older versions have bugs that produce cryptic Ruby errors (NoMethodError, undefined method 'action') instead of a real error message:
gem update vpsfree-client
You want at least vpsfree-client 0.20.1. This also pulls in a compatible vpsadmin-client / haveapi-client. Running an outdated client is the single most common cause of snapshot-download commands failing for no obvious reason.
2. Manual snapshot download #
The straightforward way is interactive:
vpsfreectl snapshot download
You’ll be shown a numbered list of available snapshots for your VPS and asked to pick one:
VPS #xxxxx
(1) @2026-07-11T23:00:30Z
...
(14) @2026-07-24T23:00:34Z
Pick a snapshot for download: 14
Note: the number you type here (14) is just the position in this list, not the actual snapshot ID — that distinction matters for automation (see below).
3. Automating downloads (e.g. via cron) #
For unattended backups you need the real snapshot ID, not the list position. Get it like this:
#!/bin/bash
# Downloads the latest snapshot of the given vpsFree VPS via vpsfreectl.
# If the latest snapshot is locked (423 Locked), tries the previous one.
# Saves to BACKUP_DIR and keeps only the last KEEP_COUNT backups.
# Usage: vpsfree-download-latest-snapshot.sh [VPS_ID]
# Without a parameter.
set -euo pipefail
vps_id="${1:-xxxxx}"
backup_dir="/mnt/usb/backups/vpsfree"
keep_count=3
mkdir -p "$backup_dir"
cd "$backup_dir"
dataset_id=$(vpsfreectl -H --columns -o dataset.id vps show "$vps_id" | tr -d '[:space:]')
if [ -z "$dataset_id" ]; then
echo "Failed to determine dataset_id for VPS $vps_id" >&2
exit 1
fi
mapfile -t snapshot_ids < <(vpsfreectl -H --columns -o id dataset.snapshot list "$dataset_id" | awk '{gsub(/[ \t]/,""); print}' | tac)
if [ "${#snapshot_ids[@]}" -eq 0 ]; then
echo "Could not find any snapshot for dataset $dataset_id" >&2
exit 1
fi
downloaded=0
for snapshot_id in "${snapshot_ids[@]}"; do
echo "VPS $vps_id -> dataset $dataset_id -> snapshot $snapshot_id"
if output=$(vpsfreectl snapshot download "$snapshot_id" 2>&1); then
echo "$output"
downloaded=1
break
else
echo "$output" >&2
if echo "$output" | grep -q "RestClient::Locked"; then
echo "Snapshot $snapshot_id is locked, trying the previous one..." >&2
continue
else
exit 1
fi
fi
done
if [ "$downloaded" -ne 1 ]; then
echo "Failed to download any snapshot (all locked?)" >&2
exit 1
fi
# Keep only the last $keep_count backups
ls -1t "$backup_dir"/*.tar.gz 2>/dev/null | tail -n +$((keep_count + 1)) | while read -r old_file; do
echo "Deleting old backup: $old_file"
rm -f "$old_file"
done
What this does:
- looks up the dataset ID belonging to your VPS
- lists that dataset’s snapshots and takes the last one (most recent)
- downloads it by ID directly, skipping the interactive picker
4. Snapshots being “locked” #
If a scheduled download fails with a “locked” error, it’s most likely timing. vpsFree runs its own backup/snapshot job around 02:22 server time, and datasets stay locked for roughly 2–4 hours afterward. Schedule your cron job outside that window — e.g. not before 07:00.
5. Quick checklist #
-
vpsfree-clientupdated to ≥ 0.20.1 - Cron script uses the real snapshot ID, not the interactive list number
- All variables in the script are quoted
- Cron job scheduled well after ~06:00–07:00 to avoid the lock window
- Enough disk space at the destination (snapshots can be several GB; interrupted downloads can resume by overwriting/reusing the existing partial file)
That’s it — with a current client and the ID-based script above, snapshot downloads can run unattended.