Back to tutorials
Tutorial

Incremental Backup Tutorial (2026): Restic + S3-Compatible Storage for VPS & Dedicated Servers

Incremental backup tutorial (2026) using Restic + S3 storage on a VPS: encryption, schedules, retention, and restore drills.

By Anurag Singh
Updated on Aug 07, 2026
Category: Tutorial
Share article
Incremental Backup Tutorial (2026): Restic + S3-Compatible Storage for VPS & Dedicated Servers

Your first full backup feels reassuring. Your first restore makes it real. This incremental backup tutorial shows how to set up encrypted, deduplicated backups with Restic to S3-compatible object storage on a VPS or dedicated server.

You’ll also run restore drills. Those drills surface problems before an outage forces you to improvise.

The objective is simple: back up the data that matters. Run it on a schedule you can defend. Apply retention rules you can explain to a client.

Then prove you can restore a single file, a full site tree, and a “server subset” cleanly.

What you’ll build (and what you won’t)

  • Backups: Restic repositories stored in S3-compatible object storage (AWS S3, Wasabi, Backblaze B2 S3 API, MinIO, etc.).
  • Security: Client-side encryption (Restic encrypts before upload) and least-privilege API keys.
  • Automation: systemd timers (preferred) or cron on Linux.
  • Retention: Keep daily/weekly/monthly snapshots with prune rules.
  • Restore drills: File-level restore and full-path restore to a staging directory.

Not included: VM snapshot workflows. That’s a different toolchain, with different failure modes.

This guide backs up files and directories. That matches how most hosting admins recover websites and configs.

Prerequisites and a safe target list

This tutorial assumes Ubuntu 24.04 LTS or Debian 12 on a VPS/dedicated server, with root or sudo access.

The same approach works on AlmaLinux/Rocky. The package and service commands will differ.

For WordPress, mail, or multi-site hosting, prioritize the data that’s painful to rebuild:

  • /etc (system and service config)
  • Website roots (often /var/www, or /home/*/public_html on control panel setups)
  • Application config and secrets (env files, deploy keys—be intentional)
  • Logs are optional; they bloat repositories quickly

Database dumps belong in a complete backup plan. This article stays focused on file-level incremental backups for hosting operations.

If your sites rely on MySQL/MariaDB, pair this with a separate dump routine. Use your panel or your existing automation.

Need a stable machine to run this on? Start with a HostMyCode VPS for predictable storage and network.

Move to dedicated servers when you need consistent I/O under load.

Incremental backup tutorial: Install Restic and create your repository

On Ubuntu/Debian, install Restic from the distro packages. This keeps security updates flowing through normal patching.

Upstream binaries work too. For most hosting environments, packages are the sensible default.

sudo apt update
sudo apt install -y restic ca-certificates jq

Create a dedicated place for environment files and scripts:

sudo install -d -m 0750 /etc/restic
sudo install -d -m 0750 /opt/restic

Configure S3 credentials (least privilege)

Create an S3 bucket and an access key scoped to that bucket only.

Use a dedicated key per server. If one key leaks, it should not unlock every environment you run.

Create /etc/restic/restic.env:

sudo nano /etc/restic/restic.env
RESTIC_REPOSITORY=s3:https://s3.example.com/hostmycode-prod-vps-01
AWS_ACCESS_KEY_ID=REPLACE_ME
AWS_SECRET_ACCESS_KEY=REPLACE_ME
RESTIC_PASSWORD=REPLACE_WITH_A_LONG_RANDOM_PASSPHRASE

Lock the file down:

sudo chmod 0640 /etc/restic/restic.env
sudo chown root:root /etc/restic/restic.env

Initialize the repository:

set -a
source /etc/restic/restic.env
set +a

sudo -E restic init

If you hit TLS or endpoint errors, double-check the S3 endpoint URL.

Some providers require a custom region or path-style addressing. If so, you may need:

AWS_DEFAULT_REGION=us-east-1
AWS_S3_FORCE_PATH_STYLE=true

Leave those out unless your provider explicitly needs them.

Choose backup paths and exclusions that won’t bite you later

Most backup failures come down to scope.

Go too broad and you upload churn that never helps a restore. Go too narrow and you discover gaps during an incident.

Suggested base set for a hosting VPS

  • /etc
  • /var/www (or your site roots)
  • /home (only if you actually store web/app data there)
  • /root (optional; include only if it contains configs you want)

Common exclusions

Create an exclude file at /etc/restic/excludes.txt:

sudo nano /etc/restic/excludes.txt
# OS/runtime noise
/proc
/sys
/dev
/run
/tmp
/var/tmp

# Package caches
/var/cache

# Logs (optional; comment out if you want them)
/var/log

# Containers (if you run Docker; consider backing up volumes separately)
/var/lib/docker

On control panel servers, treat panel-managed paths with care.

For cPanel/WHM, most account data lives under /home. You’ll usually want to exclude transient directories like /home/*/tmp.

Keep exclusions specific. Broad patterns can hide real content.

If you need secure admin access before you automate backups, follow our SSH hardening tutorial.

Don’t build automation on top of an exposed root login.

Create a backup script with locking and readable output

You can run Restic as a one-liner. A small script makes scheduling, logging, and troubleshooting easier.

Create /opt/restic/backup.sh:

sudo nano /opt/restic/backup.sh
#!/usr/bin/env bash
set -euo pipefail

# Load environment
set -a
source /etc/restic/restic.env
set +a

HOST_TAG="$(hostname -f 2>/dev/null || hostname)"
LOCKFILE="/run/restic-backup.lock"

exec 9>"$LOCKFILE"
if ! flock -n 9; then
  echo "[$(date -Is)] Restic backup already running. Exiting."
  exit 0
fi

echo "[$(date -Is)] Starting restic backup for $HOST_TAG"

# Tip: add --one-file-system if you don't want mounted volumes included
restic backup \
  --tag "$HOST_TAG" \
  --exclude-file /etc/restic/excludes.txt \
  /etc /var/www /home \
  --verbose

echo "[$(date -Is)] Applying retention policy"
restic forget \
  --tag "$HOST_TAG" \
  --keep-daily 14 \
  --keep-weekly 8 \
  --keep-monthly 12 \
  --prune

echo "[$(date -Is)] Verifying repository structure (quick check)"
restic check --read-data-subset=2.5%

echo "[$(date -Is)] Done"

Make it executable:

sudo chmod 0750 /opt/restic/backup.sh

Why restic check --read-data-subset? It’s a practical compromise.

A full read check can get expensive on large repos. A small subset check still catches obvious corruption, credential issues, and broken endpoints early.

Automate with systemd timers (cleaner than cron)

systemd timers keep logs in the journal. They also handle missed runs after reboots.

They’re often easier to manage than a pile of cron entries.

Create the service

/etc/systemd/system/restic-backup.service:

sudo nano /etc/systemd/system/restic-backup.service
[Unit]
Description=Restic backup job
Wants=network-online.target
After=network-online.target

[Service]
Type=oneshot
ExecStart=/opt/restic/backup.sh
Nice=10
IOSchedulingClass=best-effort
IOSchedulingPriority=7

# Hardening
PrivateTmp=true
NoNewPrivileges=true
ProtectSystem=full
ProtectHome=read-only
ReadWritePaths=/run

[Install]
WantedBy=multi-user.target

Note: ProtectHome=read-only still allows reading /home. That’s usually what you want for backups.

If your backup paths live elsewhere, adjust the sandboxing to match.

Create the timer

/etc/systemd/system/restic-backup.timer:

sudo nano /etc/systemd/system/restic-backup.timer
[Unit]
Description=Nightly Restic backup

[Timer]
OnCalendar=*-*-* 02:15:00
RandomizedDelaySec=15m
Persistent=true

[Install]
WantedBy=timers.target

Enable and start:

sudo systemctl daemon-reload
sudo systemctl enable --now restic-backup.timer
sudo systemctl list-timers --all | grep restic

Run a first backup manually. Don’t wait for 2 AM:

sudo systemctl start restic-backup.service
sudo journalctl -u restic-backup.service -n 200 --no-pager

Restore drills: practice the two restores you’ll actually need

You don’t need a disaster to validate restores.

You need a staging directory and a few minutes of discipline.

1) Restore a single file (fast sanity check)

List snapshots:

set -a
source /etc/restic/restic.env
set +a

sudo -E restic snapshots

Pick the latest snapshot ID. Restore one file into /root/restore-test:

sudo mkdir -p /root/restore-test
sudo -E restic restore latest \
  --target /root/restore-test \
  --include /etc/ssh/sshd_config

Verify it exists:

sudo ls -l /root/restore-test/etc/ssh/sshd_config

2) Restore a site tree to a new path (what you’ll do under pressure)

Restoring in-place is risky. Small mistakes can turn into long incidents.

Restore to a staging path first. Inspect what you got, then decide how you’ll swap it into production.

sudo mkdir -p /srv/restore-staging
sudo -E restic restore latest \
  --target /srv/restore-staging \
  --include /var/www/example.com

Spot-check ownership and sizes:

sudo du -sh /srv/restore-staging/var/www/example.com
sudo find /srv/restore-staging/var/www/example.com -maxdepth 2 -type f | head

If your site is WordPress and you want to reduce file transfer risk during day-to-day admin work, pair this with the locked-down approach in our SFTP setup tutorial.

Retention policy that matches hosting reality

The retention in the script is a solid baseline for small-to-mid hosting nodes:

  • 14 daily: covers “we changed something last week”
  • 8 weekly: covers slow-burn issues
  • 12 monthly: covers “we need last quarter” requests

Tune it based on storage cost, compliance needs, and what your clients expect.

For ecommerce, you’ll often keep files longer. You’ll also tighten the database strategy at the same time.

Hardening checklist: keep backups from becoming your weakest link

  • Use a dedicated S3 key per server. Don’t reuse keys across environments.
  • Store the Restic password securely. If you use a password manager, document access for your team.
  • Restrict outbound access if possible (only to your S3 endpoint). If you’re unsure, start with a safe baseline; see our UFW firewall setup tutorial.
  • Log backup results and alert on failures. A “silent” backup system is not a system.

Quick diagnostic: confirm you’re really deduplicating

After a few runs, look at repo stats:

set -a
source /etc/restic/restic.env
set +a

sudo -E restic stats --mode raw-data

If the repo grows almost as fast as your total data every day, you’re probably capturing volatile paths.

Common causes include logs, caches, tmp directories, or frequently rewritten large assets.

Operational monitoring: make failures visible

At minimum, alert if the timer fails. Also alert if nothing has run in the last 24 hours.

systemd already gives you the raw signals: exit status and journal output.

Two quick checks to run during routine maintenance:

sudo systemctl status restic-backup.timer --no-pager
sudo journalctl -u restic-backup.service --since "2 days ago" --no-pager

If you already run monitoring, track backup success alongside CPU, disk, and HTTP uptime.

Our server monitoring tutorial shows a clean way to wire alerts without turning your VPS into a long-running experiment.

Common mistakes (and how to fix them fast)

  • Backups run, but restores fail due to missing password: your RESTIC_PASSWORD isn’t available to the service. Keep it in /etc/restic/restic.env and source it in the script.
  • “403 AccessDenied” from S3: your key lacks ListBucket or object permissions. Fix IAM policy or provider permissions, then rerun restic snapshots.
  • Huge daily uploads: tighten exclusions. Start by excluding /var/log, caches, and tmp directories.
  • Backup duration too long: reduce scope, schedule off-peak, and consider splitting large paths into separate repos (web vs. home) for parallelism later.

Where this fits in a HostMyCode hosting workflow

Restic-style incremental backups are a good fit when you need predictable restores and portability:

  • VPS hosting where you control the OS and want portable backups across providers.
  • Dedicated servers with multiple sites, where you want consistent retention and restore behavior.
  • Reseller environments where client requests often mean restoring specific directories, not entire nodes.

If you’re planning a server move, get backups running first. Verify a restore before you migrate.

For the migration flow itself, our hosting migration checklist pairs nicely with this setup.

Summary: your minimal “production-ready” standard

  • Encrypted Restic repo in S3-compatible storage
  • Nightly automated runs via systemd timer
  • Retention rules + prune
  • Regular restore drills to a staging directory
  • Monitoring that tells you when backups stop

If you want a stable place to run this long-term, a managed VPS hosting plan from HostMyCode can handle patching and baseline hardening while you keep full control of your backup design.

If you prefer to run everything yourself, a standard HostMyCode VPS gives you the access you need to automate Restic cleanly.

If backups are part of your uptime responsibility, run them on hosting that stays out of the way. Use a HostMyCode VPS for full root access and predictable performance, or choose managed VPS hosting if you want OS maintenance handled while you focus on sites, restores, and retention.

FAQ

Is Restic “incremental” or “full”?

Each run creates a new snapshot, but the data is deduplicated.

In practice, most daily backups upload only changed blocks. That’s what admins usually mean by incremental.

Should I back up the entire root filesystem?

Usually no.

Back up the data and config you can’t recreate quickly: /etc, site roots, and app config. Exclude pseudo-filesystems and caches.

How often should I run restic check?

A small subset check nightly (as shown) is a good baseline.

Run a full restic check monthly or after any storage incident.

Can I use this on a cPanel server?

Yes, but be careful with scope.

Focus on /home (accounts) and essential configs. Avoid backing up panel caches and transient directories.

What’s the one restore test I should always do?

Restore a real site directory to a staging path. Verify file ownership and content.

Then document the steps. That’s the restore you’ll repeat during incidents.

Incremental Backup Tutorial (2026): Restic + S3-Compatible Storage for VPS & Dedicated Servers | HostMyCode