Skip to Content
Praktisk guide

Backup av en Docker-installation

Så sparar ni databasen, filerna och inställningarna i en Docker-installation på en annan plats, schemalägger kopian och provar att den faktiskt går att läsa tillbaka.

Vad ni får ut av det här

En disk går sönder. En uppdatering slutar i felmeddelanden. Någon raderar fel mapp. Med en fungerande säkerhetskopia är det en tråkig timme; utan den kan det vara företagets kundregister, bokföringsunderlag eller dokumentarkiv som är borta.

Guiden gäller alla program ni kör med Docker Compose, till exempel Nextcloud, Vaultwarden, n8n eller Paperless-ngx. När ni är klara har ni ett skript som tar kopian, en kopia som ligger på en annan maskin eller i molnet, och ett schema som kör skriptet utan att ni behöver komma ihåg det. Och ni har provat att läsa tillbaka den, vilket är det enda sättet att veta att den duger.

Det här behöver ni innan ni börjar

  • En installation som startas med docker compose up -d från en mapp med en compose.yaml. Har ni inte det än, börja med guiden Installera Docker.
  • Tillgång till en terminal på datorn eller servern: Terminal på Linux Mint eller en VPS, PowerShell på Windows.
  • En plats utanför datorn där kopian kan ligga: en annan dator, en nätverksdisk eller en molnlagring.
  • En halvtimme då programmet får vara avstängt en kort stund, första gången.

Vad som faktiskt ska sparas

Allt som programmet skriver hamnar på ett av två ställen, och det är de två som ska med i kopian.

Volymer är lagringsutrymmen som Docker sköter själv. I compose.yaml känner ni igen dem på att raden under volumes: börjar med ett namn, till exempel db_data:/var/lib/mysql, och att samma namn står längst ner i filen. Docker sätter mappens namn framför, så volymen db_data i mappen nextcloud heter nextcloud_db_data på datorn. Kör docker volume ls för att se de riktiga namnen.

Bind-mounts är vanliga mappar på datorn som containern får skriva i. De börjar med en sökväg, till exempel ./data:/data eller /home/anna/paperless/media:/usr/src/paperless/media. De kopierar ni som vilken mapp som helst.

Till det kommer två små filer som är lätta att glömma: compose.yaml och, om den finns, .env. I .env ligger ofta lösenorden till databasen. Utan dem kan ni ha en perfekt kopia av datan och ändå inte komma in i den.

Stäng av, kopiera, starta igen

Det enklaste sättet att få en kopia som hänger ihop är att stänga av programmet medan ni kopierar. En databas som skriver samtidigt som ni kopierar dess filer kan ge en kopia som är trasig på ett sätt ni upptäcker först när ni behöver den. Det gäller Postgres, MariaDB och SQLite lika mycket.

För ett litet företag är en minuts driftstopp klockan halv tre på natten sällan ett problem. Så gör så här:

cd ~/nextcloud
mkdir -p backup
docker compose stop
docker run --rm -v nextcloud_nextcloud_data:/data:ro -v "${PWD}/backup:/backup" alpine tar czf /backup/nextcloud_data.tar.gz -C /data .
docker run --rm -v nextcloud_db_data:/data:ro -v "${PWD}/backup:/backup" alpine tar czf /backup/db_data.tar.gz -C /data .
docker compose start

Kommandot i mitten startar en liten tillfällig container (Alpine Linux), kopplar in volymen skrivskyddat och packar ihop innehållet till en fil i mappen backup. --rm gör att containern försvinner när den är klar. Byt volymnamnen mot dem ni såg i docker volume ls, en rad per volym.

Samma kommandon fungerar i PowerShell på Windows, eftersom ${PWD} betyder aktuell mapp i båda. Byt bara cd ~/nextcloud mot sökvägen till er mapp, till exempel cd $HOME\Documents\nextcloud.

En databasdump som extra livlina

Volymkopian ovan är en kopia av databasens filer, och den går bara att läsa tillbaka med samma huvudversion av databasen. En dump är något annat: en fil som beskriver hela databasen i en form som även en nyare version kan läsa in. Den är bra att ha när en uppdatering har gått snett, eller när ni flyttar till en annan server.

Kommandona nedan kör dumpen inne i databascontainern och hämtar sedan ut filen. De läser användarnamn och lösenord från containerns egna miljövariabler, så att lösenordet inte hamnar i terminalens historik. Kontrollera i er compose.yaml att variablerna heter som i exemplen; det gör de i de officiella avbilderna, men en del guider använder andra namn.

Postgres:

docker compose exec db sh -c 'pg_dump -U "$POSTGRES_USER" -Fc "$POSTGRES_DB" > /tmp/db.dump'
docker compose cp db:/tmp/db.dump ./backup/db.dump

MariaDB eller MySQL:

docker compose exec db sh -c 'mariadb-dump --single-transaction -u"$MARIADB_USER" -p"$MARIADB_PASSWORD" "$MARIADB_DATABASE" > /tmp/db.sql'
docker compose cp db:/tmp/db.sql ./backup/db.sql

På äldre avbilder heter programmet mysqldump och variablerna börjar med MYSQL_ i stället för MARIADB_. --single-transaction gör att dumpen blir en sammanhängande ögonblicksbild utan att databasen behöver stängas av.

SQLite: här finns ingen separat databasserver, bara en fil i en volym. Den följer med när ni packar volymen med programmet avstängt, som i förra avsnittet. Det räcker.

db i kommandona är tjänstens namn i compose.yaml. Heter er databastjänst något annat, till exempel postgres eller mariadb, byter ni ut det.

Varför går omvägen via /tmp i containern och docker compose cp? Därför att den fungerar likadant på Windows. PowerShell hanterar inte binärdata när man skickar utdata till en fil med >, och en Postgres-dump i formatet -Fc är binär. Kör ni bara Linux kan ni skicka dumpen direkt till en fil med docker compose exec -T, men exemplen ovan fungerar överallt.

Var kopian ska ligga

En kopia på samma disk som originalet skyddar mot att ni raderar fel fil. Mer än så är den inte värd. Går disken sönder, blir datorn stulen eller servern hackad försvinner båda.

Tumregeln som brukar användas: tre exemplar av datan, på två olika sorters lagring, varav ett på en annan plats. För ett litet företag kan det se ut så här: originalet på servern, en kopia på en nätverksdisk på kontoret och en kopia i en molnlagring.

Till molnet är rclone det verktyg som går att använda mot nästan alla lagringstjänster, från Backblaze och Hetzner Storage Box till Google Drive och OneDrive. Ni ställer in anslutningen en gång med rclone config, och sedan kopierar ni med:

rclone copy ./backup offsite:nextcloud-backup

offsite är namnet ni gav anslutningen. rclone copy raderar ingenting på mottagarsidan, vilket är precis vad ni vill när det gäller säkerhetskopior.

Kopian innehåller allt som finns i systemet: kunduppgifter, lösenordsvalv, avtal. Den ska därför vara krypterad innan den lämnar huset. rclone har en inbyggd typ av anslutning som heter crypt och som krypterar filerna innan de laddas upp. Välj den när ni kör rclone config, och spara krypteringslösenordet på samma ställe som företagets övriga lösenord. Tappar ni bort det går kopian inte att läsa, inte ens för er.

I rclone config betyder det två anslutningar: först en vanlig mot lagringstjänsten, sedan en av typen crypt som pekar på den första. Det är den andra ni kopierar till.

Personuppgifter i en säkerhetskopia omfattas av GDPR på samma sätt som originalet. Lagrar ni kopian hos en molntjänst behöver ni veta var den tjänsten har sina servrar och ha ett personuppgiftsbiträdesavtal med den.

Ett skript som gör allt

När stegen fungerar för hand lägger ni dem i en fil så att de kan köras automatiskt. Exemplet gäller en Nextcloud-installation med MariaDB på Linux; byt namn och sökvägar mot era egna.

Spara som /home/anna/nextcloud/backup.sh:

#!/bin/bash
set -e
cd /home/anna/nextcloud
DATUM=$(date +%F)
mkdir -p backup

docker compose exec -T db sh -c 'mariadb-dump --single-transaction -u"$MARIADB_USER" -p"$MARIADB_PASSWORD" "$MARIADB_DATABASE"' > "backup/db-$DATUM.sql"

docker compose stop
trap 'docker compose start' EXIT
docker run --rm -v nextcloud_nextcloud_data:/data:ro -v "$PWD/backup:/backup" alpine tar czf "/backup/nextcloud_data-$DATUM.tar.gz" -C /data .
docker run --rm -v nextcloud_db_data:/data:ro -v "$PWD/backup:/backup" alpine tar czf "/backup/db_data-$DATUM.tar.gz" -C /data .

cp compose.yaml backup/
[ -f .env ] && cp .env backup/

find backup -name '*.gz' -mtime +14 -delete
find backup -name '*.sql' -mtime +14 -delete

rclone copy backup offsite:nextcloud-backup

Gör filen körbar med chmod +x backup.sh och provkör den med ./backup.sh.

Två rader förtjänar en förklaring. trap 'docker compose start' EXIT ser till att programmet startas igen även om något steg däremellan misslyckas; utan den kan ett fel mitt i natten lämna systemet avstängt tills någon märker det. Raderna med find tar bort lokala kopior som är äldre än fjorton dagar, så att disken inte fylls. Kopiorna i molnet ligger kvar tills ni själva rensar där.

Schemalägg kopian

Linux Mint och VPS: cron

Öppna schemat för er användare:

crontab -e

Lägg till en rad längst ner:

30 2 * * * /home/anna/nextcloud/backup.sh >> /home/anna/nextcloud/backup.log 2>&1

Det betyder: klockan 02.30 varje dag, och allt skriptet skriver hamnar i backup.log. Titta i loggen dagen efter. Står det inget felmeddelande där och det finns nya filer i backup fungerar det.

Er användare behöver vara med i gruppen docker för att skriptet ska kunna köra Docker utan sudo. Det sköttes i guiden Installera Docker.

Windows: Schemaläggaren

På Windows skriver ni samma steg som ett PowerShell-skript, till exempel C:\Users\anna\Documents\nextcloud\backup.ps1:

Set-Location "$HOME\Documents\nextcloud"
$datum = Get-Date -Format "yyyy-MM-dd"
New-Item -ItemType Directory -Force -Path backup | Out-Null

docker compose exec db sh -c 'mariadb-dump --single-transaction -u"$MARIADB_USER" -p"$MARIADB_PASSWORD" "$MARIADB_DATABASE" > /tmp/db.sql'
docker compose cp db:/tmp/db.sql "backup/db-$datum.sql"

docker compose stop
try {
    docker run --rm -v nextcloud_nextcloud_data:/data:ro -v "${PWD}/backup:/backup" alpine tar czf "/backup/nextcloud_data-$datum.tar.gz" -C /data .
    docker run --rm -v nextcloud_db_data:/data:ro -v "${PWD}/backup:/backup" alpine tar czf "/backup/db_data-$datum.tar.gz" -C /data .
}
finally {
    docker compose start
}

Copy-Item compose.yaml backup\
if (Test-Path .env) { Copy-Item .env backup\ }

Öppna sedan Schemaläggaren från startmenyn och skapa en enkel aktivitet; guiden i programmet leder er genom stegen. Ge aktiviteten ett namn, låt den köras dagligen vid en tid ni väljer, och välj att den ska starta ett program. Som program anger ni powershell.exe och som argument -ExecutionPolicy Bypass -File "C:\Users\anna\Documents\nextcloud\backup.ps1".

Två saker skiljer Windows från en server. Docker Desktop måste vara igång när skriptet körs, och datorn måste vara påslagen. En bärbar dator som står i väskan klockan 02.30 tar ingen kopia. Välj en tid då datorn brukar vara igång. Eller öppna aktivitetens egenskaper efteråt och kryssa i, bland inställningarna, att den ska köras så snart som möjligt om en schemalagd körning missades. Den missade körningen startar då med några minuters fördröjning, som standard tio.

Kopieringen till molnet kan ni lägga till med rclone även på Windows, eller låta skriptet skriva till en mapp som OneDrive eller en nätverksdisk redan synkar.

Prova att läsa tillbaka

En säkerhetskopia som aldrig har lästs tillbaka är en förhoppning. Gör provet första gången ni har ett fungerande skript, och sedan någon gång i halvåret.

Provet görs bäst på en annan dator, så att ni inte råkar skriva över det som fungerar. Kopiera compose.yaml, .env och säkerhetskopiorna dit. Skapa containrar och volymer utan att starta något, så att programmet inte hinner lägga upp en ny tom installation, och packa sedan upp arkiven i volymerna:

docker compose create
docker run --rm -v nextcloud_nextcloud_data:/data -v "${PWD}/backup:/backup" alpine sh -c "cd /data && tar xzf /backup/nextcloud_data-2026-01-15.tar.gz"
docker run --rm -v nextcloud_db_data:/data -v "${PWD}/backup:/backup" alpine sh -c "cd /data && tar xzf /backup/db_data-2026-01-15.tar.gz"
docker compose up -d

Byt datumet mot det i era filer. Mappen måste heta som på originalet, annars får volymerna andra namn.

Vill ni i stället läsa tillbaka en dump, till exempel efter en misslyckad uppdatering, kopierar ni in den och läser in den i en tom databas:

docker compose cp ./backup/db.dump db:/tmp/db.dump
docker compose exec db sh -c 'pg_restore -U "$POSTGRES_USER" -d "$POSTGRES_DB" --clean --if-exists /tmp/db.dump'

För MariaDB:

docker compose cp ./backup/db.sql db:/tmp/db.sql
docker compose exec db sh -c 'mariadb -u"$MARIADB_USER" -p"$MARIADB_PASSWORD" "$MARIADB_DATABASE" < /tmp/db.sql'

Logga sedan in i programmet och kontrollera att det senaste ni lade in finns där. En fil, en post, ett lösenord. Stämmer det har ni en säkerhetskopia.

Så här vet ni att det funkade

  • backup.log eller Schemaläggarens historik visar att skriptet har gått utan fel de senaste dagarna.
  • Mappen backup innehåller filer med dagens datum, och de är inte på noll byte.
  • Samma filer finns på den andra platsen, i molnet eller på nätverksdisken.
  • Programmet är igång igen efter en schemalagd körning, utan att ni har startat det.
  • Ni har läst tillbaka en kopia på en annan dator och sett era egna data i programmet.
  • Krypteringslösenordet och .env finns sparade i företagets lösenordshanterare.

Vanliga fel

"no such volume" eller en tom arkivfil. Volymnamnet stämmer inte. Kör docker volume ls och använd det fulla namnet med mappens namn först. Står det fel namn skapar Docker en ny tom volym och packar ihop den, helt utan felmeddelande.

Dumpen blir några hundra byte stor. Oftast ett felmeddelande som hamnade i filen i stället för på skärmen. Öppna filen i en textredigerare och läs. Vanligast är att variablerna heter något annat i er compose.yaml än i exemplet.

Cron kör inte skriptet, men det fungerar för hand. Cron har en mycket kortare sökväg till program än er terminal. Skriv hela sökvägar i skriptet om det inte hittar docker eller rclone, och kontrollera backup.log.

Programmet är avstängt på morgonen. Skriptet har stoppat men inte startat. Kontrollera att raden med trap (eller finally på Windows) finns med, och läs loggen för att se vilket steg som föll.

Återläsningen ger en databas som inte startar. Ni har läst in volymkopian i en annan huvudversion av databasen än den som skapade den. Använd samma avbildsversion som när kopian togs, eller läs in dumpen i stället.

Disken fylls. Raderna med find saknas eller pekar på fel mapp. Kontrollera med ni -sh backup hur mycket kopiorna tar.

Nästa steg

Med en säkerhetskopia på plats kan ni uppdatera utan att hålla andan. Guiden Uppdatera utan dataförlust går igenom hur, och den förutsätter att ni har gjort det här först.

Kör ni på en VPS som nås från internet är Brandvägg och grundhärdning av en VPS nästa guide att ta. En säkerhetskopia hjälper er tillbaka efter ett intrång; brandväggen gör det mindre sannolikt att det blir ett.