Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

Automate Backups with restic

By Kokil Thapa | Last reviewed: September 2026

Production data disappears when disks fail, accounts get compromised, or someone runs the wrong command. You need a backup tool that encrypts by default, deduplicates efficiently, and runs unattended on a schedule. To Automate Backups with restic, you install the binary, initialize an encrypted repository, store credentials outside scripts, and trigger snapshots through Linux system administration patterns such as cron or systemd timers. I've used this stack on shared EC2 hosts that run Laravel legal-tech portals and sister sites deployed with Deployer 7 and GitLab CI — the same servers that need reliable nightly dumps without manual babysitting.

What is restic and why should you Automate Backups with restic?

restic is a single-binary backup program written in Go. It creates encrypted, content-addressed snapshots of files and directories. Only changed data blocks are stored, so repeated runs stay fast and cheap on bandwidth.

Compared with plain tar plus rsync, restic gives you snapshot history, encryption at rest, and built-in integrity checks. Compared with cloud-only agents, you keep control of retention, scheduling, and storage location — important for Nepal-based clients on budget VPS plans where off-site copy to S3 or Backblaze B2 is often Rs 500–2,000/month (~USD 4–15) instead of a full managed backup SKU.

Automate Backups with restic — Core FlowApp Server/var/www, DB dumpsrestic Clientencrypt + chunkRepositorylocal / S3 / SFTPScheduler Layercron · systemd timer · CI post-deploy hookforget --prune enforces retention
Automate Backups with restic: scheduled snapshots from application paths into an encrypted repository, local or remote.

On production Laravel applications I maintain, restic complements application-level tools. Spatie Laravel Backup handles in-app database exports; restic then snapshots those dump files together with storage/ and config. That layered approach mirrors what we describe in Laravel Spatie backup automation and broader backup strategy design guidance.

ApproachEncryptionDeduplicationSnapshot historyBest fit
resticAES-256 by defaultYesYes, with tagsFull-server or mixed file backups
rsync onlyNo (unless filesystem-level)NoManual rotationMirror sync, low complexity
mysqldump + tarNoNoManualDB-only, small servers
Cloud agentVendor-dependentVariesYesHands-off, higher cost

For a deeper comparison of sync tools, see rsync vs rclone for server backups. restic often pairs with rclone-backed repositories when you need exotic backends.

How do you install restic and initialize an encrypted repository?

Start on Ubuntu 22.04 or 24.04 with a dedicated backup user or root-only automation. Install restic from your package manager or the official release binary documented at restic installation docs.

Install restic

sudo apt update
sudo apt install restic
restic version

Confirm you are on a current 0.16.x or 0.17.x release. Pin the version in your runbook so staging and production match.

Create a password file and repository

Never pass passwords on the command line; process listings leak them. Use a root-readable env file:

sudo install -d -m 0700 /root/.config/restic
sudo bash -c 'openssl rand -base64 32 > /root/.config/restic/password'
sudo chmod 0400 /root/.config/restic/password

export RESTIC_PASSWORD_FILE=/root/.config/restic/password
export RESTIC_REPOSITORY=/var/backups/restic-repo

sudo mkdir -p /var/backups/restic-repo
sudo restic init

For S3-compatible storage (AWS, Cloudflare R2, Wasabi), set the repository URL instead:

export RESTIC_REPOSITORY=s3:https://s3.amazonaws.com/my-restic-bucket/host1
export AWS_ACCESS_KEY_ID=AKIA...
export AWS_SECRET_ACCESS_KEY=...

Follow the patterns in automate off-site backups to S3 and off-site backups to S3 or R2 with restic when wiring bucket policies and lifecycle rules.

First manual backup

sudo restic backup /var/www \
  --exclude-file=/etc/restic/excludes.txt \
  --tag daily \
  --verbose

Typical excludes for Laravel stacks: node_modules, vendor if you rebuild on deploy, and ephemeral cache paths. Keep database dumps in a known directory such as /var/backups/db/ populated by a pre-backup script — the same idea as automate database backups on Linux.

restic Scheduled Backup PipelinePre-backupmysqldumprestic backupnew snapshotrestic checkverify packsforget --pruneretentionExample Retention Policy--keep-daily 7 --keep-weekly 4 --keep-monthly 6--keep-yearly 2 for compliance archivesAlign with RPO/RTO from your DR plan
Pipeline to Automate Backups with restic: dump databases, snapshot files, verify, then prune old snapshots.

How do you Automate Backups with restic using cron and systemd?

Automation fails when credentials live in crontab lines or when jobs overlap. Use a wrapper script, file locks, and structured logging.

Wrapper script at /usr/local/bin/restic-backup.sh

#!/bin/bash
set -euo pipefail

LOCKFILE=/var/run/restic-backup.lock
exec 9>"$LOCKFILE"
flock -n 9 || { echo "Backup already running"; exit 0; }

set -a
source /root/.config/restic/env
set +a

/usr/local/bin/pre-backup.sh

restic backup /var/www /etc /var/backups/db \
  --exclude-file=/etc/restic/excludes.txt \
  --tag "host-$(hostname -s)" \
  --tag daily

restic forget --tag daily \
  --keep-daily 7 --keep-weekly 4 --keep-monthly 6 \
  --prune

restic check --read-data-subset=5%

Store all variables in /root/.config/restic/env:

RESTIC_PASSWORD_FILE=/root/.config/restic/password
RESTIC_REPOSITORY=s3:https://s3.amazonaws.com/my-bucket/prod-web-01
AWS_DEFAULT_REGION=ap-south-1

Protect that file with mode 0400. Generate strong repository passwords with a password generator and record them in your team vault — not in ticket comments.

cron entry

sudo crontab -e

15 2 * * * /usr/local/bin/restic-backup.sh >> /var/log/restic-backup.log 2>&1

Run during low-traffic windows. For Nepal-hosted sites, 02:15 NPT often clears the midnight batch of WordPress cron and Laravel scheduler jobs. Stagger multi-server fleets so S3 egress and API rate limits are not hit at once.

systemd timer alternative

systemd gives you journald integration and dependency ordering — useful when backup must run after MySQL flush scripts. Define /etc/systemd/system/restic-backup.service and a matching timer unit. Official timer syntax is documented in the systemd.timer manual.

  1. Create the oneshot service invoking your wrapper script.
  2. Create a timer with OnCalendar=*-*-* 02:15:00 and Persistent=true.
  3. Enable with systemctl enable --now restic-backup.timer.
  4. Inspect failures via journalctl -u restic-backup.service.

This matches the operational style in automated server backups complete setup and Ubuntu server backup strategies.

How do you send restic snapshots to off-site S3 or SFTP storage?

Local repositories protect against accidental deletes, not datacenter loss. Copy snapshots off the machine. restic speaks S3 natively; for SFTP use restic init --repo sftp:user@backup-host:/path with SSH keys.

S3 bucket hardening checklist

  • Dedicated IAM user with s3:ListBucket scoped to the prefix and s3:GetObject/s3:PutObject only.
  • Bucket versioning enabled; block public access at account level.
  • Separate bucket per environment — never share prod and staging credentials.
  • Lifecycle transition to Glacier only if restore drills account for retrieval latency.

On sister sites I maintain — including Notary Kathmandu and Translation Nepal — identical restic env files differ only by repository prefix and tags. That keeps Deployer releases and user uploads recoverable from one playbook.

3-2-1 Backup Topology with resticProductionlive app + DBLocal restic repofast restoreOff-site S3 reporegion isolated3 copies · 2 media types · 1 off-siterestic copy command syncs snapshots between reposTag snapshots by host and environmentNever store RESTIC_PASSWORD only on the server you backup
Automate Backups with restic using local and off-site repositories to satisfy the 3-2-1 rule.

Sync a second copy with:

restic copy --from-repo /var/backups/restic-repo \
  --to-repo s3:https://s3.amazonaws.com/my-bucket/dr-copy

Schedule restic copy weekly after your daily backup window. Read restic fast encrypted backups for performance tuning when repositories grow past a few hundred gigabytes.

How do you verify, restore, and monitor automated restic backups?

Backups you never restore are wishful thinking. Build verification into the same automation that creates snapshots.

Listing and testing restores

restic snapshots
restic ls latest

restic restore latest --target /tmp/restic-restore-test --include /var/www/example.com/.env
diff /var/www/example.com/.env /tmp/restic-restore-test/var/www/example.com/.env

Monthly, restore a random file and a database dump to a staging path. Document steps in your runbook per test and validate your backups. For MySQL-heavy stacks, combine file restore with logical dumps described in database backup strategies for small servers.

Monitoring and alerts

Parse exit codes in your wrapper. restic returns non-zero on failure. Send mail or webhook alerts when:

  • restic backup fails or skips expected paths.
  • restic check reports pack corruption.
  • No new snapshot appears within 26 hours.
  • Repository size jumps more than 40% day-over-day — possible ransomware or log runaway.

Hook alerts into the same channels you use for SSL expiry and deploy failures. Support and maintenance retainers should include quarterly restore drills, not just disk space graphs.

restic Restore Decision TreeWhat failed?Single file lostrestic restore --includeApp dir corruptrestore target pathFull server lossnew VM + init repoAlways restore DB dumps to staging firstConfirm checksum before touching production MySQL
Choose the smallest restic restore scope that fixes the incident, then escalate to full-server recovery only when needed.

Common production gotchas

I've encountered these during real deployments:

  • Stale opcache after restore: reload PHP-FPM after restoring PHP files so workers serve fresh code.
  • Wrong RESTIC_REPOSITORY in env: staging credentials writing into prod prefix — use hostname tags and separate env files.
  • Overlapping cron jobs: without flock, two backups corrupt snapshot metadata under load.
  • Forgetting prune: repositories grow until the disk fills; always pair backup with forget --prune.
  • Password loss: encrypted data is unrecoverable without RESTIC_PASSWORD — store offline copies.

Legal-tech portals with uploaded PDFs and client documents — like those in our Court Marriage in Nepal and Mijar Law Associates work — need document paths included explicitly. Verify storage/app and private upload dirs are not excluded by a overly broad pattern.

For hosting migrations, pair restic with website migration planning so DNS cutover happens only after a verified restore on the target VM. Domain and hosting choices affect which S3 region keeps latency acceptable for nightly runs.

Key Takeaways

  • Initialize an encrypted restic repository, keep passwords in a root-only file, and never commit secrets to Git.
  • Automate Backups with restic through a locked wrapper script plus cron or systemd timers that run backup, forget, and check in sequence.
  • Mirror snapshots off-site with S3 or SFTP repos; use restic copy for a second location.
  • Tag snapshots by host and environment; apply daily/weekly/monthly retention with forget --prune.
  • Restore to staging monthly and alert on missing snapshots or failed checks — backups exist to be tested.
  • Layer restic file snapshots atop logical database dumps for complete Laravel and WordPress recovery paths.

People Also Ask

Is restic suitable for large MySQL databases?

restic backs up files, not live InnoDB tables safely by itself. Dump databases first with mysqldump or Percona tools, then snapshot the dump directory. For very large datasets, consider binary log strategies from our MySQL replication article alongside file-level restic runs.

How much disk space does a restic repository need?

First snapshot size approximates source data. Later snapshots store only changed blocks — often 1–5% of full size for typical web apps. Plan headroom for prune operations, which temporarily need extra space while repacking.

Can restic run without root?

Yes, if the backup user can read all target paths. System configs and other users' web roots usually require root or ACL adjustments. Running as root with locked-down env files is simpler on single-tenant VPS hosts.

Does restic work with Cloudflare R2 or Wasabi?

Any S3-compatible endpoint works when you set the correct region and path-style URL. Test with a small repository before pointing production schedules at the bucket.

Build a backup pipeline you will actually restore from

Automate Backups with restic when you want encrypted, deduplicated snapshots under your control — not when you want a set-and-forget checkbox. Wire the wrapper script this week, schedule off-site copy next, and book a restore drill before your next framework upgrade. If you want help auditing an existing Ubuntu fleet or wiring restic into a Laravel deploy pipeline, see our Linux administration services or contact us for a scoped review. Related reading: backup and disaster recovery on the cloud, automate server backups with rsync and cron, and the home page for more engineering guides from Kokil Thapa.

Frequently Asked Questions

restic is a single-binary backup program written in Go. It creates encrypted, content-addressed snapshots of files and directories, storing only changed data blocks on each run. Compared with plain tar plus rsync, you get snapshot history, AES-256 encryption at rest, and built-in integrity checks. Compared with cloud-only agents, you control retention, scheduling, and storage location. On production Laravel servers I maintain, that control matters on budget VPS plans where off-site copy to S3 or Backblaze B2 often costs Rs 500–2,000/month (~USD 4–15) instead of a full managed backup SKU.

On Ubuntu 22.04 or 24.04, install restic from the package manager or official release binary, then confirm you are on a current 0.16.x or 0.17.x release and pin the version in your runbook. Never pass passwords on the command line. Create a root-readable password file at /root/.config/restic/password with mode 0400, set RESTIC_PASSWORD_FILE and RESTIC_REPOSITORY, then run restic init. For local storage use /var/backups/restic-repo; for S3-compatible backends set the s3: URL plus AWS credentials in a protected env file.

Often Rs 500–2,000/month (~USD 4–15) on S3 or Backblaze B2, versus a full managed backup SKU.

Automation fails when credentials live in crontab lines or jobs overlap. Use a wrapper script at /usr/local/bin/restic-backup.sh with set -euo pipefail, a flock lock file at /var/run/restic-backup.lock, and variables sourced from /root/.config/restic/env protected with mode 0400. The script should run a pre-backup database dump, restic backup with tags, restic forget --prune for retention, and restic check. Schedule via root crontab at 02:15 during low-traffic windows, logging to /var/log/restic-backup.log.

Both can trigger the same wrapper script, but systemd timers give you journald integration and dependency ordering, which helps when backup must run after MySQL flush scripts. Define a oneshot restic-backup.service and a matching timer with OnCalendar set to 02:15 and Persistent=true, then enable with systemctl enable --now restic-backup.timer. Inspect failures via journalctl -u restic-backup.service. Cron is simpler and widely familiar; systemd fits fleets where backup ordering and structured logging matter more.

restic speaks S3 natively; set RESTIC_REPOSITORY to an s3: URL with AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY in your env file. For SFTP use restic init with sftp:user@backup-host:/path and SSH keys. Harden S3 buckets with a dedicated IAM user scoped to the prefix, versioning enabled, public access blocked, and separate buckets per environment. Schedule restic copy weekly from a local repo to an off-site repo for a second location, satisfying the 3-2-1 rule without sharing prod and staging credentials.

Yes. Any S3-compatible endpoint works when you set the correct region and path-style URL in RESTIC_REPOSITORY and your credential env vars. Cloudflare R2 and Wasabi are explicitly supported patterns in production setups alongside AWS S3. Test with a small repository and a manual backup before pointing your nightly cron or systemd timer at the bucket. Confirm bucket policies, lifecycle rules, and IAM or API key scoping match your prefix layout so staging credentials cannot write into production prefixes.

Layer the tools rather than choosing one. Spatie Laravel Backup handles in-app database exports; restic then snapshots those dump files together with storage/ and config paths. Typical excludes for Laravel stacks include node_modules, vendor if you rebuild on deploy, and ephemeral cache paths. Keep database dumps in a known directory such as /var/backups/db/ populated by a pre-backup script. Include storage/app and private upload directories explicitly, especially on legal-tech portals where client PDFs must not be dropped by an overly broad exclude pattern.

restic backs up files, not live InnoDB tables safely by itself. Dump databases first with mysqldump or Percona tools, then snapshot the dump directory through your restic wrapper. For very large datasets, consider binary log strategies alongside file-level restic runs. On MySQL-heavy stacks, combine monthly file restore tests with validation of logical dumps. The pre-backup.sh step in your automation pipeline should flush and export before restic backup runs, matching the same pattern used for automated database backups on Linux production servers.

First snapshot size approximates source data. Later snapshots store only changed blocks, often 1–5% of full size for typical web apps.

Build verification into the same automation that creates snapshots. Your wrapper should run restic check --read-data-subset=5% after backup and forget. Monthly, list snapshots with restic snapshots, restore a random file and a database dump to a staging path such as /tmp/restic-restore-test, and diff critical files like .env against production. Document steps in your runbook per test. Parse exit codes and alert when backup fails, check reports corruption, or no new snapshot appears within 26 hours.

A practical starting policy from production runbooks is --keep-daily 7 --keep-weekly 4 --keep-monthly 6 applied to snapshots tagged daily, followed by --prune to reclaim space. Always pair backup with forget --prune; repositories grow until the disk fills if you skip pruning. Tag snapshots by host and environment using flags like --tag host-$(hostname -s) and --tag daily so retention rules do not accidentally prune the wrong fleet. Prune operations temporarily need extra headroom while repacking.

I've encountered these repeatedly on real deployments. Stale opcache after restore requires reloading PHP-FPM so workers serve fresh code. Wrong RESTIC_REPOSITORY in env can send staging credentials writing into prod prefixes, so use hostname tags and separate env files. Overlapping cron jobs without flock can corrupt snapshot metadata under load. Forgetting prune fills disks over time. Password loss means encrypted data is unrecoverable without RESTIC_PASSWORD, so store offline copies in your team vault, never in ticket comments or Git.

Yes, if the backup user can read all target paths. System configs and other users' web roots usually require root or ACL adjustments. Running as root with locked-down env files at mode 0400 is simpler on single-tenant VPS hosts where you already manage Apache, PHP-FPM, and Deployer releases. For multi-user servers, grant read access to application paths explicitly rather than weakening exclude rules. The security goal is keeping RESTIC_PASSWORD_FILE and repository credentials out of process listings and version control regardless of which user runs the job.

Parse exit codes in your wrapper script because restic returns non-zero on failure. Send mail or webhook alerts when restic backup fails or skips expected paths, restic check reports pack corruption, no new snapshot appears within 26 hours, or repository size jumps more than 40% day-over-day indicating possible ransomware or log runaway. Hook alerts into the same channels used for SSL expiry and deploy failures. Support retainers should include quarterly restore drills, not just disk space graphs, because backups you never restore are wishful thinking.

Share this article

0 Comments

Leave a comment

Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

Quick Contact Options
Choose how you want to connect me: