Rest Server 0.14.0
Overview
Rest Server is the HTTP server that implements the REST backend protocol of restic, the fast, encrypting backup program. Point restic on your laptops, servers and containers at it, and it becomes your own backup repository. restic encrypts and deduplicates everything on the client before upload, so the server only ever stores ciphertext. You pay for an EC2 instance and an EBS volume instead of per-request object storage charges.
This guide covers the Rest Server 0.14.0 on Ubuntu 24.04 AMI from SecureAnyCloud on Amazon EC2. It is one version of the SecureAnyCloud backup and recovery listing on AWS Marketplace. You pick it as the software version when you subscribe and launch.
Upstream Rest Server listens on plain HTTP and keeps repositories in /tmp unless you tell it otherwise. This image turns it into a production backup target.
What you get:
- HTTPS from the first boot. nginx serves Rest Server on port 443 with a certificate generated on your instance's first boot. The certificate names the instance's DNS name and IP addresses, so restic can verify it with
--cacert. Rest Server itself listens on127.0.0.1:8000only, and nothing listens on port 80. - A login unique to your instance. Every request needs HTTP Basic authentication as the user
restic, with a password generated on first boot and written to the root-only file/root/rest-server-info.txt. Rest Server stores only a bcrypt hash of it. No password is baked into the image, and Rest Server is never run in its anonymous--no-authmode. - Private repositories per user.
--private-reposconfines each user to repositories under their own name:resticreachesrest:https://<host>/restic/...and nothing else. Add a login per machine or team with one command. - Ransomware-resistant repositories on demand.
sudo rest-server-append-only onlets clients add snapshots but never delete or overwrite them, so a compromised client cannot destroy its own backups. - Fail closed. Rest Server and nginx refuse to start until first boot has generated the certificate and a bcrypt login.
- Locked-down serving. Requests for host names the instance does not serve are dropped. The client address Rest Server sees comes from the connection, never from a forwarding header a client sends. Rest Server, nginx and the first-boot service are blocked from the instance metadata service, and the AMI requires IMDSv2.
- Your repositories on their own disk. Repositories live on a separate 10 GiB EBS volume mounted at
/var/lib/resticwithout device files, set-uid programs or execution. You can grow and snapshot the volume on its own. - The official release, verified. The image uses the official Rest Server release, on fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.
What is on the image: Rest Server 0.14.0 (the official release binary in /opt/rest-server, licence in /usr/share/doc/rest-server), nginx, Ubuntu 24.04 LTS, cloud-init and the AWS Systems Manager agent. The restic client is not on the image. You run it on the machines you back up.
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 (or a private route you use for management).
- A security group that allows TCP 22 (SSH) from your own networks only, and TCP 443 (the restic REST endpoint) from the networks of the machines you back up. Nothing else needs to be open.
- restic 0.17 or later on the machines you back up. The examples use 0.18.0. Install it from your package manager or the restic releases page.
Launch the instance
In the AWS Marketplace listing, choose Continue to Subscribe, then Continue to Configuration. Pick Rest Server 0.14.0 on Ubuntu 24.04 as the software version, pick your Region, and choose Launch through EC2.
Choose m5.large, your key pair and a security group as described above. The AMI already carries the 10 GiB repository volume. Size it for your backups on the launch page, or grow it later (Step 11).
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=rest-server}]'Connect to your instance
Version SSH user
Rest Server 0.14.0 on Ubuntu 24.04 ubuntu
ssh -i <your-key.pem> ubuntu@<public-ip>Check the services
First boot generates the certificate and the login. Rest Server and nginx then start. Confirm that they are running:
systemctl is-active rest-server nginx
sudo test -f /var/lib/sac/data/rest-server-firstboot.done && echo FIRSTBOOT_DONEactive active FIRSTBOOT_DONEOnly SSH and nginx listen on the network. Rest Server 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' | sort0.0.0.0:22 users:(("sshd",...),("systemd",...)) 0.0.0.0:443 users:(("nginx",...),("nginx",...),("nginx",...)) 127.0.0.1:8000 users:(("rest-server",...)) 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",...))Get your URL and password
The credentials file is readable by root only. The command below prints it with the password hidden. Run sudo cat /root/rest-server-info.txt to show the password itself:
sudo sed -E 's/^(REST_SERVER_PASSWORD=).*/\1<hidden>/' /root/rest-server-info.txt# Rest Server 0.14.0 - generated on first boot by rest-server-firstboot.service
# This password is unique to this instance; none was baked into the image. rest-server keeps only a bcrypt hash
# of it in /etc/rest-server/.htpasswd. Keep this file secret. REST_SERVER_URL=https://ec2-<public-ip-dashed>.compute-1.amazonaws.com/ REST_SERVER_USER=restic REST_SERVER_PASSWORD=<hidden> REST_SERVER_REPOSITORY=rest:https://ec2-<public-ip-dashed>.compute-1.amazonaws.com/restic/<repository-name> REST_SERVER_CA_CERT=/etc/nginx/tls/rest-server.crt REST_SERVER_CERT_SHA256=C6:5F:16:D7:02:08:E7:0E:C3:89:1F:0C:63:C3:5F:5F:27:3A:9C:46:D7:BF:C0:9C:04:93:D4:9E:45:31:6D:8C
#
# On a client (restic 0.17 or later), with a copy of the certificate above:
# export RESTIC_REST_USERNAME=restic RESTIC_REST_PASSWORD=<REST_SERVER_PASSWORD>
# export RESTIC_REPOSITORY=rest:https://ec2-<public-ip-dashed>.compute-1.amazonaws.com/restic/<repository-name>
# restic --cacert rest-server.crt init
# Change the password:
sudo rest-server-set-password
# Add a user (own namespace):
sudo rest-server-set-password --user <name>
# Ransomware-resistant mode:
sudo rest-server-append-only on
# Change the name it serves as:
sudo rest-server-set-hostname <your-dns-name>
# User guide: https://secureanycloud.com/stacks/rest-server-0-14-0/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>/ and https://<private-ip>/ work too.
REST_SERVER_CERT_SHA256 is the certificate's fingerprint. Compare it against the copy you give your clients. To serve under your own DNS name, see Step 10.
Back up to the server
restic reads the server login from RESTIC_REST_USERNAME and RESTIC_REST_PASSWORD, the repository from RESTIC_REPOSITORY, and the certificate to trust from RESTIC_CACERT. The repository path starts with the user name (/restic/...), because each user reaches only their own repositories.
This step runs the test from the instance itself. Step 6 does the same from another machine.
Install the restic client (on the instance only for this test) and check its download:
cd /tmp && curl -fsSLO https://github.com/restic/restic/releases/download/v0.18.0/restic_0.18.0_linux_amd64.bz2 echo "98f6dd8bf5b59058d04bfd8dab58e196cc2a680666ccee90275a3b722374438e restic_0.18.0_linux_amd64.bz2" | sha256sum -c - bunzip2 -f restic_0.18.0_linux_amd64.bz2 && sudo install -m 0755 restic_0.18.0_linux_amd64 /usr/local/bin/restic && rm -f restic_0.18.0_linux_amd64 restic versionrestic_0.18.0_linux_amd64.bz2: OK restic 0.18.0 compiled with go1.24.1 on linux/amd64Keep the settings in a root-only file. The repository password is a second, separate secret. restic encrypts the repository with it, and without it nobody, including you, can read the backups. Store it somewhere safe as well:
sudo install -d -m 0700 /root/restic-demo
sudo bash -c 'umask 077; openssl rand -hex 24 > /root/restic-demo/repo-password'
sudo bash -c 'umask 077; P=$(grep "^REST_SERVER_PASSWORD=" /root/rest-server-info.txt | cut -d= -f2-) cat > /root/restic-demo/env <<ENV export RESTIC_REPOSITORY=rest:https://127.0.0.1/restic/demo export RESTIC_REST_USERNAME=restic export RESTIC_REST_PASSWORD=$P export RESTIC_PASSWORD_FILE=/root/restic-demo/repo-password export RESTIC_CACERT=/etc/nginx/tls/rest-server.crt ENV'
sudo ls -l /root/restic-demo | awk 'NR>1{print $1, $NF}'-rw------- env -rw------- repo-passwordCreate the repository, back up two folders, and list the snapshot:
sudo bash -c '. /root/restic-demo/env && restic init'created restic repository a67c345e2b at rest:https://127.0.0.1/restic/demo/ Please note that knowledge of your password is required to access the repository. Losing your password means that your data is irrecoverably lost.sudo bash -c '. /root/restic-demo/env && restic backup /etc/nginx/sites-available /usr/share/doc/rest-server'no parent snapshot found, will read all files Files: 4 new, 0 changed, 0 unmodified Dirs: 7 new, 0 changed, 0 unmodified Added to the repository: 20.943 KiB (10.309 KiB stored) processed 4 files, 17.174 KiB in 0:00 snapshot e25661b1 savedsudo bash -c '. /root/restic-demo/env && restic snapshots'ID Time Host Tags Paths Size --------------------------------------------------------------------------------------------------- e25661b1 2026-10-19 20:56:59 ip-<private-ip-dashed> /etc/nginx/sites-available 17.174 KiB /usr/share/doc/rest-server --------------------------------------------------------------------------------------------------- 1 snapshotsRestore a folder from the snapshot and compare it with the original. Then run a restic check on the whole repository:
sudo bash -c '. /root/restic-demo/env && restic restore latest --target /tmp/restic-restore --include /usr/share/doc/rest-server' diff -r /usr/share/doc/rest-server /tmp/restic-restore/usr/share/doc/rest-server && echo RESTORED_IDENTICAL rm -rf /tmp/restic-restorerestoring snapshot e25661b1 of [/etc/nginx/sites-available /usr/share/doc/rest-server] at 2026-10-19 20:56:59.455804574 +0000 UTC by root@ip-<private-ip-dashed> to /tmp/restic-restore Summary: Restored 6 / 3 files/dirs (12.749 KiB / 12.749 KiB) in 0:00 RESTORED_IDENTICALsudo bash -c '. /root/restic-demo/env && restic check'using temporary cache in /tmp/restic-check-cache-2548086462 create exclusive lock for repository load indexes check all packs check snapshots, trees and blobs [0:00] 100.00% 1 / 1 snapshots no errors were foundOn the server the repository is a folder under the user's name on the repository volume, owned by the unprivileged restic service account and readable by nobody else:
sudo ls -l /var/lib/restic/restic/demo | awk 'NR>1{print $1, $3, $NF}'-rw------- restic config drwx------ restic data drwx------ restic index drwx------ restic keys drwx------ restic locks drwx------ restic snapshotsBack up from your own machines
On each machine you back up, install restic 0.17 or later. Copy the server's certificate from the instance, and compare its fingerprint with REST_SERVER_CERT_SHA256 from Step 4:
# on your laptop or server scp -i <your-key.pem> ubuntu@<public-ip>:/etc/nginx/tls/rest-server.crt ~/rest-server.crt openssl x509 -noout -fingerprint -sha256 -in ~/rest-server.crtSet the same variables, using the server's DNS name and the password from Step 4, and use restic as usual. Give each machine its own repository name or its own login (Step 9):
# on your laptop or server export RESTIC_REPOSITORY=rest:https://<public-dns-name>/restic/laptop export RESTIC_REST_USERNAME=restic export RESTIC_REST_PASSWORD='<your-password>' export RESTIC_PASSWORD_FILE=~/.restic-repo-password export RESTIC_CACERT=~/rest-server.crt restic init restic backup ~/Documents restic snapshotsUse the environment variables rather than putting the password in the repository URL (rest:https://restic:<password>@host/...), which shows up in the process list.
Schedule restic backup with cron or a systemd timer. Run restic forget --keep-daily 7 --keep-weekly 4 --prune periodically to expire old snapshots. If you use append-only mode (Step 8), run it on the server.
Check the security posture
Requests without the login, or with a wrong password, are refused. The health check stays open for load balancers. A request for a host name the instance does not serve is dropped without an answer, and nothing answers on port 80:
C=/etc/nginx/tls/rest-server.crt curl -s --cacert $C -o /dev/null -w 'no login -> HTTP %{http_code}\n' https://127.0.0.1/restic/ curl -s --cacert $C -K <(printf 'user = "restic:not-the-password"\n') -o /dev/null -w 'wrong password -> HTTP %{http_code}\n' https://127.0.0.1/restic/ curl -s --cacert $C -o /dev/null -w 'health check -> HTTP %{http_code}\n' https://127.0.0.1/healthz curl -s --cacert $C -H 'Host: evil.example' -o /dev/null -w 'unknown host -> %{http_code}\n' https://127.0.0.1/healthz || true curl -s -o /dev/null -w 'plain http :80 -> %{http_code}\n' --connect-timeout 3 http://127.0.0.1/ || trueno login -> HTTP 401 wrong password -> HTTP 401 health check -> HTTP 200 unknown host -> 000 plain http :80 -> 000000 means no answer: nginx closes the connection for a host name it does not serve, and nothing listens on port 80.
With the login, the user's own repository answers and another user's path is refused. The password is passed to curl on standard input, so it never appears in the process list:
sudo bash -c ' P=$(grep "^REST_SERVER_PASSWORD=" /root/rest-server-info.txt | cut -d= -f2-) auth() { printf "user = \"restic:%s\"\n" "$P"; } C=/etc/nginx/tls/rest-server.crt auth | curl -K - -s --cacert $C -o /dev/null -w "own repository -> HTTP %{http_code}\n" https://127.0.0.1/restic/demo/config auth | curl -K - -s --cacert $C -o /dev/null -w "another user s path -> HTTP %{http_code}\n" https://127.0.0.1/alice/demo/config 'own repository -> HTTP 200 another user s path -> HTTP 401Rest Server runs as the unprivileged restic user with authentication and private repositories enabled. Rest Server, nginx and the first-boot service cannot reach the instance metadata service:
ps -o user=,args= -C rest-server systemctl show rest-server nginx rest-server-firstboot -p Id -p IPAddressDenyrestic /opt/rest-server/rest-server --listen 127.0.0.1:8000 --path /var/lib/restic --htpasswd-file /etc/rest-server/.htpasswd --private-repos IPAddressDeny=169.254.169.254/32 168.63.129.16/32 fd00:ec2::254/128 Id=rest-server.service IPAddressDeny=169.254.169.254/32 168.63.129.16/32 fd00:ec2::254/128 Id=nginx.service IPAddressDeny=169.254.169.254/32 168.63.129.16/32 fd00:ec2::254/128 Id=rest-server-firstboot.serviceRest Server keeps only a bcrypt hash of the password. The plain text is in the root-only credentials file, and the TLS key is root-only:
sudo sed -E 's/^(restic:\$2y\$10\$).*/\1<bcrypt-hash>/' /etc/rest-server/.htpasswd
sudo stat -c '%a %U:%G %n' /etc/rest-server/.htpasswd /root/rest-server-info.txt /etc/nginx/tls/rest-server.keyrestic:$2y$10$<bcrypt-hash> 640 root:restic /etc/rest-server/.htpasswd 600 root:root /root/rest-server-info.txt 600 root:root /etc/nginx/tls/rest-server.keyThe certificate was generated on this instance and names its own addresses:
openssl x509 -noout -subject -ext subjectAltName -in /etc/nginx/tls/rest-server.crtsubject=CN = ec2-<public-ip-dashed>.compute-1.amazonaws.com, O = sac 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:localhostRansomware-resistant repositories (append-only mode)
A backup client holds the credentials of its repository, so malware on that machine could delete its backups too. In append-only mode, Rest Server lets clients create and read snapshots but refuses every delete or overwrite. Turn it on:
sudo rest-server-append-only on
sudo rest-server-append-only statusrest-server-append-only: ON ONBackups keep working, but restic forget can no longer remove anything:
sudo bash -c '. /root/restic-demo/env && restic backup -q /etc/nginx/sites-available /usr/share/doc/rest-server'
sudo bash -c '. /root/restic-demo/env && restic forget --keep-last 1' 2>&1 | grep -E '^(remove|Remove|unable)'
sudo bash -c '. /root/restic-demo/env && restic snapshots --compact'remove 1 snapshots: Remove(<snapshot/e25661b183>) failed: unexpected HTTP response (403): 403 Forbidden unable to remove snapshot/e25661b1835a052ec8fcb1a14ae86f1c8fe4bae1da905f4077edf40ed86833e0 from the repository ID Time Host Tags Size ------------------------------------------------------------------- e25661b1 2026-10-19 20:56:59 ip-<private-ip-dashed> 17.174 KiB b7aa2bb6 2026-10-19 20:57:19 ip-<private-ip-dashed> 17.174 KiB ------------------------------------------------------------------- 2 snapshotsThe server refused the delete (403 Forbidden) and both snapshots are still there. restic still exits 0 in this case, so watch for unable to remove in your logs.
To expire old snapshots, switch append-only mode off briefly (sudo rest-server-append-only off), run restic forget ... --prune from a trusted machine, then switch it on again. rest-server-append-only restarts Rest Server, so start the next backup a second or two later.
Turn it off again for the rest of this guide:
sudo rest-server-append-only offrest-server-append-only: OFFChange the password or add users
rest-server-set-password manages the login file. Run it with no arguments to generate a new random password for restic. The password is written to /root/rest-server-info.txt.
To choose the password yourself, pass it on standard input. printf is a shell builtin, so the password never appears in the process list. Start the line with a space so most shells keep it out of your history:
printf '%s\n' '<paste-your-new-password>' | sudo rest-server-set-password --stdinTo give a machine or a colleague their own login, add --user <name> with a password on standard input. That user reaches only rest:https://<host>/<name>/..., so their backups are invisible to everyone else.
Remove a login with --delete <name>. The repositories stay in /var/lib/restic/<name>. Passwords are 12 to 128 printable characters without spaces or :.
Rest Server caches logins, so the helper restarts it to apply the change:
printf '%s\n' '<paste-a-password-for-alice>' | sudo rest-server-set-password --user alice --stdin sudo rest-server-set-password --delete aliceYour own DNS name and certificate
Point a DNS record at the instance, then set the name that Rest Server is served as. This creates a new self-signed certificate for the name and the instance's IP addresses, updates nginx and the URL in the credentials file, and keeps the name across reboots. Afterwards, give your clients the new certificate:
sudo rest-server-set-hostname <your-domain>To use a certificate from your own certificate authority instead, copy it over the generated pair and reload nginx. Running rest-server-set-hostname again replaces it with a self-signed one. Clients that trust your authority no longer need RESTIC_CACERT:
sudo install -m 0644 <your-domain>.crt /etc/nginx/tls/rest-server.crt
sudo install -m 0600 <your-domain>.key /etc/nginx/tls/rest-server.key
sudo nginx -t && sudo systemctl reload nginxThe repository volume and backing up the server
Repositories live on the separate EBS volume, owned by the restic service account and mounted without device files, set-uid programs or execution:
findmnt -no SOURCE,FSTYPE,OPTIONS /var/lib/restic df -h --output=size,used,avail,target /var/lib/restic
sudo stat -c '%a %U:%G %n' /var/lib/restic/dev/nvme1n1 ext4 rw,nosuid,nodev,noexec,relatime Size Used Avail Mounted on 9.8G 1.1M 9.3G /var/lib/restic 750 restic:restic /var/lib/resticTo grow the volume, modify its size in the EC2 console, then run sudo resize2fs on its device (shown by findmnt above).
Protect the backups themselves with EBS snapshots of this volume, taken from the EC2 console, with aws ec2 create-snapshot, or on a schedule with Amazon Data Lifecycle Manager. Alternatively, copy the repositories to a second location with restic copy.
restic encrypts the repository contents, so a snapshot is only useful together with the repository passwords your clients use.
Server components
Component Path
Rest Server release binary /opt/rest-server/rest-server
Licence and upstream README /usr/share/doc/rest-server/
Repositories (EBS volume) /var/lib/restic/<user>/<repository>
Login file (bcrypt) /etc/rest-server/.htpasswd
Service unit /etc/systemd/system/rest-server.service (--private-repos, loopback only)
nginx site /etc/nginx/sites-available/sac-rest-server
Certificate and key /etc/nginx/tls/
First boot script /usr/local/sbin/rest-server-firstboot.sh
Helpers /usr/local/sbin/rest-server-set-password, rest-server-set-hostname, rest-server-append-only
Credentials /root/rest-server-info.txt (mode 0600)
/opt/rest-server/rest-server --version cat /opt/rest-server/VERSION nginx -v 2>&1 ls -1 /usr/share/doc/rest-serverrest-server version rest-server 0.14.0 compiled with go1.24.3 on linux/amd64 Rest Server 0.14.0 (official release, BSD-2-Clause, sha256 4c9c95bc079a0334e81fad379b19dc5c3353c71c2c88d652cafce2081c2b1c66) nginx version: nginx/1.24.0 (Ubuntu) LICENSE README.mdLogs: sudo journalctl -u rest-server for Rest Server, /var/log/nginx/access.log for requests (with the real client address).
To move to a newer Rest Server release, download it from the Rest Server releases page and check it against the release's SHA256SUMS. Then replace /opt/rest-server/rest-server and run sudo systemctl restart rest-server. Repositories in /var/lib/restic are kept.
Security recommendations
- Allow port 22 in the security group from your own networks only, and port 443 only from the machines you back up.
- Give every machine or team its own login (Step 9), so a leaked password exposes one namespace. Rotate a password if it may have been shared.
- Turn on append-only mode (Step 8) for machines that could be compromised, and expire snapshots from a trusted place.
- Store the restic repository passwords somewhere other than the machines you back up. Without them, the backups cannot be read.
- Use your own DNS name and certificate (Step 10), and snapshot the repository volume (Step 11).
- Keep the operating system patched. Unattended security upgrades are on, and
sudo apt-get update && sudo apt-get upgradeapplies everything else.
Support and licensing
Rest Server and restic are both open source under the BSD 2-Clause licence. There is no per-user or per-CPU licence fee. The Rest Server licence is in /usr/share/doc/rest-server/.
SecureAnyCloud is not affiliated with or endorsed by the restic project. The AMI price covers SecureAnyCloud's hardening, first-boot setup and support.
- Email: sales@secureanycloud.com
- Website: https://secureanycloud.com
- Support: 24/7, with a 24 hour response SLA
Deploy on AWS
Launch Rest Server 0.14.0 on Ubuntu 24.04 on AWS with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com