All systems operational 6 offshore regions No-KYC checkout
Hands-on Field guide

Encrypted off-site backups: 3-2-1 on your own offshore VPS

Every hardening checklist ends with "keep off-host backups" and then walks away from the hardest part. This one does the work: what actually separates a snapshot from a backup, why the encryption has to happen before a single byte leaves the machine, how an append-only repository survives the night somebody owns your source box — and how to prove any of it by restoring. The destination is a second offshore VPS from $8.00/mo in a different region: a box you control, holding a repository whose key we could not hand to anyone even if we were asked for it.

Updated 2026-08-31 · 15 min read · Fleet operations
On this page
  1. A snapshot is not a backup
  2. 3-2-1, rewritten for a server nobody can identify
  3. Encrypt before it leaves the machine
  4. Borg, restic, rclone: pick one and know why
  5. Append-only, or the intruder deletes your backups too
  6. Where the second copy should live
  7. A backup you have not restored is a rumour
  8. What it costs, and where it sits in the stack
  9. Step by step
SP·01

A snapshot is not a backup

The snapshot in your panel is a genuinely useful thing and you should take one before every risky change. It is also not a backup, and the difference is not pedantry — it is the entire list of situations you are trying to survive. A snapshot lives on the same host, inside the same account, behind the same credentials as the machine it protects. It answers exactly one question well: how do I undo the last twenty minutes? It answers none of the others. If the account is gone, the snapshot is gone with it. If the region is unreachable, so is the rollback. If an intruder reaches the panel, they reach the snapshots too. And if the data was already corrupt when the snapshot was taken, you have preserved the corruption with admirable fidelity.

The same applies to the two things people most often mistake for backups. RAID is not a backup: it protects against a disk dying, and it replicates rm -rf to the mirror at full speed. Replication is not a backup for the same reason — it is designed to make the second copy identical to the first as quickly as possible, including when the first copy has just been destroyed. On a no-KYC host the account branch of that list is sharper than elsewhere: registration is a handle, a password and eight recovery codes, with no email or document anywhere in the loop, so there is no support ladder to climb if you lose them. That is the product working as designed. It also means the copy that actually matters is the one that is not reachable from the credentials you might lose.

SP·02

3-2-1, rewritten for a server nobody can identify

The old rule still holds: three copies of anything you care about, on two different systems, with one of them off-site. The modern extension people write as 3-2-1-1-0 adds the two clauses that matter most in 2026 — one copy that is immutable or offline, and zero errors when you verify. Mapped onto a single server, that reads: copy one is the live data; copy two is the provider-side snapshot or the daily encrypted backups add-on, which handles the ordinary two-in-the-morning mistake with no effort from you; copy three is an encrypted repository on a second machine, in a different region, that the first machine has no authority to delete. Only the third copy survives losing the first machine's account, and only the third copy is yours in the sense that nobody else can be compelled to produce it.

Be deliberate about what "off-site" means. Another rack in the same building is not off-site in any sense that matters, and another region under the same law is only half of it — a single legal instrument that reaches one machine should not automatically reach both. With 6 regions to choose from, that is a decision you make once at deploy time and never revisit. Worth saying plainly, though: two servers here are still two servers at one provider, which is a correlated risk however good the isolation. If your threat model genuinely includes losing us, the third copy belongs somewhere else entirely — a machine at home, a friend's rack, a different host on another continent. Everything in this guide works identically wherever the destination lives; the only thing that changes is the address in one environment variable.

SP·03

Encrypt before it leaves the machine

The destination should be a place that stores bytes it cannot read. That is not a policy you accept from a provider, it is a property you build: the data is chunked, compressed, encrypted and authenticated on the source, and what crosses the wire is already opaque. Borg's repokey and keyfile modes encrypt every chunk with AES-256 in counter mode and authenticate it, and the -blake2 variants swap HMAC-SHA256 for BLAKE2b, which is measurably faster on modern 64-bit CPUs. File contents, file names, and the archive manifest that lists them are all encrypted; the destination holds numbered segment files and an index it cannot interpret. restic gives you the same shape with different plumbing. Either way, the correct mental model is that you are renting disk space, not trust.

Two honest caveats. First, what still leaks: whoever holds the destination disk can see how much data arrives and when it arrives. Repository size and write timing are visible even when contents are not, which for most people is irrelevant and for a few is not. Second, encryption at rest on the destination — LUKS on the backup box — defends against the disk leaving the building, not against the running host, so it complements client-side encryption rather than replacing it. And then the part that ruins people: with repokey the key material lives inside the repository, wrapped by your passphrase, so losing the repository loses the key as well; with keyfile it lives only on the source, so losing the source loses every archive you ever made. Neither is safe until you have exported the key and put the export somewhere that is neither machine. Do that in step three, not "later".

SP·04

Borg, restic, rclone: pick one and know why

Two tools are correct answers here and the choice is genuinely about the destination. Borg does chunk-level deduplication, compression and authenticated encryption, and — the reason it is the worked example below — it ships a real server-side append-only mode that you get for free over plain SSH, with no daemon to run and no extra port to open. It needs borg installed at both ends and speaks its own protocol. restic offers the same guarantees while being backend-agnostic: SFTP, S3-compatible object storage, Backblaze B2, or its own rest-server. Nothing needs installing on a plain SFTP target, which is convenient, but append-only then depends on rest-server --append-only or a bucket policy rather than on an SSH restriction. Rule of thumb: Borg when the destination is a server you control, restic when it is object storage or when you want one tool across many backends.

What not to use, because it is the most common way this goes wrong. rclone is a synchronisation tool. Its crypt remote does give you client-side encryption, but a sync propagates deletions — the file you lost at 03:00 is faithfully removed from the destination at 03:15, which is precisely the failure you were trying to survive. It is excellent for pushing a finished Borg or restic repository to a third location; it is not a backup. The same objection applies to a bare rsync --delete in cron, which is a mirror wearing a backup's clothes. tar | gpg to a remote path is a real backup, but with no deduplication, no retention logic, and a restore that means walking an entire chain of increments to get one file back. Use the tools that were designed for this; they are packaged in every distribution mentioned in this guide.

SP·05

Append-only, or the intruder deletes your backups too

Here is the scenario that separates a backup system from a backup script. Somebody gets root on your production box — through the application, a dependency, a leaked key, it does not matter how. The first thing competent ransomware does, and the first thing a competent human does, is look for the backups, because a victim with working backups does not pay and does not panic. Your nightly job runs unattended, so its credentials are necessarily on that machine. If the job can delete archives, the intruder can delete archives, and you find out at exactly the moment you need them. This is not a hypothetical failure mode; it is the normal one.

The fix is small and it is the most important line in this guide. On the destination, pin the automated key to a single command in authorized_keys: command="borg serve --append-only --restrict-to-path /srv/borg",restrict. That key can now do exactly one thing — add data to a repository under one path. It cannot get a shell, cannot forward a port, cannot list your filesystem, and cannot remove a single archive. The restrict keyword (OpenSSH 7.2 and later) switches off pty allocation and every kind of forwarding in one word, so the restriction does not rot as options are added. Now the honest caveat that most tutorials skip: while append-only is in force, borg prune and borg compact appear to succeed and do not actually free anything — the deletion is recorded in a transaction that the next append-only session rolls back. Retention therefore needs a second, unrestricted key that you use by hand from your laptop and never store on the source. Two keys, two jobs: the nightly one can only write; the maintenance one can delete and lives somewhere the compromised machine cannot reach. Keep that key handy for one other purpose — a job killed mid-run leaves a stale lock, and borg break-lock is a write.

SP·06

Where the second copy should live

Backups are the one workload where latency does not matter, so ignore the usual instinct to put the machine near your users and choose for law and independence instead. A different country from production is the floor; a different legal family is better. Our regions each answer a different question — Romania is the flagship where DMCA notices are simply not processed, Switzerland sits outside the EU behind unusually strict data-protection statutes, Iceland has the IMMI framework, Panama has no mandatory data-retention law and no fast lane for foreign requests, Malaysia puts a copy outside Five-Eyes reach entirely, and the Netherlands is the peering hub. Which offshore location should you pick? works through the tradeoffs properly; for a backup target the answer is usually "wherever production is not". Ports are unmetered on every plan, so the first full upload is bounded by how fast the source can read its own disk, not by a transfer allowance you have to ration.

Sizing is less dramatic than people expect. Deduplication plus zstd means the repository is typically a fraction of the source, and after the first run only changed chunks cross the wire — a busy 40 GB server with a normal rate of change settles into a few hundred megabytes a night, so a year of daily archives costs far less disk than a year of daily tarballs would. The smallest plan on the ladder is a perfectly good target; step up only if you are keeping deep history of something genuinely large, and read the exact specifications on the plans page rather than trusting a number written into a guide, where it would drift. One rule about the box itself: give it nothing else to do. No web server, no database, no public service except SSH on a key. A backup target that also hosts a side project is a backup target with the side project's attack surface, and it should get the full first-hour treatment before it receives a single archive.

SP·07

A backup you have not restored is a rumour

Backup systems rarely fail loudly. They fail because an exclude pattern quietly swallowed the data directory, or because a database was copied file-by-file while writes were in flight and the dump restores into a mangled table, or because the timer has been failing for six weeks behind a distribution upgrade and nobody reads the mail spool. The only test that detects any of that is a restore. Do it on a schedule: pick an archive at random, extract it into a scratch directory, diff a handful of files against production, start the database from the dump and run a query, and write down how long the whole thing took. That number is your actual recovery time — not the one you assumed — and it is the only figure worth quoting to yourself at three in the morning. At least once, do the drill from a blank machine, because that is the real scenario: a fresh VPS, a passphrase from your password manager, an exported key from wherever you put it, and nothing else.

Monitoring deserves the same scepticism you applied to the rest of the stack. The standard advice is a third-party dead-man's-switch that your job pings on success, which quietly tells an outside service your hostnames, your schedule and when your infrastructure is unhealthy — an odd thing to bolt onto a machine you deliberately bought without leaving an identity anywhere. You do not need it. Freshness can be checked from the destination without the key at all: the newest segment file in the repository carries a timestamp, so a five-line cron on the backup box that shouts when nothing has arrived in twenty-five hours costs nothing and reveals nothing. Integrity has a key-free check too — borg check --repository-only runs locally on the destination and validates segment structure without ever seeing your data. Run the deep --verify-data pass occasionally from the source with the maintenance key, where the key belongs.

SP·08

What it costs, and where it sits in the stack

The economics are not close. A second VPS from $8.00/mo, funded off the same prepaid balance with top-ups from $30.00, online in about 15 min, against the cost of losing whatever is on the first machine. Run it alongside the provider-side backups rather than instead of them: the daily encrypted backups add-on handles the deleted-the-wrong-directory case with no work from you and no thinking at three in the morning, while what you have built here is the copy that stays yours when the account, the region or the provider is the thing that went away. They cover different failures and neither is a substitute for the other, which is the whole point of counting to three.

It is worth ending where backups sit relative to everything else, because these are four controls that fail independently. What the host knows about you is the first, and here it is close to nothing — a handle and a crypto balance, the subject of paying for hosting anonymously. Whose law applies is the second, decided by where the hardware sits rather than where you sit. What the machine allows is the third, which is the first hour after deploy and is yours alone to configure. And what survives the machine is the fourth — the only one that is a promise you make to your future self, and the only one nobody will remind you about until the day it is too late to start. An hour tonight, and a restore drill in the calendar. That is the whole thing.

SP·09

Step by step

  1. 01

    Deploy the backup target and give it nothing else to do

    Deploy a second VPS in a region that is not where production lives, and treat it as a single-purpose appliance. Run the standard first-hour checklist on it — key-only SSH, default-deny firewall on both IP families, unattended security updates — then open nothing else. The only inbound port on this machine is SSH.

    ssh root@198.51.100.7
    apt update && apt full-upgrade -y
    hostnamectl set-hostname vault-01
    apt install -y borgbackup ufw unattended-upgrades
    ufw default deny incoming && ufw default allow outgoing
    ufw limit 22/tcp && ufw enable
  2. 02

    Create a restricted borg account with two keys

    Generate two keypairs on the source: one for the nightly job (no passphrase — it has to run unattended) and one for maintenance, which you keep on your laptop only. On the target, create an unprivileged borg user and pin each key to a forced command. The job key gets --append-only; the maintenance key does not. This single file is what stops an intruder on the source from erasing your history.

    # on the source
    ssh-keygen -t ed25519 -N "" -f /root/.ssh/borg_job -C "job@edge-01"
    # on your laptop
    ssh-keygen -t ed25519 -f ~/.ssh/borg_maint -C "maint@laptop"
    
    # on the target
    adduser --disabled-password --gecos "" borg
    install -d -m 700 -o borg -g borg /home/borg/.ssh /srv/borg
    cat > /home/borg/.ssh/authorized_keys <<'EOF'
    command="borg serve --append-only --restrict-to-path /srv/borg",restrict ssh-ed25519 AAAA...job@edge-01
    command="borg serve --restrict-to-path /srv/borg",restrict ssh-ed25519 AAAA...maint@laptop
    EOF
    chown borg:borg /home/borg/.ssh/authorized_keys
    chmod 600 /home/borg/.ssh/authorized_keys
  3. 03

    Initialise the repository, then get the key off both machines

    Initialise with the maintenance key, from your laptop — creating a repository is not an append. One repository per source host keeps retention trivial and the blast radius small. Then export the key twice, in two formats, and move both exports somewhere that is neither the source nor the target: a password manager, an encrypted USB stick, a sheet of paper in a drawer. A repository whose key exists only inside itself is a coin flip.

    export BORG_REPO='ssh://borg@198.51.100.7/srv/borg/edge-01'
    export BORG_RSH='ssh -i ~/.ssh/borg_maint'
    borg init --encryption=repokey-blake2
    
    borg key export :: /tmp/edge-01.borgkey
    borg key export --paper :: /tmp/edge-01.paper
    # move both off this machine, then shred the copies
    shred -u /tmp/edge-01.borgkey /tmp/edge-01.paper
  4. 04

    Freeze application state before you read the disk

    Copying a live database directory file-by-file produces a file that looks like a database and restores like a crime scene. Dump first, back up the dump, and exclude the raw data directory. The same logic applies to anything with an on-disk format you do not control: dump it, or stop it for the seconds the snapshot takes.

    install -d -m 700 /var/backups/dumps
    mariadb-dump --single-transaction --quick --all-databases \
      > /var/backups/dumps/mariadb.sql
    # PostgreSQL:
    # sudo -u postgres pg_dumpall > /var/backups/dumps/pg.sql
    chmod 600 /var/backups/dumps/*.sql
  5. 05

    Write the backup job and put it on a timer

    Keep the script boring and let it fail loudly. The passphrase comes from a mode-600 file via BORG_PASSCOMMAND, so it never appears in the process list. Name archives with the host and an ISO timestamp so they sort. Note what is not in this script: no prune, no delete — the job key could not perform them anyway. A timer with Persistent=true catches up after a reboot, and a randomised delay stops all your machines uploading in the same second.

    cat > /usr/local/sbin/borg-backup.sh <<'EOF'
    #!/bin/bash
    set -euo pipefail
    export BORG_REPO='ssh://borg@198.51.100.7/srv/borg/edge-01'
    export BORG_RSH='ssh -i /root/.ssh/borg_job -o BatchMode=yes'
    export BORG_PASSCOMMAND='cat /root/.config/borg/passphrase'
    borg create --stats --compression zstd,6 --one-file-system \
      --exclude-caches --exclude '/var/cache/*' \
      --exclude '/var/lib/mysql' --exclude '/home/*/.cache' \
      ::'{hostname}-{now:%Y-%m-%dT%H:%M}' \
      /etc /root /home /srv /var/www /var/backups/dumps
    EOF
    chmod 700 /usr/local/sbin/borg-backup.sh
    
    cat > /etc/systemd/system/borg-backup.service <<'EOF'
    [Unit]
    Description=Off-site Borg backup
    [Service]
    Type=oneshot
    Nice=10
    IOSchedulingClass=idle
    ExecStart=/usr/local/sbin/borg-backup.sh
    EOF
    
    cat > /etc/systemd/system/borg-backup.timer <<'EOF'
    [Unit]
    Description=Nightly off-site Borg backup
    [Timer]
    OnCalendar=*-*-* 03:17:00
    RandomizedDelaySec=900
    Persistent=true
    [Install]
    WantedBy=timers.target
    EOF
    systemctl daemon-reload
    systemctl enable --now borg-backup.timer
  6. 06

    Prune with the maintenance key, compact on the target

    Retention cannot run from the source, because the source's key is append-only and its deletions would silently roll back. Run it from your laptop instead, on whatever cadence suits you — monthly is plenty. prune decides which archives to keep; compact is what actually reclaims the disk. Do a --dry-run first and read the list before you let it delete anything.

    export BORG_REPO='ssh://borg@198.51.100.7/srv/borg/edge-01'
    export BORG_RSH='ssh -i ~/.ssh/borg_maint'
    
    borg prune --dry-run --list \
      --keep-daily 7 --keep-weekly 4 --keep-monthly 6
    borg prune --list --stats \
      --keep-daily 7 --keep-weekly 4 --keep-monthly 6
    borg compact --progress
  7. 07

    Run the restore drill, then write the runbook

    Restore before you need to. Extract a real archive into a scratch directory, compare a few files with the originals, and load the database dump into a throwaway schema. Then add the two checks that run without your attention: a key-free freshness alarm on the target, and a periodic integrity pass. Finally write down the three facts you will want on your worst day — where the exported key lives, where the passphrase lives, and the exact commands below.

    borg list ::
    mkdir -p /var/tmp/drill && cd /var/tmp/drill
    borg extract --list ::edge-01-2026-08-31T03:17 etc/nginx
    diff -r etc/nginx /etc/nginx && echo 'restore OK'
    
    # on the target, no key needed:
    borg check --repository-only /srv/borg/edge-01
    find /srv/borg/edge-01/data -type f -mmin -1500 | head -1   # empty = stale
SP·10 — FAQ

Quick answers

Isn't the snapshot in my panel enough?

It is enough for the failure it was designed for — undoing a change you regret — and for nothing beyond that. A snapshot sits on the same host, inside the same account, behind the same credentials as the server it protects, so losing the account or the region loses the snapshot with it, and an intruder who reaches the panel reaches both. Take snapshots before every risky change, absolutely; they are free and they save afternoons. Then keep a separate encrypted copy on a machine the first one has no authority to delete, because that is the copy that answers a different and much worse question.

Where should the repository passphrase live?

In your password manager, with a paper copy somewhere physical, and in a mode-600 file on the source that the job reads through BORG_PASSCOMMAND so it never shows up in ps. Be clear-eyed about what that last copy means: anyone with root on the source can read the passphrase, and with a repokey repository that is enough to decrypt the archives. This is not a hole you can close — an unattended job needs its credentials — and it is exactly why the job key is append-only. The intruder who owns your server can read data they already have; what they must not be able to do is destroy the only copy that outlives the machine.

Can ServPrivacy read my backups?

No. Chunking, compression and encryption all happen on your machine before anything crosses the wire, so the destination stores numbered segment files and an index it cannot interpret, and the key never leaves your control in any form we could hold. What is visible to whoever holds the disk is metadata rather than content: that a repository exists, roughly how much data it contains, and when writes arrive. The other side of that coin is unforgiving and worth stating plainly — if you lose both the passphrase and the exported key, nobody can recover the archives, including us. Export the key in step three and store it somewhere that is neither machine.

How big should the backup VPS be?

Smaller than you think. Borg deduplicates at the chunk level and compresses what is left, so a repository is usually a fraction of the source, and after the first run only changed chunks are transferred — a busy 40 GB server with an ordinary change rate typically adds a few hundred megabytes a night. The smallest plan on the ladder is a fine target for one or two servers with a sensible retention policy; move up if you are keeping deep history of something genuinely large, such as a media library or a database that rewrites most of itself daily. Exact CPU, memory and disk figures live on the plans page rather than in this guide, where they would drift.

Borg or restic — which should I use?

Both are correct; the destination decides. Choose Borg when the destination is a server you control over SSH, because its append-only mode is a one-line restriction in authorized_keys with no daemon and no extra open port, which is the single most valuable property in this whole setup. Choose restic when the destination is S3-compatible object storage or when you want one tool across several very different backends — it needs nothing installed on a plain SFTP target, though immutability then depends on rest-server --append-only or a bucket policy instead of an SSH restriction. What matters far more than the choice is that you pick one, automate it, and restore from it on a schedule.

How often should I test a restore?

Quarterly as a floor, and immediately after any change to the job, the exclude list or the machine's layout — those changes are where silent breakage is born. A drill does not have to be heavy: extract one archive into a scratch directory, diff a few files, load the database dump into a throwaway schema, and note how long it took. Once a year, do the version that actually matters and rebuild from nothing on a fresh VPS with only your passphrase and the exported key, because that is the real scenario and it is the one that surfaces the step you forgot to write down. A backup you have never restored is a rumour about a backup.

Put it into practice

VPS online in 15 min, dedicated handed over in 2–12 h. Top up from $30.00 in crypto — no identity attached.

Deploy a VPS