GuiderBörja härSäkerhetskopior med restic

Säkerhetskopiera servern till en annan server med restic

Krypterade säkerhetskopior varje natt från en Debian 13-server till en annan server med restic och dess REST-server, i läget append-only, så att den som tar över servern du säkerhetskopierar inte kan radera eller ändra gamla kopior.

Testad på restic 0.18.0 och rest-server 0.13.0 på Debian 13 (trixie) på två servrar hos Melonslab Uppdaterad 2 oktober 2026

Rekommenderad server för guiden

VC-S Micro · 2 vCPU · 8 GB Minne · 250 GB Lagring

Månadsvis, ingen bindningstid 7 dagars öppet köp

90 kr/mån

Beställ nu
På den här sidan

Det här sätter du upp

En säkerhetskopia på samma server överlever inte att servern går förlorad. De flesta av våra guider slutar med "kopiera säkerhetskopian till en annan maskin": den här guiden visar hur, för vilken Debian 13-server som helst.

restic gör krypterade och deduplicerade säkerhetskopior: varje natt skickar den bara det som har ändrats, och varje säkerhetskopia, en så kallad snapshot, går att återställa för sig. Kopiorna hamnar på en andra server, till exempel en liten andra server hos Melonslab, som kör restics REST-server. Den körs i läget append-only: servern du säkerhetskopierar kan lägga till nya snapshots, men inte radera eller ändra gamla. Om någon tar över den servern, eller om ett utpressningsvirus krypterar den, finns förra veckans kopior kvar. Gamla snapshots tar backupservern bort på egen hand, enligt ett schema du bestämmer.

restic och dess REST-server är projekt med öppen källkod, under licensen BSD 2-Clause, som drivs av frivilliga på GitHub. restic startades 2014 av Alexander Neumann, och dess huvudutvecklare anger Tyskland som sin ort. restic skickar ingen telemetri: under en säkerhetskopiering anslöt den bara till det repo den fick. Vi använde Debians paket, som uppdateras med resten av systemet.

Varje steg nedan har körts på två nyinstallerade VC-P Alloy (2 vCPU, 8 GB) hos Melonslab med Debian 13:

  • Den första servern säkerhetskopierade /etc, /root, /home, /srv, /usr/local och /var/lib varje natt till den andra, över TLS med ett fastlåst certifikat, plus dumpar av PostgreSQL, av MariaDB och av en PostgreSQL i en Podman-container utan root.
  • Att radera en snapshot från den första servern misslyckades med 403 Forbidden, och alla snapshots fanns kvar.
  • Backupservern behöll 7 dagliga, 4 veckovisa och 6 månatliga snapshots och tog bort resten, av 76 som täckte 210 dagar.
  • En enskild fil, en hel hemkatalog och alla tre databaserna återställdes och stämde med originalen. restic check hittade inga fel.
  • En misslyckad säkerhetskopiering visade en varning vid nästa inloggning, och båda servrarna körde sina timers igen efter en omstart.

Den första säkerhetskopian av en liten server tog 3 sekunder, och dess 65 MB tog 14 MB på backupservern efter deduplicering och komprimering. restic använde omkring 100 MB minne medan den körde, och REST-servern omkring 20 MB.

Innan du börjar

Du behöver:

  • servern du vill säkerhetskopiera, med Debian 13, uppsatt som i säkerhetsguiden;
  • en andra server med Debian 13 för säkerhetskopiorna, uppsatt på samma sätt, med tillräckligt med disk för dem. Det ska vara en annan maskin än den första: en andra server hos Melonslab fungerar.

Exemplen använder 203.0.113.10 och 2001:db8::10 för servern du säkerhetskopierar, som guiden kallar web1, och 203.0.113.20 och 2001:db8::20 för backupservern. Byt ut dem genomgående. Kommandona körs som root, och varje steg anger på vilken server.

1. Sätt upp backupservern

Installera REST-servern, restic (för att ta bort gamla snapshots i steg 8) och htpasswd på backupservern:

apt update
apt install -y restic-rest-server restic apache2-utils

Paketet skapar en egen användare, restic-rest-server, utan inloggning, och servern körs som den användaren. Skapa katalogen för säkerhetskopiorna:

mkdir -p /srv/restic
chown restic-rest-server: /srv/restic
chmod 700 /srv/restic

Säkerhetskopiorna går över internet, så REST-servern använder TLS. Den behöver ett certifikat, och ett certifikat från Let's Encrypt kräver ett DNS-namn. Gör i stället ett eget för serverns adresser, giltigt i 10 år. Servern du säkerhetskopierar litar på just det här certifikatet och inget annat, vilket är striktare än ett publikt certifikat:

openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 -nodes -days 3650 \
  -subj "/CN=backup" -addext "subjectAltName=IP:203.0.113.20,IP:2001:db8::20" \
  -keyout /etc/restic-rest-server/tls.key -out /etc/restic-rest-server/tls.crt
chgrp restic-rest-server /etc/restic-rest-server/tls.key
chmod 640 /etc/restic-rest-server/tls.key

Varje server du säkerhetskopierar får en egen användare och ett eget lösenord på backupservern. Skapa ett långt lösenord, och lägg till användaren web1 med det:

openssl rand -base64 32
htpasswd -B /etc/restic-rest-server/users.htpasswd web1

htpasswd frågar efter lösenordet två gånger. Ha det till hands till steg 3. Ersätt nu innehållet i /etc/default/restic-rest-server med:

# Listen on port 8000, IPv4 and IPv6.
LISTEN = :8000

# One repository per server you back up, under this folder.
BACKUP_DIR = /srv/restic

# --append-only: clients can add backups, but never delete or change them.
# --private-repos: each user can only reach the repository with its own name.
ARGS = "\
  --htpasswd-file /etc/restic-rest-server/users.htpasswd \
  --append-only \
  --private-repos \
  --tls \
  --tls-cert /etc/restic-rest-server/tls.crt \
  --tls-key /etc/restic-rest-server/tls.key \
"

Med --private-repos kan användaren web1 bara använda /srv/restic/web1, så en andra server du säkerhetskopierar kan varken läsa eller fylla den förstas repo. Starta servern:

systemctl enable --now restic-rest-server
journalctl -u restic-rest-server -o cat

Loggen slutar med start server on [::]:8000 och TLS enabled. Öppna porten bara för servrarna du säkerhetskopierar, med båda deras adresser. Vår backupserver hade inte ufw, så de här två reglerna är inte testade:

ufw allow from 203.0.113.10 to any port 8000 proto tcp
ufw allow from 2001:db8::10 to any port 8000 proto tcp

Skriv till sist ut certifikatet, och kopiera det med raderna BEGIN och END:

cat /etc/restic-rest-server/tls.crt

2. Låt servrarna nå varandra

Om båda servrarna finns hos Melonslab, i samma nätverk, kan de inte nå varandra förrän trafiken går via gatewayen: sätt först upp det på båda servrarna, som i guiden om att koppla ihop två servrar. En backupserver någon annanstans behöver ingen ändring. Kontrollera i båda fallen från den här servern att porten svarar, på varje adress du tänker använda:

timeout 5 bash -c '</dev/tcp/2001:db8::20/8000' && echo open

3. Installera restic och skapa repot

Resten av guiden körs på servern du säkerhetskopierar, om inte steget anger något annat. Debian 13 har restic 0.18.0, som har allt guiden använder:

apt install -y restic
mkdir -p /etc/restic
chmod 700 /etc/restic

Klistra in certifikatet från steg 1 i /etc/restic/server.crt. Skapa sedan repots lösenord. restic krypterar allt med det innan det lämnar servern, så backupservern ser aldrig dina filer:

openssl rand -base64 32 > /etc/restic/password
chmod 600 /etc/restic/password
cat /etc/restic/password

Spara lösenordet någon annanstans än på servern också, i din lösenordshanterare. Utan det kan ingen återställa säkerhetskopiorna, varken du eller vi. Går servern förlorad, gör kopian i /etc/restic det också.

Skapa /etc/restic/env, med användarens lösenord från steg 1 i stället för REST_PASSWORD, och lås filen:

RESTIC_REPOSITORY=rest:https://[2001:db8::20]:8000/web1/
RESTIC_REST_USERNAME=web1
RESTIC_REST_PASSWORD=REST_PASSWORD
RESTIC_PASSWORD_FILE=/etc/restic/password
RESTIC_CACERT=/etc/restic/server.crt
RESTIC_CACHE_DIR=/var/cache/restic
chmod 600 /etc/restic/env

Den sista delen av adressen, web1/, måste vara användarens namn. IPv4 fungerar också, som rest:https://203.0.113.20:8000/web1/: certifikatet gäller för båda adresserna. Läs in inställningarna i ditt skal, och skapa repot:

set -a; . /etc/restic/env; set +a
restic init
created restic repository 4e462201c7 at rest:https://[2001:db8::20]:8000/web1/

Kör raden med set -a igen i varje nytt skal innan du använder restic för hand.

4. Välj vad som ska säkerhetskopieras

På en server som är uppsatt som i våra guider innehåller de här katalogerna allt du skulle sakna:

  • /etc: systemets inställningar, ufw:s regler och serverns SSH-nycklar.
  • /root och /home: med Podman utan root, som i Podman-guiden, har varje apps användare sina inställningar, sina Quadlet-filer, sina volymer under ~/.local/share/containers/storage/volumes och sin katalog ~/backup i sin hemkatalog.
  • /srv, /usr/local och /var/lib, där andra program har sina data, och /var/spool/cron, för crontabbar.

En del är bättre att lämna utanför. Skapa /etc/restic/excludes:

# Package lists, downloaded again by apt update.
/var/lib/apt/lists
# Database files change while they are copied: back up a dump instead.
/var/lib/postgresql
/var/lib/mysql
# Container image layers, downloaded again by podman pull.
/home/*/.local/share/containers/storage/overlay
# Caches.
/root/.cache
/home/*/.cache

Avbildernas lager är den största delen av en Podman-användares lagring, 293 MB för en PostgreSQL-avbild på vår server, och kommer tillbaka med podman pull. Volymerna följer med i säkerhetskopian.

Filerna för en databas som körs är ingen säker säkerhetskopia: de ändras medan restic läser dem, och kopian kanske inte går att använda. Därför lämnas databaskatalogerna utanför, och steg 6 säkerhetskopierar en dump i stället. Volymer med en apps databas kopieras också, men återställ appen från dess dump, eller från filen i ~/backup som appens egen guide skapar.

5. Kör den varje natt

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

[Unit]
Description=Back up this server with restic
Wants=network-online.target
After=network-online.target
OnFailure=restic-backup-failed.service

[Service]
Type=oneshot
EnvironmentFile=/etc/restic/env
Nice=10
IOSchedulingClass=idle
# Files and folders.
ExecStart=/usr/bin/restic backup --tag files --exclude-file /etc/restic/excludes --exclude-caches /etc /root /home /srv /usr/local /var/lib /var/spool/cron
# After a backup that worked, remove the warning from one that failed.
ExecStartPost=/usr/bin/rm -f /etc/motd.d/restic-backup

--exclude-caches lämnar också utanför varje katalog som markerar sig själv som cache. Om en säkerhetskopiering misslyckas startar OnFailure en andra tjänst, som lämnar en varning som du ser vid nästa inloggning. Skapa /etc/systemd/system/restic-backup-failed.service:

[Unit]
Description=Warn at login that the restic backup failed

[Service]
Type=oneshot
ExecStart=/bin/sh -c "mkdir -p /etc/motd.d && echo \"The backup failed on $$(date). See: journalctl -u restic-backup\" > /etc/motd.d/restic-backup"

Och timern, /etc/systemd/system/restic-backup.timer:

[Unit]
Description=Back up this server every night

[Timer]
OnCalendar=*-*-* 02:00
RandomizedDelaySec=1h
Persistent=true

[Install]
WantedBy=timers.target

Säkerhetskopieringen startar vid en slumpmässig tid mellan 02.00 och 03.00, så att flera servrar inte startar samtidigt, och Persistent=true kör en missad säkerhetskopiering så snart servern är igång igen. Kör den första säkerhetskopieringen nu, och slå på timern:

systemctl daemon-reload
systemctl start restic-backup
journalctl -u restic-backup -o cat
systemctl enable --now restic-backup.timer
Files:        4008 new,     0 changed,     0 unmodified
Dirs:          356 new,     0 changed,     0 unmodified
Added to the repository: 47.438 MiB (9.488 MiB stored)
processed 4008 files, 65.225 MiB in 0:03
snapshot afedc961 saved

systemctl list-timers restic-backup.timer visar nästa körning. Så här ser du hur säkerhetskopiorna går:

systemctl --failed
journalctl -u restic-backup --since yesterday
restic snapshots

6. Säkerhetskopiera databaser

restic kan köra ett kommando och spara det som kommandot skriver ut som en fil i en snapshot. Misslyckas kommandot, misslyckas också säkerhetskopieringen, och ingen halvfärdig dump sparas. Lägg till en rad ExecStart per databas i tjänsten från steg 5, före raden ExecStartPost, och kör systemctl daemon-reload.

För PostgreSQL från Debians paket:

ExecStart=/usr/bin/restic backup --tag db --stdin-filename postgresql.sql --stdin-from-command -- runuser -u postgres -- pg_dumpall

För MariaDB från Debians paket:

ExecStart=/usr/bin/restic backup --tag db --stdin-filename mariadb.sql --stdin-from-command -- mariadb-dump --all-databases --single-transaction --routines --events

För PostgreSQL i en Podman-container utan root, som i våra appguider, körs dumpen som appens användare. Här heter användaren notes, containern notes-db, och både databasen och databasanvändaren notes:

ExecStart=/usr/bin/restic backup --tag db --stdin-filename notes-db.sql --stdin-from-command -- systemd-run -M notes@ --user -P -q podman exec notes-db pg_dump -U notes notes

systemd-run -M notes@ --user kör kommandot i användarens session, där dess containrar finns. runuser och sudo -u räcker inte: då misslyckas Podman med OCI permission denied.

Varje dump blir en egen snapshot, taggad db. På vår server, med containern stoppad, misslyckades säkerhetskopieringen, systemctl --failed listade restic-backup.service, och nästa inloggning visade:

The backup failed on Fri Oct  2 06:47:39 PM CEST 2026. See: journalctl -u restic-backup

7. Kontrollera att servern inte kan radera sina säkerhetskopior

Försök ta bort alla snapshots utom den senaste:

restic forget --keep-last 1 --prune
Remove(<snapshot/afedc96152>) failed: unexpected HTTP response (403): 403 Forbidden
unable to remove snapshot/afedc961524b... from the repository
...
3 snapshots have been removed, running prune
...
Fatal: unexpected HTTP response (403): 403 Forbidden

Trots raden i mitten togs ingenting bort: restic snapshots listar alla. REST-servern vägrar radera eller skriva över något, så varken du eller en inkräktare kan ta bort dem härifrån. Att ta bort en enskild snapshot med restic forget och dess id misslyckas också med 403 Forbidden, men där avslutar restic 0.18.0 med status 0, så lita inte på dess slutstatus. Det misslyckade försöket lämnar några dubbla poster i indexet, som restic check kallar ofarliga. Rensningen i steg 8 städar bort dem.

8. Ta bort gamla säkerhetskopior på backupservern

Snapshots tas bort av backupservern, som läser repot direkt från disken. Den behöver repots lösenord för det, så servern som har säkerhetskopiorna kan också läsa dem. Båda servrarna är dina, och servern du säkerhetskopierar kan fortfarande inte radera något.

Spara lösenordet från steg 3 på backupservern, där bara root kan läsa det:

mkdir -p /etc/restic
chmod 700 /etc/restic
nano /etc/restic/web1.password
chmod 600 /etc/restic/web1.password

Skapa /etc/systemd/system/restic-prune@.service, en tjänst för alla servrar du säkerhetskopierar:

[Unit]
Description=Remove old restic snapshots of %i

[Service]
Type=oneshot
User=restic-rest-server
Group=restic-rest-server
UMask=027
# The password is readable only by root. systemd hands this service a copy.
LoadCredential=password:/etc/restic/%i.password
CacheDirectory=restic-prune
Environment=RESTIC_REPOSITORY=/srv/restic/%i RESTIC_PASSWORD_FILE=%d/password RESTIC_CACHE_DIR=/var/cache/restic-prune
ExecStart=/usr/bin/restic forget --prune --retry-lock 2h --keep-daily 7 --keep-weekly 4 --keep-monthly 6

Den körs som REST-serverns användare, så att filerna den skriver får rätt ägare, och LoadCredential ger den lösenordet utan att den användaren kan läsa /etc/restic. Policyn behåller den senaste snapshoten för var och en av de senaste 7 dagarna, 4 veckorna och 6 månaderna, för filerna och för varje databas för sig. Ändra siffrorna efter behov. --retry-lock 2h väntar om en säkerhetskopiering pågår.

Och /etc/systemd/system/restic-prune@.timer:

[Unit]
Description=Remove old restic snapshots of %i every day

[Timer]
OnCalendar=*-*-* 12:00
RandomizedDelaySec=1h
Persistent=true

[Install]
WantedBy=timers.target

Slå på den för web1, och kör den en gång:

systemctl daemon-reload
systemctl enable --now restic-prune@web1.timer
systemctl start restic-prune@web1
journalctl -u restic-prune@web1 -o cat

Vårt testrepo hade 76 snapshots av filerna, en var tredje dag under 210 dagar, gjorda med restics flagga --time. Tjänsten behöll 12 av dem, och tog bort de data som bara de andra använde:

Applying Policy: keep 7 daily, 4 weekly, 6 monthly snapshots
...
60 snapshots have been removed, running prune
...
done

För ytterligare en server du säkerhetskopierar: lägg till en användare för den i steg 1, dess lösenord i /etc/restic/<namn>.password, och slå på restic-prune@<namn>.timer.

9. Återställ

Kör de här kommandona på servern du säkerhetskopierar, med inställningarna inlästa som i steg 3. latest betyder den senaste snapshoten, och eftersom varje databas har egna snapshots väljer du filerna med --tag files. Så här ser du vad som finns:

restic snapshots
restic ls latest --tag files /etc/ssh

Så här återställer du en enskild fil till en ny katalog, så att inget skrivs över:

restic restore latest --tag files --target /root/restore --include /etc/ssh/sshd_config

Den hamnar i /root/restore/etc/ssh/sshd_config. Så här återställer du en hel katalog till en ny plats, till exempel en appanvändares hemkatalog:

restic restore latest:/home/notes --tag files --target /root/notes-restore

Filerna behåller sina ägare och rättigheter. En Podman-volyms filer finns i .local/share/containers/storage/volumes/<volym>/_data. Kopiera tillbaka det du behöver, och ta bort den återställda katalogen efteråt.

En databas återställer du genom att strömma tillbaka dess dump. PostgreSQL från Debians paket:

restic dump latest --tag db --path /postgresql.sql /postgresql.sql | runuser -u postgres -- psql -X

pg_dumpall återställer alla databaser och roller. På en server som redan har några av dem skriver den fel som role "postgres" already exists för dem, och återställer resten: ta först bort en skadad databas med dropdb. MariaDB:

restic dump latest --tag db --path /mariadb.sql /mariadb.sql | mariadb

PostgreSQL i containern utan root:

restic dump latest --tag db --path /notes-db.sql /notes-db.sql | systemd-run -M notes@ --user -P -q podman exec -i notes-db psql -X -U notes notes

För att återställa till en ny server när den gamla har gått förlorad: installera restic där, skapa /etc/restic från steg 3 igen med lösenordet från din lösenordshanterare, och återställ som ovan.

10. Testa dina återställningar

En säkerhetskopia som du aldrig har återställt är en förhoppning, inte en säkerhetskopia. Återställ en fil och en databasdump på servern en gång i månaden, och kontrollera att de är vad du väntar dig. Låt också restic kontrollera repot, och läsa en tiondel av de lagrade data varje gång:

restic check --read-data-subset 10%
read 10.0% of data packs
no errors were found

restic check utan flaggan kontrollerar bara strukturen, och läser inga data.

Felsökning

connect: no route to host eller TLS handshake timeout mellan två servrar hos Melonslab. Trafiken mellan dem går inte via gatewayen. Följ guiden om att koppla ihop två servrar på båda servrarna.

x509: certificate signed by unknown authority. restic använder inte ditt certifikat: RESTIC_CACERT saknas i /etc/restic/env, eller så är inställningarna inte inlästa i det här skalet.

unexpected HTTP response (401): 401 Unauthorized. Användarnamnet eller dess lösenord är fel, eller så slutar adressen inte med användarens eget namn: med --private-repos kan web1 bara använda /web1/.

Fatal: wrong password or no key found. /etc/restic/password innehåller inte repots lösenord från steg 3. Kanske klistrade du in användarens lösenord från steg 1 där i stället.

Kör det på din egen server

VC-S Micro

90 kr/mån

vCPU
2
Minne
8 GB
Lagring
250 GB
Trafik
10 TB
Standard
HDD · RAID 10
  • Full root-åtkomst
  • Nativt /64 IPv6
  • RAID-skyddad lagring
  • Malmö, Sverige
  • Månadsvis, ingen bindningstid
  • 7 dagars öppet köp
Alla guider