Skip to content

juanitolaguna/restic-kit

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Restic Kit

This repository serves as a reusable template for streamlining backups. Copy the example files and populate them with your own values.

Main problems solved

  • Provides safe “cold” backups by stopping only the relevant compose stacks, then restarting them after the snapshot.
  • Makes restores fast & repeatable: stage a snapshot into a working folder, then apply it to service. It will recreate the respective folders & volumes.
  • Avoids manual volume lists maintenance by scanning compose files in the services dir and generating the backup volume mounts automatically. (Yes, volume access is mount based to keep it more safe, portable and system agnostic.)
  • Adds scheduled remote replication (restic copy to SFTP or mounted NAS) without re-running a hot backup.

Security checklist before publishing

  • Keep only template files in Git (.env.example, remotes/remote.env.example, services/*/docker-compose.env.example).
  • Keep real runtime secrets out of Git (restic-backup/.env, restic-backup/remotes/<name>/remote.env, services/*/docker-compose.env).
  • Do not store SSH private keys inside this repository tree.
  • Keep backup and staging data out of Git (restic-repository/, restic-backup/tmp-for-restore/).
  • restic-backup/docker-compose.yml mounts /var/run/docker.sock; use only trusted images and scripts in this stack.
  • If any real secret or private key was ever committed or pushed, rotate it.

2 Backup Types

At a high level there are two backup types: local and remote.

A local copy is kept for fast backup/restore operations if a service breaks or experiences data loss.

One or more remote copies are kept in case the whole server is lost and you need to rebuild on a new VM or host. Remote copies are created via restic copy instead of restic backup. This means you only need one cold backup (local), which minimizes downtime while preserving data integrity.

Important

Check available make commands first: run make inside restic-backup/ (see restic-backup/Makefile).

If you want to spin up restic do not use docker compose up, use the make command: make restic-up because before spinning up the container it generates a yml override with all the available volumes declared in the compose files within the services folder. Hence, if you add or remove services running in the homelab, you don’t have to manually declare the volumes for each service you want to back up.

All commands below assume you are in the restic-backup/ folder:

cd restic-backup

Quick start (local backups)

  1. Create your local secrets file:
cp .env.example .env

Edit .env and set RESTIC_PASSWORD.

  1. Start the restic stack (dynamic volume mounts):
make restic-up

This starts only local services (backup/prune/check).

  1. Useful local commands:

make snapshot creates a restic snapshot (manual test run; cron handles the scheduled ones).

make snapshot-list lists all available snapshots.

make snapshot-stage SNAPSHOT_ID=<id> stages a snapshot into /tmp-for-restore. Restic stores data incrementally, but the restore produces the full filesystem state for that snapshot date, ready for recovery.

make stage-apply SERVICES="paperless" restores the service folder and its Docker volumes from the staged snapshot data (the output of snapshot-stage). It stops only the containers in that compose stack, restores, then starts them again.

Remote SFTP copy (restic copy)

This avoids hot backups by copying snapshots from the local repo to a remote repo.

Remote config lives under remotes/<name>/:

  • remote.env (repo + schedule)
  • ssh/ (key, config, known_hosts)

Example structure:

remotes/sftp/ssh
├── config
├── known_hosts
└── sftpbackup

1) Create the remote config (recommended: interactive)

This creates remotes/<name>/remote.env and remotes/<name>/ssh/*:

make remote-add TYPE=sftp

Manual alternative:

mkdir -p remotes/my-remote
cp remotes/remote.env.example remotes/my-remote/remote.env

Edit remotes/my-remote/remote.env:

  • RESTIC_REPOSITORY (remote SFTP repo, e.g. sftp:user@host:/path/to/repo)
  • RESTIC_PASSWORD (remote repo password)
  • REMOTE_COPY_CRON (default: 1h after local backups)

If you keep /restic mounted read-only in the copy container, keep:

RESTIC_COPY_ARGS=--no-lock

and ensure copy runs when no other restic job is active.

2) Provide SSH key + host verification

If you used make remote-add TYPE=sftp, this is already done (you can skip this section).

Create the SSH folder and drop your private key there: (it is recommended to create a separate sftp user on your remote target, without login shell capabilities, and limited access only to a certain folder)

mkdir -p ./remotes/my-remote/ssh
cp /path/to/your/key ./remotes/my-remote/ssh/sftpbackup
chmod 700 ./remotes/my-remote/ssh
chmod 600 ./remotes/my-remote/ssh/sftpbackup

Add a minimal SSH config (recommended):

cat > ./remotes/my-remote/ssh/config <<'EOF'
Host your-host-name
  HostName your-host-name
  User sftpbackup-user
  IdentityFile /root/.ssh/sftpbackup
  IdentitiesOnly yes
EOF
chmod 600 ./remotes/my-remote/ssh/config

Add known_hosts (required because /root/.ssh is mounted read-only):

ssh-keyscan -H your-host-name >> ./remotes/my-remote/ssh/known_hosts
chmod 600 ./remotes/my-remote/ssh/known_hosts

3) Start (or recreate) the copy container

Env and SSH changes require a recreate:

make remote-up REMOTE=my-remote
make remote-up REMOTE=my-remote FORCE_RECREATE=1

4) Initialize the remote repository (one time)

make remote-init REMOTE=my-remote

5) Run remote copy / list / stage

make snapshot-remote REMOTE=my-remote
make snapshot-list-remote REMOTE=my-remote
make snapshot-stage-remote REMOTE=my-remote SNAPSHOT_ID=<id>

Adding another remote

Second SFTP remote

Recommended: run the bootstrap again and choose a new remote name:

make remote-add TYPE=sftp

Manual alternative:

  1. Copy an existing remote folder, e.g. remotes/my-remoteremotes/sftp-pi.
  2. Edit remotes/sftp-pi/remote.env and ssh/ in that folder.
  3. Start the copy container and use it:
make remote-up REMOTE=sftp-pi
make snapshot-remote REMOTE=sftp-pi

Mounted NAS / SMB / NFS remote

  1. Create a new folder, e.g. remotes/nas-homelab.
  2. In remotes/nas-homelab/remote.env, set:
RESTIC_REPOSITORY=/mnt/nas/restic
  1. You can omit ssh/ for this type.
  2. Start and use:
make remote-up REMOTE=nas-homelab
make snapshot-remote REMOTE=nas-homelab

Template notes

  • services/paperless is kept as an example stack (compose + env) for learning.
  • The services/ folder is scanned to generate dynamic volume mounts.
  • docker-compose.volumes.generated.yml is generated and not meant to be committed.
  • Remote config now lives under remotes/<name>/ (env + ssh).

Using symlinks for external folders

You can keep your services or restic repository outside this repo and symlink them in. This works as long as Docker can access the target paths.

Example:

ln -s /Users/you/my-homelab-services ./services
ln -s /Users/you/my-homelab-restic-repo ../restic-repository

Quick access check (must succeed):

docker run --rm -v /Users/you/my-homelab-services:/mnt alpine ls /mnt

If the test fails, add the target path to Docker’s shared folders (Docker Desktop Settings → Resources → File Sharing), or move the target under /Users/.... For OrbStack, paths under /Users/... are typically available by default; if a path is outside that tree, move it under /Users/... or add a bind mount to a shared path.

About

A practical backup-and-restore framework for Docker homelabs.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors