[{"data":1,"prerenderedAt":178},["ShallowReactive",2],{"seo-verification":3,"blog-restic-s3-3-2-1-backup-strategy-vps-en":6},{"google":4,"bing":5},"EycwPY2XMyTkVzas3n1ygeNJFGAH513qrMjfDljzsMQ","",{"key":7,"data":8},"blog-restic-s3-3-2-1-backup-strategy-vps-en",{"id":9,"slug":10,"slugs":11,"title":15,"excerpt":16,"readTime":17,"views":18,"isPinned":19,"publishedAt":20,"category":21,"categories":27,"featuredImage":29,"bgImage":30,"posterImage":31,"relatedSolution":29,"intro":32,"sections":33,"ctaTitle":122,"ctaBody":123,"ctaButton":124,"ctaUrl":125,"relatedPosts":126},347,"restic-s3-3-2-1-backup-strategy-vps",{"fr":12,"en":10,"ar":13,"es":14},"backup-vps-restic-s3-3-2-1","restic-s3-3-2-1-backup-strategy-vps-ar","estrategia-3-2-1-backup-vps-restic-s3","3-2-1 Backup Strategy for Your VPS with Restic and S3","Build a complete 3-2-1 strategy on your VPS: Restic to Backblaze B2 or Cloudflare R2, systemd timer, retention policy and automated restore test.",11,0,false,"2026-09-10T00:00:00+00:00",{"id":22,"name":23,"slug":24,"color":25,"icon":26},8,"Security & Monitoring","securite-monitoring","bg-rose-500\u002F10 text-rose-400","security",[28],{"id":22,"name":23,"slug":24,"color":25,"icon":26},null,"\u002Fblog\u002Fcovers\u002Fbg.svg","\u002Fblog\u002Fcovers\u002Fbackup-vps-restic-s3-3-2-1-poster.svg","Enabling your hosting provider's snapshots is not a 3-2-1 strategy: it is one copy, on one medium, at one provider. If the VM disappears, your account is compromised, or the datacenter suffers an incident, your data goes with it. The 3-2-1 rule addresses this risk: three copies, on two distinct media, one of which is off-site and encrypted before leaving the server. This guide covers the complete implementation with Restic, a systemd timer, a calibrated retention policy and an automated restore test that fails when the backup is corrupt.",[34,38,49,52,84,112,116,119],{"type":35,"title":36,"body":37},"h2","Why hosting snapshots are not enough: three concrete limits","A hosting snapshot is convenient, but it shares the same failure plane as your VM. First limit: if the VM is deleted — by mistake, by your provider, or after a payment failure — the associated snapshots disappear with it. Second limit: if an attacker compromises your hosting account, they can delete snapshots and VM in seconds via the API or dashboard. Third limit: datacenter snapshots never test the restore. A 'consistent' snapshot can contain a corrupt filesystem or a database in an inconsistent state; you will only find out when you need it.",{"type":39,"title":40,"items":41},"ul","What a 3-2-1 strategy guarantees, and what a snapshot does not",[42,43,44,45,46,47,48],"**Three copies**: the original on local disk, one on hosting snapshots, one off-site on S3 — no single point of failure can erase everything.","**Two distinct media**: your VPS NVMe disk and an object storage bucket at another provider are physically and logically separated.","**Encryption before transfer**: Restic encrypts client-side with AES-256; the S3 storage provider sees only opaque blobs, unreadable without your key.","**Restore testing**: an untested backup is a promise, not a guarantee. A systemd timer can automatically verify that a sentinel file can be restored.","**Provider independence**: your B2 or R2 bucket cannot be deleted from your VPS hosting provider's dashboard.","A hosting snapshot does not encrypt before transfer: data is readable by the provider.","A hosting snapshot never verifies the application-level consistency of your databases.",{"type":35,"title":50,"body":51},"The three copies explained: local, hosting, off-site","The 3-2-1 rule is not a rigid recipe — it is a risk diversification principle.\n\n**Copy 1 — your VPS local disk.** This is your first line: the original, on NVMe. It serves fast restores of a file accidentally deleted, with no network latency.\n\n**Copy 2 — hosting snapshots.** The automatic backup managed by your VPS provider covers configuration accidents and accidental deletions at the system level. It is your on-site safety net.\n\n**Copy 3 — off-site object storage, encrypted with Restic.** This is the link that most teams forget, and the only one that survives the complete loss of the hosting account. Restic encrypts data before any transfer, deduplicates it to minimise cost and sends it to an S3-compatible bucket at a third-party provider. This third copy is what this guide builds.",{"type":53,"title":54,"headers":55,"rows":59},"comparison","Backblaze B2 vs Cloudflare R2: choosing your S3 backend",[56,57,58],"Criterion","Backblaze B2","Cloudflare R2",[60,64,68,72,76,80],[61,62,63],"Storage","Public per-GB rate","Free up to 10 GB\u002Fmonth, then public per-GB rate",[65,66,67],"Egress (outbound traffic)","Free to Cloudflare and select CDN partners; billed outside partners","Free without limit — no egress cost",[69,70,71],"S3 compatibility","Native S3-compatible API; endpoint `s3.us-west-004.backblazeb2.com`","Native S3-compatible API; endpoint `\u003Caccount>.r2.cloudflarestorage.com`",[73,74,75],"`RESTIC_REPOSITORY` variable","`s3:https:\u002F\u002Fs3.us-west-004.backblazeb2.com\u002Fbucket-name`","`s3:https:\u002F\u002F\u003Caccount>.r2.cloudflarestorage.com\u002Fbucket-name`",[77,78,79],"Access keys","B2 Application Key (Key ID + Application Key)","R2 API Token with Object Read & Write permissions",[81,82,83],"Recommended use case","High volume with limited egress or from Cloudflare infrastructure","Frequent egress or regular restore tests from anywhere",{"type":85,"title":86,"steps":87},"steps","Complete implementation: Restic + S3 on your VPS",[88,91,94,97,100,103,106,109],{"title":89,"body":90},"Export environment variables to a secured file","Create `\u002Froot\u002F.restic-env` with the required variables. For Backblaze B2:\n\n```bash\nexport RESTIC_REPOSITORY=\"s3:https:\u002F\u002Fs3.us-west-004.backblazeb2.com\u002Fyour-bucket\"\nexport RESTIC_PASSWORD=\"your-repository-password\"\nexport AWS_ACCESS_KEY_ID=\"your-b2-key-id\"\nexport AWS_SECRET_ACCESS_KEY=\"your-b2-application-key\"\n```\n\nFor Cloudflare R2, replace the repository URL and keys:\n\n```bash\nexport RESTIC_REPOSITORY=\"s3:https:\u002F\u002F\u003Caccount-id>.r2.cloudflarestorage.com\u002Fyour-bucket\"\nexport RESTIC_PASSWORD=\"your-repository-password\"\nexport AWS_ACCESS_KEY_ID=\"your-r2-access-key-id\"\nexport AWS_SECRET_ACCESS_KEY=\"your-r2-secret-access-key\"\n```\n\nImmediately restrict permissions: `chmod 600 \u002Froot\u002F.restic-env`. This file must never be committed to a Git repository or included in a backup accessible without authentication.",{"title":92,"body":93},"Initialise the Restic repository on S3","Load the environment, then initialise the encrypted repository on your bucket:\n\n```bash\nsource \u002Froot\u002F.restic-env\nrestic init\n```\n\nRestic creates the repository structure and seals the encryption with your password. The operation outputs a repository ID — note it. **Store the repository password outside the VPS**: in a password manager, an encrypted vault on a separate machine, or a secret manager. Without it, no restore is possible, even if you have full access to the bucket.",{"title":95,"body":96},"Run a first backup to S3","Test the complete path with a manual backup:\n\n```bash\nsource \u002Froot\u002F.restic-env\nrestic backup \u002Fetc \u002Fvar\u002Fwww \u002Fopt\u002Fdocker-data\n```\n\nFor databases, generate a dump first or use stdin mode. PostgreSQL example:\n\n```bash\npg_dump -U postgres mydb | restic backup --stdin --stdin-filename mydb.sql\n```\n\nVerify the snapshot was created: `restic snapshots`.",{"title":98,"body":99},"Define the retention policy","The `forget` command removes references to old snapshots; `--prune` physically frees orphaned blocks in the backend:\n\n```bash\nrestic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 3 --prune\n```\n\n**Rationale for the values.** Seven daily snapshots cover a full week — enough to detect a silent corruption that only appears a few days after the incident. Four weekly snapshots offer a one-month window to detect a slow application-level issue. Three monthly snapshots allow a restore to a state prior to the current quarter — useful after a failed database migration. `--prune` is essential: without it, `forget` marks snapshots for deletion but does not free space in the bucket, and the storage bill continues to grow.",{"title":101,"body":102},"Distinguish restic check from restic check --read-data","These two commands do not verify the same thing.\n\n`restic check` validates **repository metadata** — pack structure, index consistency, pointer integrity. Fast (seconds to a few minutes depending on repository size). Run it after every `forget --prune`.\n\n```bash\nrestic check\n```\n\n`restic check --read-data` downloads and verifies **every data blob** by comparing each to its cryptographic hash. Slow (proportional to repository volume) and potentially costly in egress if your provider bills outbound traffic. Reserve it for a monthly check or after doubts about bucket integrity.\n\n```bash\nrestic check --read-data\n```\n\nA weekly metadata check is sufficient to catch common corruptions without transfer overhead.",{"title":104,"body":105},"Create the systemd backup unit (.service)","Create `\u002Fetc\u002Fsystemd\u002Fsystem\u002Frestic-backup.service`:\n\n```bash\n[Unit]\nDescription=Restic backup to S3\nAfter=network-online.target\nWants=network-online.target\n\n[Service]\nType=oneshot\nEnvironmentFile=\u002Froot\u002F.restic-env\nExecStartPre=\u002Fbin\u002Fsh -c 'curl -sf --max-time 10 https:\u002F\u002Fone.one.one.one > \u002Fdev\u002Fnull || exit 1'\nExecStart=\u002Fusr\u002Fbin\u002Frestic backup \u002Fetc \u002Fvar\u002Fwww \u002Fopt\u002Fdocker-data\nExecStartPost=\u002Fusr\u002Fbin\u002Frestic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 3 --prune\nExecStartPost=\u002Fusr\u002Fbin\u002Frestic check\nStandardOutput=journal\nStandardError=journal\n```\n\n`ExecStartPre` checks network connectivity before attempting the backup — if the VPS is isolated or the bucket is unreachable, the service fails cleanly with an actionable status rather than hanging or writing a silent error to the logs.",{"title":107,"body":108},"Create the systemd scheduling unit (.timer)","Create `\u002Fetc\u002Fsystemd\u002Fsystem\u002Frestic-backup.timer`:\n\n```bash\n[Unit]\nDescription=Daily timer for restic-backup.service\n\n[Timer]\nOnCalendar=*-*-* 03:00:00\nPersistent=true\nRandomizedDelaySec=900\n\n[Install]\nWantedBy=timers.target\n```\n\n`Persistent=true` ensures that if the VPS is off at 03:00, the backup runs at the next boot. Enable and start:\n\n```bash\nsystemctl daemon-reload\nsystemctl enable --now restic-backup.timer\n```\n\nVerify the timer status: `systemctl status restic-backup.timer` and last run logs: `journalctl -u restic-backup.service`.",{"title":110,"body":111},"Create an automated restore test with a sentinel file","Create the sentinel file and include it in your backup:\n\n```bash\necho \"sentinel-$(date +%s)\" > \u002Fopt\u002Frestic-sentinel.txt\n```\n\nCreate `\u002Fetc\u002Fsystemd\u002Fsystem\u002Frestic-restore-test.service`:\n\n```bash\n[Unit]\nDescription=Restic restore test\nAfter=network-online.target\n\n[Service]\nType=oneshot\nEnvironmentFile=\u002Froot\u002F.restic-env\nExecStart=\u002Fbin\u002Fsh -c '\n  rm -rf \u002Ftmp\u002Frestic-test-restore && \\\n  restic restore latest --target \u002Ftmp\u002Frestic-test-restore && \\\n  test -f \u002Ftmp\u002Frestic-test-restore\u002Fopt\u002Frestic-sentinel.txt && \\\n  echo \"Restore OK: $(cat \u002Ftmp\u002Frestic-test-restore\u002Fopt\u002Frestic-sentinel.txt)\" || \\\n  (echo \"FAILED sentinel restore\" && exit 1)\n'\nStandardOutput=journal\nStandardError=journal\n```\n\nCreate the weekly timer `\u002Fetc\u002Fsystemd\u002Fsystem\u002Frestic-restore-test.timer`:\n\n```bash\n[Unit]\nDescription=Weekly Restic restore test\n\n[Timer]\nOnCalendar=Sun 04:00:00\nPersistent=true\n\n[Install]\nWantedBy=timers.target\n```\n\nEnable: `systemctl enable --now restic-restore-test.timer`.",{"type":113,"title":114,"body":115},"tip","Restic secrets: isolated .env file, never hard-coded in the service","Never place `RESTIC_PASSWORD`, `AWS_ACCESS_KEY_ID` or `AWS_SECRET_ACCESS_KEY` directly in a systemd unit definition, a versioned script or a Dockerfile. The `\u002Froot\u002F.restic-env` file with `chmod 600` is the right approach: it is loaded by `EnvironmentFile=` without ever appearing in `systemctl show` or logs. Add `\u002Froot\u002F.restic-env` to your `.gitignore` or `.dockerignore` if your `\u002Froot` directory is versioned. For multi-user environments, prefer a secret manager or systemd credentials (`LoadCredential=`) over a plain file.",{"type":35,"title":117,"body":118},"Troubleshooting: four common errors","**`Fatal: unable to open config file` on `restic init` or first `restic backup`.** Restic cannot reach the bucket. Check that variables are loaded (`echo $RESTIC_REPOSITORY`) and credentials are correct. Verify IAM permissions on your B2 or R2 key: it needs at minimum read, write and list rights on the bucket. On B2, an Application Key restricted to a specific bucket must explicitly include `listBuckets`. On R2, the token must have `Object Read` and `Object Write` permissions on the target bucket.\n\n**S3 bucket: insufficient permissions.** If `restic init` succeeds but `restic backup` fails with an authorisation error, a bucket policy is likely overriding the key's permissions. On B2, check that no `denyUpload` bucket rule is active. On R2, check that the bucket is not in public mode with write restrictions.\n\n**systemd timer not triggering.** Diagnose with three commands: `systemctl status restic-backup.timer`, `systemctl list-timers --all | grep restic`, `journalctl -u restic-backup.service --since today`. If the timer is active but the service has not run, verify that `OnCalendar` is syntactically valid: `systemd-analyze calendar '*-*-* 03:00:00'` should return a next trigger date.\n\n**`restic check --read-data` too slow.** On a repository of several tens of GB, `--read-data` can take hours and generate significant egress costs on B2. Use `--read-data-subset=10%` for a random sample check on each weekly run, and reserve the full verification for a scheduled monthly maintenance window.",{"type":35,"title":120,"body":121},"Recovery plan: how long to restore from B2 or R2?","Restore time depends on three factors: data volume, available bandwidth between your VPS and the bucket, and any egress costs.\n\nAs an estimate: a 20 GB Restic repository on B2 (already deduplicated and compressed data) restores in roughly 15 to 30 minutes with a standard datacenter connection at 1 Gbps. On R2, egress is free — no financial pressure to stagger the restore. On B2, if your VPS is outside the Backblaze partner network, the first GB are free then billed; a full restore of a large repository may incur non-trivial costs.\n\nTwo practices reduce recovery time: first, maintain a list of your critical directories separate from cache or log directories (do not back up `\u002Ftmp`, `\u002Fproc`, unused Docker volumes) — a smaller repository restores faster. Second, regularly test the partial restore of a single directory (`restic restore latest --target \u002Ftmp\u002Ftest --include \u002Fetc`) to calibrate the real duration on your infrastructure, rather than discovering it during an incident. `restic stats` gives the repository size for planning.","A VPS built for serious backup strategies","Root access, local storage, automatic snapshots and included outbound bandwidth: a ServOrbit VPS is the starting point for any 3-2-1 strategy.","Get a VPS with root access","\u002Fvps-cloud",[127,142,157],{"id":128,"slug":129,"slugs":130,"title":134,"excerpt":135,"readTime":136,"views":18,"isPinned":19,"publishedAt":137,"category":138,"categories":139,"featuredImage":29,"bgImage":30,"posterImage":141,"relatedSolution":29},113,"automate-your-vps-backups-with-restic",{"fr":131,"en":129,"ar":132,"es":133},"sauvegardes-restic-vps","أتمتة-نسخ-خادمك-vps-الاحتياطية-باستخدام-restic","copias-de-seguridad-vps-con-restic","Automate Your VPS Backups with Restic","Automate your VPS backups with Restic: encrypted snapshots, deduplication and shipping to S3 or any object backend.",3,"2026-02-27T00:00:00+00:00",{"id":22,"name":23,"slug":24,"color":25,"icon":26},[140],{"id":22,"name":23,"slug":24,"color":25,"icon":26},"\u002Fblog\u002Fcovers\u002Fsauvegardes-restic-vps-poster.svg",{"id":143,"slug":144,"slugs":145,"title":149,"excerpt":150,"readTime":151,"views":18,"isPinned":19,"publishedAt":152,"category":153,"categories":154,"featuredImage":29,"bgImage":30,"posterImage":156,"relatedSolution":29},114,"encrypted-vps-backups-with-borgbackup",{"fr":146,"en":144,"ar":147,"es":148},"backups-borgbackup-vps","نسخ-خادم-vps-الاحتياطية-المشفرة-باستخدام-borgbackup","copias-de-seguridad-vps-con-borgbackup","Encrypted VPS Backups with BorgBackup","Back up your VPS with BorgBackup: powerful deduplication, authenticated encryption and compression. A complete guide and Restic comparison.",4,"2026-02-26T00:00:00+00:00",{"id":22,"name":23,"slug":24,"color":25,"icon":26},[155],{"id":22,"name":23,"slug":24,"color":25,"icon":26},"\u002Fblog\u002Fcovers\u002Fbackups-borgbackup-vps-poster.svg",{"id":158,"slug":159,"slugs":160,"title":164,"excerpt":165,"readTime":166,"views":167,"isPinned":19,"publishedAt":168,"category":169,"categories":175,"featuredImage":29,"bgImage":30,"posterImage":177,"relatedSolution":29},126,"server-backups-jetbackup-explained",{"fr":161,"en":159,"ar":162,"es":163},"sauvegardes-serveur-jetbackup","نسخ-الخوادم-الاحتياطية-شرح-jetbackup","copias-seguridad-servidor-jetbackup","JetBackup on cPanel\u002FWHM: installation and restore","How to install JetBackup on WHM, set up S3 or SFTP destinations, schedule backups and restore an account, a database or an email independently.",9,1,"2026-02-18T00:00:00+00:00",{"id":170,"name":171,"slug":172,"color":173,"icon":174},7,"Self-hosting","self-hosting","bg-indigo-500\u002F10 text-indigo-400","cloud",[176],{"id":170,"name":171,"slug":172,"color":173,"icon":174},"\u002Fblog\u002Fcovers\u002Fsauvegardes-serveur-jetbackup-poster.svg",1789046178263]