Storage AWS

Backrest 1.14.1 on AWS User Guide

| Product: Backrest 1.14.1 on Ubuntu 24.04 on AWS

Overview

This guide covers launching and using the Backrest 1.14.1 on Ubuntu 24.04 AMI from cloudimg on Amazon EC2. It is one version of the cloudimg backup and recovery listing on AWS Marketplace; you pick it as the software version when you subscribe and launch.

Backrest is a web interface and scheduler for restic, the fast, encrypting, deduplicating backup program. You add repositories (a local directory, Amazon S3 or any S3 compatible store, SFTP, a restic REST server, Backblaze B2 and more), define backup plans with schedules and retention policies, and browse or restore any snapshot from the browser. Every backup is a real restic snapshot, so your data stays in the open restic repository format and can always be restored with plain restic.

Upstream Backrest serves plain HTTP, and when it starts without a configuration file it runs with authentication disabled and offers a first run screen to whoever opens it first. Because Backrest can run commands on the server (hooks), that would be a remote shell for anyone who reaches it. The cloudimg image makes it safe to put on a network:

  • HTTPS from the first boot. nginx serves Backrest on port 443 with a certificate generated on your instance's first boot, naming the instance's DNS name and IP addresses. Backrest itself listens on 127.0.0.1:9898 only, and nothing listens on port 80.
  • A login unique to your instance, always on. First boot creates the user admin with a random password written to /root/backrest-credentials.txt (root only); the password is hashed with bcrypt from standard input, never placed on a command line, and Backrest stores only the hash. A start guard refuses to run Backrest unless authentication is on with a bcrypt login, and a watcher stops Backrest within seconds if anyone turns authentication off, so the first run screen and an unauthenticated API are never reachable.
  • Backups and hooks run unprivileged. Backrest, restic and every hook command you configure run as the backrest system user (no shell, no password, no sudo, no Linux capabilities) inside a sandboxed systemd service. It backs up what you grant it read access to (Step 8).
  • Metadata service blocked. Hooks can call webhooks and repositories can point at any URL, so Backrest (with restic and its hooks), nginx and the first boot service are blocked from the instance metadata service, and the AMI requires IMDSv2. Repositories on Amazon S3 therefore use access keys, not the instance role (Step 9).
  • Locked down serving. Requests for host names the instance does not serve are dropped; the client address Backrest sees comes from the connection, never from a forwarding header a client sends; sign in attempts are rate limited per client.
  • restic built in. restic 0.19.1, the exact version Backrest 1.14.1 requires, is on the image, so Backrest never downloads anything at run time.
  • Your data on its own disk. Backrest's configuration, operation log, restic cache, local repositories and restores live on a separate 10 GiB EBS volume mounted at /var/lib/backrest without device files, set-uid programs or execution, which you can grow and snapshot on its own.
  • The official releases, verified against their published checksums, and fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.

What is on the image: Backrest 1.14.1 (the official release binary /usr/local/bin/backrest, licence in /usr/share/doc/backrest), restic 0.19.1 (/usr/local/bin/restic, licence in /usr/share/doc/restic), nginx, Ubuntu 24.04 LTS, cloud-init and the AWS Systems Manager agent.

Default user: ubuntu · Architecture: x86_64 · Recommended instance type: m5.large

Prerequisites

  • An AWS account subscribed to the listing, an EC2 key pair and a VPC subnet with a route to the internet.
  • A security group that allows TCP 22 (SSH) and TCP 443 (the Backrest web UI) from your own networks only. Nothing else needs to be open.

Step 1: Launch the instance

In the AWS Marketplace listing choose Continue to Subscribe, then Continue to Configuration, pick Backrest 1.14.1 on Ubuntu 24.04 as the software version and your Region, then Launch through EC2. Choose m5.large, your key pair, and a security group as above. The AMI already carries the 10 GiB data volume; make it larger on the launch page if you plan to keep local repositories on the instance (you can also grow it later, Step 12).

With the AWS CLI (replace the values in angle brackets):

aws ec2 run-instances --region <region> --image-id <ami-id-from-the-listing> \
  --instance-type m5.large --key-name <your-key-pair> \
  --security-group-ids <your-security-group> --subnet-id <your-subnet> \
  --metadata-options HttpTokens=required,HttpEndpoint=enabled \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=backrest}]'

Step 2: Connect to your instance

Version SSH user
Backrest 1.14.1 on Ubuntu 24.04 ubuntu
ssh -i <your-key.pem> ubuntu@<public-ip>

Step 3: Check the services

First boot generates the certificate and the admin login, then Backrest, nginx and the authentication watcher start:

systemctl is-active backrest nginx backrest-auth-watch.path
sudo test -f /var/lib/cloudimg/backrest-firstboot.done && echo FIRSTBOOT_DONE
active
active
active
FIRSTBOOT_DONE

Only SSH and nginx listen on the network. Backrest listens on loopback behind nginx, and the DNS resolver listens on loopback only:

sudo ss -lntp | awk 'NR>1 {print $4, $6}' | sed -E 's/pid=[0-9]+,fd=[0-9]+/.../g' | sort
0.0.0.0:22 users:(("sshd",...),("systemd",...))
0.0.0.0:443 users:(("nginx",...),("nginx",...),("nginx",...))
127.0.0.1:9898 users:(("backrest",...))
127.0.0.53%lo:53 users:(("systemd-resolve",...))
127.0.0.54:53 users:(("systemd-resolve",...))
[::]:22 users:(("sshd",...),("systemd",...))
[::]:443 users:(("nginx",...),("nginx",...),("nginx",...))

nginx answers its health check and serves the web UI over HTTPS, verified against the instance's own certificate:

curl -s --cacert /etc/nginx/tls/backrest.crt https://127.0.0.1/healthz
curl -s --cacert /etc/nginx/tls/backrest.crt -o /dev/null -w 'web UI -> HTTP %{http_code}\n' https://127.0.0.1/
ok
web UI -> HTTP 200

Step 4: Get your URL and password

The credentials file is readable by root only. This prints it with the password hidden; sudo cat /root/backrest-credentials.txt shows the password itself:

sudo sed -E 's/^(BACKREST_ADMIN_PASSWORD=).*/\1<hidden>/' /root/backrest-credentials.txt
# Backrest 1.14.1 - generated on first boot by backrest-firstboot.service
# This password is unique to this instance; none was baked into the image. Backrest keeps only a bcrypt hash of
# it in /var/lib/backrest/config/config.json. Keep this file secret.

BACKREST_URL=https://ec2-<public-ip-dashed>.compute-1.amazonaws.com/
BACKREST_ADMIN_USER=admin
BACKREST_ADMIN_PASSWORD=<hidden>
BACKREST_INSTANCE_ID=backrest-i-0defc4653325be8a8
BACKREST_CA_CERT=/etc/nginx/tls/backrest.crt
BACKREST_CERT_SHA256=EC:41:16:17:49:30:92:12:EA:0D:28:32:8C:E6:F3:FB:CE:71:B3:2A:80:B8:C2:64:3D:FD:AD:71:B1:A3:2A:0C
#
# Sign in at BACKREST_URL with BACKREST_ADMIN_USER and BACKREST_ADMIN_PASSWORD. The certificate is self-signed
# (check its SHA-256 above); use your own DNS name and certificate for production.
# Local repositories: /var/lib/backrest/repos     Restores: /var/lib/backrest/restores
# New admin password:            sudo backrest-set-password
# Change the name it serves as:  sudo backrest-set-hostname <your-dns-name>
# User guide: https://www.cloudimg.co.uk/guides/backrest-1-14-1-on-ubuntu-24-04-aws/

The URL uses the instance's public DNS name from EC2 instance metadata. The certificate also names the public and private IP addresses, so https://<public-ip>/ works too. BACKREST_CERT_SHA256 is the certificate's fingerprint: compare it with the one your browser shows before you trust the certificate. The instance ID (backrest- followed by the EC2 instance ID) labels this instance's snapshots, so several instances can share one repository. To serve under your own DNS name with a certificate browsers already trust, see Step 11.

Step 5: Sign in to the web UI

Browse to the BACKREST_URL. The certificate is self signed for this instance, so the browser asks you to confirm it the first time (check the fingerprint against BACKREST_CERT_SHA256). Sign in as admin with the password from Step 4:

The Backrest login dialog with the user name admin filled in and the password masked

The dashboard summarises every plan and repository: when each last backed up, when it runs next, how much it protects and the recent operations. The screenshots here were taken after the backup in Step 6:

The Backrest dashboard: everything backed up, the demo plan with its next run and retention, recent activity and the local repository

Use Add Repo to connect a repository and Add Plan to choose what to back up, when, and how many snapshots to keep. Each plan has a Backup Now button, and its tree view lists every backup, forget and restore; select a backup to see its statistics and browse or restore its files:

The demo plan in Backrest: the tree of backups and restores, and the selected snapshot's details with the snapshot browser

Settings holds the instance ID, the user accounts and multihost sharing. Authentication is on, with the admin user created at first boot; leave Disable Authentication off (Step 10 explains what happens if it is switched on):

Backrest Settings: the instance ID, Disable Authentication switched off, and the admin user

Step 6: Back up a directory

Everything in the web UI is also available through Backrest's API. This creates a small directory to protect:

sudo install -d -m 0755 /srv/demo-data
echo "Hello from Backrest on AWS" | sudo tee /srv/demo-data/hello.txt >/dev/null
sudo cp /etc/os-release /srv/demo-data/os-release
ls -l /srv/demo-data
total 8
-rw-r--r-- 1 root root  27 Oct 20 02:56 hello.txt
-rw-r--r-- 1 root root 400 Oct 20 02:56 os-release

Then it signs in as admin (the password goes to curl on standard input, so it never appears in the process list), adds a repository called local on the data volume with a random repository password (Backrest initialises it), adds a plan called demo that backs up /srv/demo-data every night at 02:00 and keeps 7 daily, 4 weekly and 6 monthly snapshots, runs the plan now and lists its snapshots. It is safe to run again: the repository and plan are only added once, and each run adds a snapshot:

TOKEN=$(sudo sed -n 's/^BACKREST_ADMIN_PASSWORD=//p' /root/backrest-credentials.txt \
  | jq -Rc '{username: "admin", password: .}' \
  | curl -s --cacert /etc/nginx/tls/backrest.crt -H 'Content-Type: application/json' --data-binary @- \
      https://127.0.0.1/v1.Authentication/Login | jq -r '.token // empty')
br() { curl -s --cacert /etc/nginx/tls/backrest.crt -H @<(printf 'Authorization: Bearer %s\n' "$TOKEN") \
         -H 'Content-Type: application/json' --data-binary "${2:-{\}}" "https://127.0.0.1/v1.Backrest/$1"; }
[ -n "$TOKEN" ] && echo "signed in as admin"
if ! br GetConfig | jq -e '(.repos // []) | any(.id == "local")' >/dev/null; then
  openssl rand -hex 24 | jq -Rc '{repo: {id: "local", uri: "/var/lib/backrest/repos/local", password: .,
      prunePolicy: {schedule: {maxFrequencyDays: 7}}, checkPolicy: {schedule: {maxFrequencyDays: 30}}}}' \
    | br AddRepo @- | jq -r '.repos[] | select(.id == "local") | "repository \(.id): \(.uri)"'
fi
if ! br GetConfig | jq -e '(.plans // []) | any(.id == "demo")' >/dev/null; then
  br GetConfig | jq -c '.plans = ((.plans // []) + [{id: "demo", repo: "local", paths: ["/srv/demo-data"],
      schedule: {cron: "0 2 * * *", clock: "CLOCK_LOCAL"},
      retention: {policyTimeBucketed: {daily: 7, weekly: 4, monthly: 6}}}])' \
    | br SetConfig @- | jq -r '.plans[] | select(.id == "demo") | "plan \(.id): \(.paths | join(",")) -> \(.repo), daily at 02:00"'
fi
br Backup '{"value": "demo"}' >/dev/null && echo "backup of plan demo finished"
br ListSnapshots '{"repoId": "local", "planId": "demo"}' \
  | jq -r '.snapshots[] | "snapshot \(.id[0:8])  \(.paths | join(","))  files: \(.summary.totalFilesProcessed)  new data: \(.summary.dataAdded // 0) bytes"'
signed in as admin
repository local: /var/lib/backrest/repos/local
plan demo: /srv/demo-data -> local, daily at 02:00
backup of plan demo finished
snapshot 27f08332  /srv/demo-data  files: 2  new data: 1830 bytes

The repository is an ordinary restic repository, so restic itself can read it. This runs restic as the backrest user and reads the repository password from Backrest's configuration (it is never printed):

sudo -u backrest XDG_CACHE_HOME=/var/lib/backrest/cache \
  RESTIC_PASSWORD_COMMAND='jq -r --arg id local .repos[]|select(.id==$id).password /var/lib/backrest/config/config.json' \
  restic -r /var/lib/backrest/repos/local snapshots
ID        Time                 Host              Tags                                               Paths           Size
-------------------------------------------------------------------------------------------------------------------------
27f08332  2026-10-20 02:56:32  ip-172-31-83-155  plan:demo,created-by:backrest-i-0defc4653325be8a8  /srv/demo-data  427 B
fe86282d  2026-10-20 02:56:45  ip-172-31-83-155  plan:demo,created-by:backrest-i-0defc4653325be8a8  /srv/demo-data  427 B
-------------------------------------------------------------------------------------------------------------------------
Timestamps shown in local time
2 snapshots

Keep every repository password somewhere safe, outside this instance. restic encrypts the repository with it, and without it nobody (cloudimg included) can restore your data. Backrest shows it when you edit the repository in the web UI, and sudo jq -r '.repos[] | "\(.id) \(.password)"' /var/lib/backrest/config/config.json prints them all.

Step 7: Restore files

Restores go to a directory you choose (the plan's Restore action in the web UI, or the API). This restores /srv/demo-data from the newest snapshot into a new directory under /var/lib/backrest/restores and compares it with the original:

TOKEN=$(sudo sed -n 's/^BACKREST_ADMIN_PASSWORD=//p' /root/backrest-credentials.txt \
  | jq -Rc '{username: "admin", password: .}' \
  | curl -s --cacert /etc/nginx/tls/backrest.crt -H 'Content-Type: application/json' --data-binary @- \
      https://127.0.0.1/v1.Authentication/Login | jq -r '.token // empty')
br() { curl -s --cacert /etc/nginx/tls/backrest.crt -H @<(printf 'Authorization: Bearer %s\n' "$TOKEN") \
         -H 'Content-Type: application/json' --data-binary "${2:-{\}}" "https://127.0.0.1/v1.Backrest/$1"; }
SNAP=$(br ListSnapshots '{"repoId": "local", "planId": "demo"}' | jq -r '.snapshots | last | .id')
TARGET=/var/lib/backrest/restores/demo-$(date -u +%Y%m%dT%H%M%S)
jq -nc --arg s "$SNAP" --arg t "$TARGET" \
  '{planId: "demo", repoId: "local", snapshotId: $s, path: "/srv/demo-data", target: $t}' \
  | br Restore @- | jq -r '"restore scheduled as operation \(.operationId)"'
for i in $(seq 1 30); do sudo test -f "$TARGET/demo-data/hello.txt" && break; sleep 2; done
sudo ls -l "$TARGET/demo-data"
sudo diff -r /srv/demo-data "$TARGET/demo-data" && echo "restored files match /srv/demo-data"
restore scheduled as operation 13
total 8
-rw-r--r-- 1 backrest backrest  27 Oct 20 02:56 hello.txt
-rw-r--r-- 1 backrest backrest 400 Oct 20 02:56 os-release
restored files match /srv/demo-data

Restored files belong to the backrest user. Backrest writes restores only where the backrest user may write: /var/lib/backrest/restores on its data volume, or a directory you create for it (for example sudo install -d -o backrest -g backrest /srv/restores). System directories such as /etc and /usr, and home directories, are read only for Backrest; restore elsewhere first and copy the files into place with sudo. In the web UI you can also download a single file or a folder as an archive from the snapshot browser.

Step 8: Back up your own data (read access for the backrest user)

Backrest runs as the unprivileged backrest user, so it backs up files that user can read: world readable files as above, and anything you grant it. A directory only root can read is refused until you give backrest read access, for example with an access control list (this also installs the acl tools):

sudo install -d -m 0700 /srv/private-data
echo "private notes" | sudo tee /srv/private-data/notes.txt >/dev/null; sudo chmod 0600 /srv/private-data/notes.txt
sudo -u backrest cat /srv/private-data/notes.txt 2>&1 || true
sudo DEBIAN_FRONTEND=noninteractive apt-get -o DPkg::Lock::Timeout=600 update -qq >/dev/null 2>&1
sudo DEBIAN_FRONTEND=noninteractive apt-get -o DPkg::Lock::Timeout=600 install -y -qq acl >/dev/null 2>&1
sudo setfacl -R -m u:backrest:rX /srv/private-data
sudo setfacl -d -m u:backrest:rX /srv/private-data
sudo -u backrest cat /srv/private-data/notes.txt
cat: /srv/private-data/notes.txt: Permission denied
private notes

The default ACL (-d) gives backrest read access to files created there later too. Grant read access only to what you want backed up. To back up data from other machines, run Backrest (or restic) on those machines and point them at a shared repository, or use this instance as the repository server: the Rest Server version in this listing is built for that.

Step 9: Repositories in Amazon S3 and elsewhere

Click Add Repo in the web UI and give the repository URI in restic's form, with any credentials as environment variables of the repository:

Where Repository URI Environment variables
Amazon S3 s3:s3.<region>.amazonaws.com/<bucket>/<prefix> AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY
S3 compatible (MinIO, Wasabi, R2) s3:https://<endpoint>/<bucket>/<prefix> AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY
restic REST server rest:https://<user>:<password>@<host>/<user>/<repo> (none)
SFTP sftp:<user>@<host>:/<path> (none; Backrest sets up the SSH key)
Backblaze B2 b2:<bucket>:<prefix> B2_ACCOUNT_ID, B2_ACCOUNT_KEY

For Amazon S3, create an IAM user (or role you assume elsewhere) whose access key can read and write only that bucket and prefix, and give the key to the repository. The instance's IAM role is deliberately not reachable: Backrest, restic and hook commands are blocked from the instance metadata service, so a hook or repository URL cannot be used to steal the instance's credentials. Backrest stores repository passwords and environment variables in its configuration file (/var/lib/backrest/config/config.json, readable only by backrest and root).

Under Hooks a plan or repository can run a command or notify Discord, Slack, Telegram, Gotify, Healthchecks or any Shoutrrr URL when a backup starts, succeeds or fails. Command hooks run as the backrest user inside the same sandbox as Backrest.

Step 10: Check the security posture

Every API call needs a login (including the one that would switch authentication off), a wrong password is refused, a request for a host name the instance does not serve is dropped without an answer, and nothing listens on port 80:

CA=/etc/nginx/tls/backrest.crt; J='Content-Type: application/json'
curl -s --cacert $CA -H "$J" -d '{}' -o /dev/null -w 'read the configuration, no login      -> HTTP %{http_code}\n' https://127.0.0.1/v1.Backrest/GetConfig
curl -s --cacert $CA -H "$J" -d '{"auth":{"disabled":true}}' -o /dev/null \
  -w 'turn authentication off, no login    -> HTTP %{http_code}\n' https://127.0.0.1/v1.Backrest/SetConfig
printf '{"username":"admin","password":"not-the-password"}' | curl -s --cacert $CA -H "$J" --data-binary @- -o /dev/null \
  -w 'sign in with a wrong password        -> HTTP %{http_code}\n' https://127.0.0.1/v1.Authentication/Login
curl -s --cacert $CA -o /dev/null -w 'metrics, no login                    -> HTTP %{http_code}\n' https://127.0.0.1/metrics
curl -s --cacert $CA -H 'Host: attacker.example' -o /dev/null -w 'unknown host name                    -> %{http_code}\n' https://127.0.0.1/healthz || true
curl -s -o /dev/null -m 5 -w 'plain HTTP on port 80                -> %{http_code}\n' http://127.0.0.1/ || true
read the configuration, no login      -> HTTP 401
turn authentication off, no login    -> HTTP 401
sign in with a wrong password        -> HTTP 401
metrics, no login                    -> HTTP 401
unknown host name                    -> 000
plain HTTP on port 80                -> 000

000 means no answer: nginx closes the connection for a host name it does not serve, and nothing listens on port 80.

Backrest runs as the unprivileged backrest user on loopback, with no capabilities and a read only view of the system, behind the authentication guard; Backrest (and so restic and every hook), nginx and the first boot service cannot reach the instance metadata service:

ps -o user:10=,args= -C backrest
systemctl cat backrest | grep -E '^(User|ExecStartPre|ExecStart|Environment=BACKREST_PORT|NoNewPrivileges|CapabilityBoundingSet|ProtectSystem)='
systemctl show backrest nginx backrest-firstboot -p Id -p IPAddressDeny
backrest   /usr/local/bin/backrest
User=backrest
Environment=BACKREST_PORT=127.0.0.1:9898
ExecStartPre=+/usr/local/sbin/backrest-auth-guard
ExecStart=/usr/local/bin/backrest
NoNewPrivileges=true
CapabilityBoundingSet=
ProtectSystem=full
IPAddressDeny=fd00:ec2::254/128 168.63.129.16/32 169.254.169.254/32
Id=backrest.service

IPAddressDeny=fd00:ec2::254/128 168.63.129.16/32 169.254.169.254/32
Id=nginx.service

IPAddressDeny=fd00:ec2::254/128 168.63.129.16/32 169.254.169.254/32
Id=backrest-firstboot.service

Authentication cannot be switched off while the UI is exposed. backrest-auth-guard runs before Backrest starts and refuses unless first boot has completed and the configuration has authentication on, an instance ID and at least one user with a bcrypt password. backrest-auth-watch.path watches the configuration file: if Disable Authentication is switched on in Settings (or the last user is removed), Backrest is stopped within seconds and stays stopped until authentication is back on. To recover, run sudo backrest-set-password (Step 11), which turns authentication back on, sets a new admin password and starts Backrest again.

sudo backrest-auth-guard && echo "auth guard: first boot done, authentication on, bcrypt login present"
systemctl is-enabled backrest-auth-watch.path; systemctl is-active backrest-auth-watch.path
sudo jq -c '{instance, auth: {disabled: (.auth.disabled // false), users: [.auth.users[].name]}}' /var/lib/backrest/config/config.json
auth guard: first boot done, authentication on, bcrypt login present
enabled
active
{"instance":"backrest-i-0defc4653325be8a8","auth":{"disabled":false,"users":["admin"]}}

The credentials file, the TLS key and Backrest's configuration are readable by their owners only, and the certificate was generated on this instance for its own addresses:

sudo stat -c '%a %U:%G %n' /root/backrest-credentials.txt /etc/nginx/tls/backrest.key /var/lib/backrest/config/config.json
openssl x509 -noout -subject -ext subjectAltName -in /etc/nginx/tls/backrest.crt
600 root:root /root/backrest-credentials.txt
600 root:root /etc/nginx/tls/backrest.key
600 backrest:backrest /var/lib/backrest/config/config.json
subject=CN = ec2-<public-ip-dashed>.compute-1.amazonaws.com, O = cloudimg
X509v3 Subject Alternative Name: 
    DNS:ec2-<public-ip-dashed>.compute-1.amazonaws.com, IP Address:<public-ip>, IP Address:<private-ip>, IP Address:127.0.0.1, DNS:localhost

Step 11: Users and passwords

Add users or change a password from Settings > Authentication in the web UI. If the admin password is lost, this sets a new random one, records it in the credentials file and restarts Backrest (it also turns authentication back on if it was switched off):

sudo backrest-set-password
sudo grep -c '^BACKREST_ADMIN_PASSWORD=' /root/backrest-credentials.txt
for i in $(seq 1 15); do [ "$(curl -s --cacert /etc/nginx/tls/backrest.crt -o /dev/null -w '%{http_code}' https://127.0.0.1/)" = 200 ] && break; sleep 1; done
systemctl is-active backrest
backrest-set-password: password set for user admin (see /root/backrest-credentials.txt)
1
active

sudo backrest-set-password --user <name> --stdin sets the password of another user from standard input instead (12 to 128 characters).

To serve under your own DNS name, point a DNS record at the instance, then set the name Backrest is served as. This mints a new self signed certificate for the name (and the instance's IP addresses), updates nginx and the credentials file, and keeps the name across reboots:

sudo backrest-set-hostname <your-domain>

To use a certificate from a public or your own certificate authority, copy it over the generated pair and reload nginx (running backrest-set-hostname again replaces it with a self signed one):

sudo install -m 0644 <your-domain>.crt /etc/nginx/tls/backrest.crt
sudo install -m 0600 <your-domain>.key /etc/nginx/tls/backrest.key
sudo nginx -t && sudo systemctl reload nginx

Step 12: The data volume

Backrest's configuration, operation log, restic cache, local repositories and restores live on the separate EBS volume, owned by the backrest service account and mounted without device files, set-uid programs or execution:

findmnt -no SOURCE,FSTYPE,OPTIONS /var/lib/backrest
df -h --output=size,used,avail,target /var/lib/backrest
sudo ls -l /var/lib/backrest
/dev/nvme1n1 ext4   rw,nosuid,nodev,noexec,relatime
 Size  Used Avail Mounted on
 9.8G  1.6M  9.3G /var/lib/backrest
total 36
drwx------ 3 backrest backrest  4096 Oct 20 02:56 cache
drwx------ 2 backrest backrest  4096 Oct 20 03:02 config
drwx------ 4 backrest backrest  4096 Oct 20 02:56 data
drwx------ 2 root     root     16384 Oct 20 02:30 lost+found
drwxr-x--- 3 backrest backrest  4096 Oct 20 02:56 repos
drwxr-x--- 4 backrest backrest  4096 Oct 20 02:58 restores

To grow it, modify the volume's size in the EC2 console, then run sudo resize2fs on its device (shown by findmnt above). A repository on the same instance protects against mistakes and lost files, not against losing the instance: keep at least one repository off the instance (Step 9), and snapshot this volume to keep Backrest's configuration and history.

Step 13: Server components

Component Path
Backrest release binary (web UI embedded) /usr/local/bin/backrest
restic release binary /usr/local/bin/restic
Licences and version record /usr/share/doc/backrest/, /usr/share/doc/restic/
Configuration (EBS volume) /var/lib/backrest/config/config.json
Operation log, task logs, JWT key (EBS volume) /var/lib/backrest/data/
Local repositories and restores (EBS volume) /var/lib/backrest/repos/, /var/lib/backrest/restores/
Service unit /etc/systemd/system/backrest.service (loopback only, unprivileged, guarded)
Authentication watcher /etc/systemd/system/backrest-auth-watch.path
nginx site /etc/nginx/sites-available/cloudimg-backrest
Certificate and key /etc/nginx/tls/
First boot script /usr/local/sbin/backrest-firstboot.sh
Helpers /usr/local/sbin/backrest-set-password, backrest-set-hostname, backrest-auth-guard
Credentials /root/backrest-credentials.txt (mode 0600)
backrest --version 2>&1 | grep 'backrest version'
restic version
cat /usr/share/doc/backrest/VERSION
nginx -v 2>&1
backrest version: 1.14.1, commit: 875c9cb51e626a75276a64da42c6a9ec0695010a
restic 0.19.1 compiled with go1.26.4 on linux/amd64
Backrest 1.14.1 (official release, GPL-3.0-only, sha256 acc7835a9c55b3d254f0fa285cacbe87ac912cb7e069d0211924838020e6921c)
nginx version: nginx/1.24.0 (Ubuntu)

Logs: sudo journalctl -u backrest for the service, each operation's log in the web UI, and /var/log/nginx/access.log for nginx (with the real client address).

To move to a newer Backrest release, download backrest_Linux_x86_64.tar.gz from the Backrest releases page, check it against the release's checksums file, snapshot the data volume, replace /usr/local/bin/backrest (owned by root, mode 0755) and run sudo systemctl restart backrest. If the new release needs a newer restic, replace /usr/local/bin/restic with that version from the restic releases page the same way. Your configuration and history in /var/lib/backrest are kept.

Step 14: Security recommendations

  • Allow ports 22 and 443 in the security group from your own networks only; the web UI can run commands on the server through hooks.
  • Store every repository password in a password manager, outside the instance (Step 6).
  • Keep at least one repository off the instance, in S3 or another site, with a key limited to that bucket and prefix (Step 9).
  • Grant the backrest user read access only to the data you want protected (Step 8).
  • Use your own DNS name and a trusted certificate (Step 11), and give each person their own Backrest user.
  • Keep the operating system patched; unattended security upgrades are on, and sudo apt-get update && sudo apt-get upgrade applies everything else. Watch the Backrest releases for security fixes.

Support and licensing

Backrest is open source under the GNU General Public License version 3 and restic under the BSD 2 Clause licence; there is no per user or per CPU licence fee. The licences are in /usr/share/doc/backrest/ and /usr/share/doc/restic/, and the Backrest source code is at github.com/garethgeorge/backrest. cloudimg is not affiliated with or endorsed by the Backrest or restic projects. The AMI price covers cloudimg's hardening, first boot setup and support.

  • Email: support@cloudimg.co.uk
  • Website: https://www.cloudimg.co.uk
  • Support: 24/7, with a 24 hour response SLA