DIY VPS Backups Without Paying Your Host for Them
Back up an Ubuntu VPS with Restic to S3 or Google Drive. Set up encryption, scheduled snapshots, retention, and a restore drill before you need recovery.

On this page
- Choose what must survive losing the VPS
- Install the tools and protect the recovery password
- Connect either S3 storage or Google Drive
- Prepare consistent data and install the backup scripts
- Initialize the repository and inspect the first snapshot
- Schedule the job and make missed backups visible
- Preview retention before deleting recovery points
- Restore on a replacement machine and book the next drill
- FAQ
Your VPS disappears, and the backup archive was on the same disk. Fix that by sending encrypted Restic snapshots to storage outside your hosting account. Use S3-compatible storage by default, or Google Drive through rclone. Back up application data and database-consistent exports, schedule the job, monitor missed runs, and restore onto a separate machine. You avoid the host's backup add-on, but storage and recovery still need a budget.
Choose what must survive losing the VPS
Your server stops booting, and the archive you need sits inside its inaccessible filesystem. Put the recovery copy outside the VPS and outside the hosting account that controls it. A local archive can help undo a mistake, but it cannot recover a server whose disk is gone.
This procedure targets Ubuntu 24.04 LTS on a single VPS. We recommend Restic for encrypted snapshots, with S3-compatible object storage as the default destination. Google Drive through rclone is a reasonable alternative when you already manage a suitable Drive account and accept the extra authentication dependency.
| Destination | Choose it when | Operational responsibility |
|---|---|---|
| S3-compatible object storage | You want bucket-scoped credentials and direct Restic access | Manage storage permissions and understand recovery transfer charges |
| Google Drive through rclone | You already maintain a suitable Drive account | Monitor storage quota and keep OAuth authorization working |
| Another directory on the VPS | You need temporary staging | It does not survive losing the VPS |
Write down how much recent work you can lose and how long the application can remain unavailable. Those decisions determine the backup schedule and the recovery procedure. A nightly file backup cannot satisfy an application that needs recovery close to the moment of failure.
- Include user uploads and other application files that cannot be recreated from source control.
- Include database-consistent exports or a database-native backup set, with the credentials and roles needed for recovery.
- Include system configuration and an off-server recovery note explaining how to rebuild the application.
- Exclude disposable build output and caches unless they are expensive enough to justify retaining.
The examples use /srv/app-data for persistent application files and /var/backups/app for prepared database exports. Replace these paths with your actual sources before taking the first snapshot; creating an empty directory does not protect an application stored elsewhere. If you are still choosing a backup engine, use the Restic, Borg and Kopia comparison before committing to a repository.
Install the tools and protect the recovery password
Install the distribution packages and create a private configuration directory. Restic encrypts the repository, so losing its password can make an otherwise intact backup unusable. Keep that password in an independent password manager, together with the repository address and instructions for recovering the storage account.
sudo apt-get update
sudo env DEBIAN_FRONTEND=noninteractive apt-get install -y restic rclone openssl ca-certificates
sudo install -d -m 0700 /etc/vps-backup /var/cache/vps-backup /var/backups/app
sudo install -d -m 0750 /srv/app-data
sudo bash -c 'set -e; umask 077; if [ ! -s /etc/vps-backup/password ]; then openssl rand -base64 32 > /etc/vps-backup/password; fi'
command -v restic
command -v rclone
sudo stat -c '%a %U %n' /etc/vps-backup /etc/vps-backup/passwordTransfer the password into your password manager through a trusted private session before initializing the repository. Do not paste it into an issue, agent conversation, or shared terminal transcript. A password stored only on the protected VPS disappears with the machine.
The backup job will run as root because system configuration and application exports can have restricted permissions. Keep its scripts root-owned and unavailable for modification by the application account. Apply the Ubuntu server hardening checklist alongside this setup, because encryption does not stop a compromised server from using credentials it can already read.
Connect either S3 storage or Google Drive
Choose one destination for this repository. Restic supports S3 storage directly and Google Drive through its rclone backend; the address formats differ. The official repository setup documentation describes both backends and their authentication settings.
For S3-compatible storage, create a private bucket in an account separate from your VPS provider account. Give the backup credential access only to that bucket or repository prefix, including listing, reading, writing, and deletion required by normal repository maintenance. Do not grant account administration or permission to remove the bucket.
Save the following S3 template as /etc/vps-backup/repository.env, replacing the example endpoint, bucket and credentials with values from your storage account. Use the bucket's actual HTTPS endpoint and add AWS_DEFAULT_REGION if your provider requires it. This is a configuration template, so the example values will not connect to storage.
RESTIC_REPOSITORY='s3:https://s3.example.com/vps-archive/restic'
RESTIC_PASSWORD_FILE='/etc/vps-backup/password'
RESTIC_CACHE_DIR='/var/cache/vps-backup'
AWS_ACCESS_KEY_ID='REPLACE_WITH_BUCKET_ACCESS_KEY'
AWS_SECRET_ACCESS_KEY='REPLACE_WITH_BUCKET_SECRET_KEY'For Google Drive instead, configure a remote named gdrive using the command below. Select Google Drive and complete the browser authorization flow; on a headless VPS, follow the prompt to authorize from a machine with a browser. Keep the resulting token private, and consult the rclone Google Drive documentation for scope and authorization details.
sudo rclone config --config /etc/vps-backup/rclone.conf
sudo chmod 0600 /etc/vps-backup/rclone.conf
sudo rclone --config /etc/vps-backup/rclone.conf lsd gdrive:Prefer a dedicated backup account if granting full Drive access. A folder name alone does not restrict what an OAuth token can access. Save this alternative as /etc/vps-backup/repository.env when using Drive, replacing the S3 template entirely.
RESTIC_REPOSITORY='rclone:gdrive:vps-backups/primary'
RESTIC_PASSWORD_FILE='/etc/vps-backup/password'
RESTIC_CACHE_DIR='/var/cache/vps-backup'
RCLONE_CONFIG='/etc/vps-backup/rclone.conf'Both choices leave the running VPS with credentials that can damage its backup repository. For protection against that failure, maintain an independently controlled copy or a provider-supported recovery mechanism whose deletion authority is unavailable to the VPS. Do not enable bucket expiration rules that remove live repository objects.
Prepare consistent data and install the backup scripts
Create /usr/local/sbin/vps-restic with the following contents. This wrapper gives interactive commands and the scheduled job the same configuration. Its environment file is sourced as shell code, so only root should be able to edit it.
#!/bin/bash
set -euo pipefail
export PATH=/usr/sbin:/usr/bin:/sbin:/bin
set -a
. /etc/vps-backup/repository.env
set +a
exec /usr/bin/restic "$@"Do not treat a copy of a running database directory as a recoverable database backup. Prepare a native export before Restic reads the files, and make export failure stop the job. The Postgres backup and restore procedure covers database-specific recovery requirements.
If this VPS runs a database, create an executable /etc/vps-backup/prepare-data script that produces fresh exports in /var/backups/app. Write each export to a temporary file and rename it into place only after the database tool succeeds; return a failing exit status on any error. Back up required roles separately, and do not enable the schedule until you have restored these exports successfully.
Create /usr/local/sbin/vps-backup with the following contents, adjusting the source paths. Set database_required=true if recovery depends on a database, which makes a missing preparation script fail visibly. For applications whose database records refer to uploaded files, coordinate exports and file capture with the application's supported consistency procedure, potentially including a maintenance window.
#!/bin/bash
set -euo pipefail
export PATH=/usr/sbin:/usr/bin:/sbin:/bin
database_required=false
prepare=/etc/vps-backup/prepare-data
if [[ "$database_required" == true && ! -x "$prepare" ]]; then
printf '%s\n' 'Database preparation script is required.' >&2
exit 1
fi
if [[ -x "$prepare" ]]; then
"$prepare"
fi
exec /usr/local/sbin/vps-restic backup \
--host vps-primary \
--tag production \
--exclude /etc/vps-backup \
/etc /srv/app-data /var/backups/appsudo chown root:root /etc/vps-backup/repository.env /usr/local/sbin/vps-restic /usr/local/sbin/vps-backup
sudo chmod 0600 /etc/vps-backup/repository.env
sudo chmod 0700 /usr/local/sbin/vps-restic /usr/local/sbin/vps-backup
sudo bash -n /usr/local/sbin/vps-restic
sudo bash -n /usr/local/sbin/vps-backupThe backup excludes /etc/vps-backup deliberately because it contains repository credentials. Keep a protected off-server copy of its recovery information and script configuration. Otherwise, a replacement machine will lack the instructions needed to open the repository.
Initialize the repository and inspect the first snapshot
Initialize only a new repository, then run the same backup script that the scheduler will use. If you are reconnecting to an existing repository, skip initialization and list its snapshots first. Allow enough local space for database staging as well as Restic's cache.
sudo /usr/local/sbin/vps-restic init
sudo /usr/local/sbin/vps-backup
sudo /usr/local/sbin/vps-restic snapshots --host vps-primary --tag production
sudo /usr/local/sbin/vps-restic ls latest --host vps-primary --tag productionInspect representative paths from every source you intended to protect. Pay particular attention to container volumes and application secrets stored outside /etc, because these examples will not discover them automatically. Add their real locations to the script, excluding live database files when using native exports.
Treat an incomplete backup or unreadable source as a failure requiring investigation. A repository can contain a new snapshot even when the command reports a problem, so a recent timestamp alone is insufficient. Record the expected source paths in the recovery note and review them whenever you change deployment layout.
Schedule the job and make missed backups visible
Use a systemd timer so you can inspect the schedule and job result with the server's normal service tools. Save the following service as /etc/systemd/system/vps-backup.service. Keep database preparation inside the backup script so its failure prevents a successful service result.
[Unit]
Description=Encrypted off-server backup
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
User=root
UMask=0077
ExecStart=/usr/local/sbin/vps-backup
Nice=10
TimeoutStartSec=infinitySave this timer as /etc/systemd/system/vps-backup.timer. The schedule uses the server's local timezone, and persistence allows a missed calendar activation to run after the machine returns. Change the frequency if your acceptable data-loss window is shorter than the interval shown.
[Unit]
Description=Schedule encrypted off-server backups
[Timer]
OnCalendar=*-*-* 03:00:00
RandomizedDelaySec=20m
Persistent=true
Unit=vps-backup.service
[Install]
WantedBy=timers.targetsudo systemctl daemon-reload
sudo systemctl start vps-backup.service
sudo systemctl enable --now vps-backup.timer
sudo systemctl show vps-backup.service -p Result -p ExecMainStatus
sudo systemctl list-timers vps-backup.timer --no-pager
sudo journalctl -u vps-backup.service -n 50 --no-pagerConfigure an external monitor to expect a success signal after the backup command completes successfully. Have it alert when that signal is late, allowing for normal backup duration and the randomized delay. An alert generated only on the VPS cannot tell you that the entire VPS has disappeared.
Deliberately withhold a monitoring signal and confirm that the alert reaches you before relying on it. Also inspect destination quota and repository growth during routine maintenance. We would give Claude Code redacted service logs to investigate a failed run, while keeping storage credentials and deletion authority outside its session; the AI operations guardrail guide explains that division of responsibility.
Preview retention before deleting recovery points
Keep enough history to recover from damage discovered after the next backup has already run. A useful starting policy retains daily snapshots for recent mistakes and longer-spaced snapshots for older problems. Adjust it to your data churn and how long corruption could remain unnoticed.
sudo /usr/local/sbin/vps-restic forget \
--host vps-primary --tag production \
--group-by host,paths,tags \
--keep-daily 14 --keep-weekly 8 --keep-monthly 12 \
--dry-runRestic applies retention within snapshot groups, and retained categories can overlap. forget removes snapshot references, while prune reclaims data no retained snapshot needs. Review the official retention documentation before applying a policy or changing grouping.
After a successful restore drill, run the following deletion command during a maintenance window with no backup active. Keep this manual initially so an incorrect policy cannot repeatedly remove history unattended. Do not disable repository locking to force overlapping jobs through.
sudo /usr/local/sbin/vps-restic forget \
--host vps-primary --tag production \
--group-by host,paths,tags \
--keep-daily 14 --keep-weekly 8 --keep-monthly 12 \
--pruneStorage-side version history and deleted-file retention can keep consuming space after Restic prunes. Check the provider's accounting instead of assuming repository cleanup immediately releases all storage. Keep storage lifecycle policies from expiring objects that still belong to the active repository.
Restore on a replacement machine and book the next drill
Provision a separate recovery machine and install the same tools there. Recreate the root-only wrapper and repository configuration using your off-server recovery information, then obtain the password from your password manager. Do not copy credentials from the original VPS during this drill, because the exercise must work when that machine is unavailable.
Run the following commands on the recovery machine with enough free disk space for the restored files. The repository check reads stored data, which can incur transfer charges and take time. The restore target is a new private directory, keeping recovered configuration separate from the replacement machine's active configuration.
sudo /usr/local/sbin/vps-restic snapshots --host vps-primary --tag production
sudo /usr/local/sbin/vps-restic check --read-data
restore_dir=$(sudo mktemp -d /var/tmp/vps-restore.XXXXXX)
sudo /usr/local/sbin/vps-restic restore latest \
--host vps-primary --tag production \
--target "$restore_dir"
sudo ls -la "$restore_dir/srv/app-data"
sudo ls -la "$restore_dir/var/backups/app"The Restic restore documentation explains snapshot selection and target paths. For this initial drill, latest is convenient; during an incident, select a known-good snapshot from before the damage. Never restore the saved /etc tree directly over a running replacement server without reviewing its contents.
Import the database into an isolated instance and start the application with restored data. Disable outbound mail, payment actions and background jobs that could affect real users. Check an application operation that reads both database records and uploaded files, since successful decryption alone does not establish application consistency.
Record the elapsed recovery time and every missing dependency in the off-server recovery note. Fix those gaps, then create a recurring calendar task for another restore drill and repeat it after substantial storage or deployment changes. Your next action is to book that drill with a named owner and a specific date.
FAQ
What is a good VPS backup tool for a small server?
We recommend Restic when you want encrypted file snapshots and a choice of off-server destinations. Its command-line workflow fits scheduled Linux jobs without needing a separate management server. The deciding factor is whether you can operate its recovery process: identify persistent data, capture databases consistently, preserve credentials independently, and restore the application when the original VPS is unavailable.
Can I back up a VPS to Google Drive?
Yes. Configure Google Drive as an rclone remote, then let Restic use that remote as its repository backend. Keep Restic responsible for snapshot history instead of relying on a plain synchronized folder. Drive account access, OAuth authorization and available storage become backup dependencies, so include them in monitoring and prove that your recovery machine can authenticate independently.
How do I back up a VPS to S3?
Create a private bucket, obtain credentials scoped to its backup contents, and configure Restic with the provider's endpoint and your repository password. Initialize the repository before uploading snapshots. Keep account administration separate from the server credential, and understand which permissions allow deletion. Recovery also depends on retaining account access when the hosting account or original machine is unavailable.
Are host snapshots enough for VPS backups?
Host snapshots can be useful for rollback, particularly before a risky system change. Their recovery value depends on where they are stored, how they are protected, and whether you can access them after losing the hosting account. Keep an independent copy of irreplaceable data and use a database-supported consistency method rather than assuming a disk snapshot captures every application correctly.
Can I back up a running database with Restic?
Restic can store database exports or database-native backup files, but copying an actively changing database directory is not a substitute for the database's backup procedure. Make the native backup complete successfully before file capture begins. Restore it into an isolated database during drills, including required roles and extensions, and coordinate uploaded files if their contents must match database records.
Can I make VPS backups without paying for storage?
You may be able to use storage already included in an account you maintain, but that does not remove capacity limits or operational work. Budget for growing history and the possibility of downloading the entire repository during recovery. Compare the full arrangement with the host's add-on, including your time maintaining credentials, checking failed jobs and practicing restores.
Put this guide in the bigger picture
See how this topic connects to the rest of your product. Our free field guide explains apps, infrastructure and AI, with checklists to help you make business decisions.






