GuiderUtveckling och driftS3-lagring med Garage

Kör din egen S3-lagring med Garage

Garage på Debian 13 i rootless Podman bakom Caddy, din egen S3-kompatibla lagring för säkerhetskopior, appar och statiska webbplatser, med nycklar per app, restic-säkerhetskopior och en webbplats direkt från en bucket.

Testad på Garage 2.4.0 och 2.4.1 på Debian 13 (trixie) på en server 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

S3 är lagringsprotokollet som Amazon tog fram för sitt moln, och många program talar det: verktyg för säkerhetskopiering som restic och rclone, Mastodon, Nextclouds externa lagring, verktyg för statiska webbplatser och många fler. Med Garage får du en egen S3-tjänst på din egen server: du skapar buckets och nycklar och pekar programmen mot den i stället för mot Amazon.

Garage är gjord för att köras på små servrar och för att senare kunna spridas över flera. Här körs den på en server, från sin officiella avbild, i Podman under en egen användare som heter garage, bakom Caddy från Podman-guiden.

Garage utvecklas av Deuxfleurs, en ideell förening i Lille i Frankrike, med bidrag från Europeiska kommissionens program Next Generation Internet, delvis via NLnet. Den är öppen källkod under GNU AGPL v3. Den kontaktar ingen tjänst på internet: den kan skicka spårningsdata till en OpenTelemetry-mottagare, men bara om du sätter upp en, och på vår server öppnade den inga utgående anslutningar.

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

  • AWS CLI och rclone laddade upp, listade, hämtade och raderade filer, även filer på 1 GB i delar och i en enda uppladdning, med samma kontrollsumma efteråt.
  • En nyckel med bara läsrätt kunde hämta men inte ladda upp eller radera. En försignerad länk fungerade för vem som helst tills den gick ut, och slutade fungera när den ändrades.
  • restic säkerhetskopierade /etc till en bucket och återställde den, och dess kontroll hittade inga fel.
  • En liten webbplats visades direkt från en bucket, med en startsida i varje katalog och en egen 404-sida.
  • En uppladdning på 20 MB som tog över fem minuter gick igenom Caddy utan tidsgräns.
  • Efter att Garages data och metadata raderats tog en återställning från säkerhetskopian tillbaka alla buckets, nycklar och filer. Allt startade också igen av sig självt efter en omstart av servern, och Garage uppdaterades från 2.4.0 till 2.4.1.

Garage använde omkring 30 MB minne i vila och 55 MB medan den tog emot en fil på 1 GB.

En server har en kopia av dina filer. Om dess disk går sönder eller servern går förlorad är filerna borta, om du inte har en säkerhetskopia någon annanstans. Steg 11 säkerhetskopierar Garage, och Garages dokumentation beskriver hur du kör ett kluster med tre eller fler servrar, som håller kopior på flera av dem. Det upplägget är inte testat här.

Innan du börjar

Du behöver:

  • en server uppsatt som i Podman-guiden, med Caddy igång;
  • en A-post och en AAAA-post för s3.example.com, och för www.example.com om du vill visa en webbplats från en bucket, som pekar på din server;
  • tillräckligt med disk för det du ska lagra: Garage lägger dina filer i hemkatalogen för användaren garage.

Exemplen använder s3.example.com för S3-tjänsten, www.example.com för webbplatsen, 203.0.113.10 för serverns IPv4-adress och 198.51.100.7 för din egen adress. Byt ut dem genomgående.

1. Skapa användaren

Som root:

useradd -m -s /bin/bash garage
loginctl enable-linger garage
machinectl shell garage@

Allt fram till steg 4 körs som garage.

2. Skriv konfigurationen

Garage har två sorters data i två kataloger:

  • data, själva filerna, delade i block om 1 MB, komprimerade och lagrade en gång även när flera filer har samma innehåll. Det är den som växer.
  • meta, metadatabasen, som håller reda på vilka block som bildar vilken fil i vilken bucket, och på buckets och nycklar. Den är liten: omkring 20 MB på vår server med 1 GB lagrat. Utan den går blocken i data inte att sätta ihop igen.

Skapa båda, och konfigurationen med nya hemligheter:

mkdir -p ~/meta ~/data
umask 077
cat > ~/garage.toml <<EOF
metadata_dir = "/var/lib/garage/meta"
data_dir = "/var/lib/garage/data"
db_engine = "sqlite"
metadata_fsync = true
metadata_auto_snapshot_interval = "6h"

replication_factor = 1

rpc_bind_addr = "127.0.0.1:3901"
rpc_public_addr = "127.0.0.1:3901"
rpc_secret = "$(openssl rand -hex 32)"

[s3_api]
s3_region = "garage"
api_bind_addr = "[::]:3900"

[s3_web]
bind_addr = "[::]:3902"
root_domain = ".web.example.com"
index = "index.html"

[admin]
api_bind_addr = "[::]:3903"
admin_token = "$(openssl rand -base64 32)"
metrics_token = "$(openssl rand -base64 32)"
EOF

umask 077 gör filen läsbar bara för garage, eftersom den innehåller hemligheterna. Det här gör inställningarna:

  • db_engine = "sqlite" med metadata_fsync = true skyddar metadata om servern tappar strömmen. Garages standard, LMDB, är snabbare men kan skadas vid en oren avstängning, och i ett kluster lagar den sig själv från de andra servrarna. En ensam server har inga andra att laga sig från.
  • metadata_auto_snapshot_interval gör en konsekvent kopia av metadatabasen var sjätte timme, i meta/snapshots. Garage behåller de två senaste. Steg 11 använder dem.
  • replication_factor = 1 betyder en kopia av varje block, det enda valet med en server.
  • rpc_* är hur Garage-servrar talar med varandra. Med en server används det bara av kommandot garage, så det lyssnar bara inne i containern.
  • s3_region är regionnamnet som klienterna måste använda. Vilket namn som helst fungerar, så länge klienterna använder samma (steg 6).
  • [s3_web] visar buckets som webbplatser (steg 9). root_domain är obligatorisk: en bucket som heter blog skulle också visas som blog.web.example.com, om du pekar det namnet mot servern. En bucket som har ett fullständigt värdnamn som namn, som www.example.com, visas under det namnet.
  • [admin] är Garages admin-API. Det publiceras bara på 127.0.0.1 och kräver token för allt utom en hälsokontroll.

3. Definiera containern

mkdir -p ~/.config/containers/systemd

Skapa ~/.config/containers/systemd/garage.container:

[Unit]
Description=Garage S3 storage

[Container]
ContainerName=garage
Image=docker.io/dxflrs/garage:v2.4.1
Exec=/garage server --single-node
# Garage waits up to 10 s for open connections when it stops: give it time to finish
StopTimeout=30
Volume=%h/garage.toml:/etc/garage.toml:ro
Volume=%h/meta:/var/lib/garage/meta
Volume=%h/data:/var/lib/garage/data
# Only Caddy, on this server, can reach Garage: the ports are not open to the internet.
PublishPort=127.0.0.1:8108:3900
PublishPort=127.0.0.1:8109:3902
PublishPort=127.0.0.1:3903:3903

[Service]
Restart=always

[Install]
WantedBy=default.target

Avbilden publiceras av Garages utvecklare och innehåller bara programmet garage. Den har ingen tagg som följer nya versioner, så versionen skrivs ut, och steg 12 byter den. --single-node sätter upp servern som ett kluster med en server första gången den startar, med diskens storlek som kapacitet.

Utan StopTimeout dödar Podman Garage efter 10 sekunder, medan den fortfarande väntar på att Caddys öppna anslutningar ska stängas och innan den har hunnit stänga ner ordentligt.

Starta den och gör garage till ett kort kommando för verktyget i containern:

systemctl --user daemon-reload
systemctl --user start garage
echo "alias garage='podman exec -e RUST_LOG=warn garage /garage'" >> ~/.bashrc
source ~/.bashrc
garage status
==== HEALTHY NODES ====
ID                Hostname      Address         Tags       Zone  Capacity  DataAvail         Version
533c080e04902ded  7875444de8c3  127.0.0.1:3901  [default]  dc1   39.2 GiB  35.7 GiB (90.9%)  v2.4.1

RUST_LOG=warn håller verktygets egna loggrader borta från svaren.

4. Sätt Caddy framför

Gå tillbaka till root med exit, byt till machinectl shell caddy@ och lägg till de här blocken sist i ~/Caddyfile:

s3.example.com {
    reverse_proxy 127.0.0.1:8108
}

www.example.com {
    reverse_proxy 127.0.0.1:8109
}

Starta om Caddy med systemctl --user restart caddy. Sedan, från valfri dator:

curl https://s3.example.com/
<?xml version="1.0" encoding="UTF-8"?><Error><Code>AccessDenied</Code><Message>Forbidden: Garage does not support anonymous access yet</Message><Resource>/</Resource><Region>garage</Region></Error>

Det är Garage som svarar genom Caddy: varje anrop till S3-tjänsten kräver en nyckel.

5. Skapa buckets och nycklar

Tillbaka som garage (exit och sedan machinectl shell garage@). En bucket rymmer filer, och en nyckel är ett par av ett ID och en hemlighet som ett program signerar sina anrop med. Nycklar och buckets är fristående: en nyckel kan använda flera buckets, och en bucket kan ha flera nycklar. Ge varje program eller dator en egen nyckel med åtkomst bara till sina egna buckets, så att en nyckel som läcker bara exponerar dem.

Skapa en bucket och en nyckel för din egen dator:

garage bucket create files
garage key create laptop
garage bucket allow --read --write --owner files --key laptop

garage key create skriver ut Key ID, som börjar med GK, och Secret key. Kopiera båda. För att se hemligheten igen senare använder du garage key info laptop --show-secret.

Behörigheterna är:

  • --read: lista och hämta.
  • --write: ladda upp och radera.
  • --owner: ändra bucketens inställningar, som webbplatsinställningen.

En nyckel för ett program som bara ska läsa, som en server som hämtar nya versioner:

garage key create reader
garage bucket allow --read files --key reader

Med den nyckeln misslyckas en uppladdning med AccessDenied ... Forbidden: Operation is not allowed for this key. Som standard kan en nyckel inte heller skapa buckets: det gör du här, med garage bucket create. garage bucket list, garage key list och garage bucket info files visar vad som finns och vem som har åtkomst.

För att begränsa hur mycket en bucket kan rymma:

garage bucket set-quotas --max-size 50GiB files

En uppladdning över gränsen misslyckas då med Bucket size quota is reached. --max-size none tar bort gränsen.

6. Anslut en klient

S3-klienter behöver fyra saker: adressen, regionen, nyckeln och adresseringssättet.

  • Adress: https://s3.example.com.
  • Region: garage, värdet för s3_region från steg 2. En klient som signerar med en annan region, som us-east-1, får AuthorizationHeaderMalformed ... expected: '20261002/garage/s3/aws4_request'.
  • Adresseringssätt: path style, där bucketen är en del av sökvägen, som i https://s3.example.com/files/photo.jpg. Många klienter använder som standard virtual-host style, https://files.s3.example.com/photo.jpg, som kräver ett DNS-namn och ett certifikat för varje bucket. Det här upplägget har inte det, så välj path style överallt där en klient frågar.

Installera klienterna på din egen dator. På Debian och Ubuntu:

sudo apt install awscli rclone

För AWS CLI skapar du ~/.aws/config:

[default]
region = garage
endpoint_url = https://s3.example.com
s3 =
    addressing_style = path

och ~/.aws/credentials, med din nyckel:

[default]
aws_access_key_id = GK061e2abc3bf8c344f8128842
aws_secret_access_key = your-secret-key

För rclone skapar du ~/.config/rclone/rclone.conf:

[garage]
type = s3
provider = Other
access_key_id = GK061e2abc3bf8c344f8128842
secret_access_key = your-secret-key
endpoint = https://s3.example.com
region = garage
force_path_style = true

Båda filerna innehåller din hemlighet: på Linux kör du chmod 600 på dem. Kontrollera att de fungerar:

aws s3 ls
rclone lsd garage:

Båda listar bucketen files.

7. Ladda upp, lista, dela och radera

Med AWS CLI:

aws s3 cp report.pdf s3://files/
aws s3 ls s3://files/
aws s3 cp s3://files/report.pdf copy.pdf
aws s3 sync ./photos s3://files/photos/
aws s3 rm s3://files/report.pdf

Med rclone:

rclone copy report.pdf garage:files/
rclone ls garage:files
rclone sync ./photos garage:files/photos
rclone delete garage:files/photos

Båda klienterna skickar stora filer i delar, flera åt gången. Vår testfil på 1 GB gick upp i 128 delar med AWS CLI och kom tillbaka med samma kontrollsumma, och en enda uppladdning på 1 GB utan delar fungerade också. För att städa bort delar från uppladdningar som aldrig blev klara:

garage bucket cleanup-incomplete-uploads --older-than 1d files

För att ge någon en fil utan att ge dem en nyckel gör du en försignerad länk. Den är signerad med din nyckel och fungerar för vem som helst tills den går ut, här efter en timme:

aws s3 presign s3://files/report.pdf --expires-in 3600

Länken är lång och börjar med https://s3.example.com/files/report.pdf?X-Amz-Algorithm=. Ändrar du ett enda tecken i den får du Forbidden: Invalid signature.

En fil du raderar försvinner ur bucketen direkt. Garage frigör dess block på disken omkring tio minuter senare, och bara när ingen annan fil använder dem.

8. Säkerhetskopiera en server med restic

restic, från restic-guiden, kan lagra sina säkerhetskopior i en bucket. Ge varje server som säkerhetskopieras en egen bucket och nyckel. Som garage:

garage bucket create restic
garage key create restic-server1
garage bucket allow --read --write restic --key restic-server1

På servern du säkerhetskopierar skapar du, som root, /root/.config/restic/garage.env med den nya nyckeln, och gör den läsbar bara för root med chmod 600:

export RESTIC_REPOSITORY=s3:https://s3.example.com/restic
export AWS_ACCESS_KEY_ID=GK5171b99763045598d9b4aa46
export AWS_SECRET_ACCESS_KEY=the-secret-key
export AWS_DEFAULT_REGION=garage
export RESTIC_PASSWORD_FILE=/root/.config/restic/password

Lägg ett långt lösenord i /root/.config/restic/password, också med chmod 600, och spara en kopia någon annanstans: utan det går säkerhetskopian inte att läsa. Sedan:

. /root/.config/restic/garage.env
restic init
restic backup /etc
restic snapshots
restic restore latest --target /tmp/restore
restic check

restic använder path style av sig självt. restic check slutar med no errors were found. Om restic och Garage körs på samma server försvinner säkerhetskopian med servern: säkerhetskopiera till Garage på en annan server, eller kopiera Garage till en annan plats som i steg 11.

9. Visa en statisk webbplats

Garage visar en bucket som webbplats när bucketen har webbplatsens adress som namn och webbplatsåtkomst är påslagen. Som garage:

garage bucket create www.example.com
garage bucket allow --read --write www.example.com --key laptop
garage bucket website --allow --index-document index.html --error-document 404.html www.example.com

Ladda upp webbplatsen från din dator, till exempel utdatamappen från en generator för statiska webbplatser:

aws s3 sync ./public s3://www.example.com/ --delete

--delete tar bort filer ur bucketen som inte längre finns i mappen. Öppna https://www.example.com:

  • / visar index.html, och /docs/ visar docs/index.html.
  • /docs, utan snedstreck, skickas vidare till /docs/.
  • en sida som saknas visar din 404.html, med status 404.
  • filerna får rätt innehållstyp, som text/css för stilmallar, och en ETag, så att webbläsare kan cacha dem.

Bara buckets med webbplatsåtkomst påslagen visas. Dina andra buckets, som files, går inte att nå på det här sättet.

10. Besökarnas adresser och gränser för uppladdning

Caddy skickar varje besökares adress vidare till Garage i headern X-Forwarded-For, och ersätter en sådan header om besökaren själv skickade en. Garage skriver den i sin logg, med Caddys adress efter:

journalctl --user -u garage | grep GET
198.51.100.7 (via [::ffff:203.0.113.10]:41190) (key GK061e2abc3bf8c344f8128842) GET /files/report.pdf

Adressen efter via är serverns egen, eftersom det är därifrån Caddys anslutningar ser ut att komma inne i containern. Besökare över IPv6 visas med sin IPv6-adress. Loggen visar också vilken nyckel som gjorde varje anrop.

Caddy sätter ingen gräns för hur stor en uppladdning får vara, och ingen tidsgräns: en uppladdning på 1 GB gick igenom i ett enda anrop, och det gjorde även en som tog fem minuter och tjugo sekunder. Gränserna som gäller är S3:s och din disks. För att neka uppladdningar över en viss storlek lägger du till request_body i blocket för s3.example.com, till exempel:

s3.example.com {
    request_body {
        max_size 2GB
    }
    reverse_proxy 127.0.0.1:8108
}

En större uppladdning i ett stycke misslyckas då med 413 Content Too Large. Uppladdningar i delar skickar varje del som ett eget anrop, så gränsen gäller storleken på en del, inte på hela filen: en fil på 30 MB i delar om 8 MB gick fortfarande igenom med en gräns på 20 MB.

11. Säkerhetskopiera Garage

Garage på en server har ingen kopia någon annanstans. För att säkerhetskopiera den behöver du konfigurationen, metadata och data, och metadata måste vara en konsekvent kopia: databasen används medan Garage kör, så säkerhetskopiera en ögonblickskopia, inte den aktiva filen. Det här skriptet körs som root, tar en ny ögonblickskopia och säkerhetskopierar allt med restic, till ett repository på en annan server som i restic-guiden. Skapa /root/garage-backup.sh:

#!/bin/sh
set -e
# A fresh, consistent copy of the metadata database
systemd-run --user -M garage@ --wait --pipe -q podman exec -e RUST_LOG=warn garage /garage meta snapshot
# The live database is left out: the snapshot is the copy to restore
restic backup /home/garage/garage.toml /home/garage/.config/containers /home/garage/meta /home/garage/data \
  --exclude "/home/garage/meta/db.sqlite*"
chmod 700 /root/garage-backup.sh

Kör det med restic-inställningarna för det repositoryt inlästa, för hand eller från en timer som i restic-guiden. Det skriver ut Snapshot created och sedan restics sammanfattning. Datakatalogen är lika stor som det du lagrar, så den första säkerhetskopian tar en stund, och senare skickar bara nya block.

För att återställa på en ny eller rensad server: sätt upp den med steg 1 till 4 och återställ sedan säkerhetskopian som root:

systemd-run --user -M garage@ --wait -q systemctl --user stop garage
restic restore latest --target /

Lägg sedan, som garage, den senaste ögonblickskopian i stället för databasen, starta Garage och låt den kontrollera sina tabeller:

cd ~/meta
rm -f db.sqlite db.sqlite-wal db.sqlite-shm
cp "snapshots/$(ls snapshots | tail -1)/db.sqlite" db.sqlite
systemctl --user start garage
garage repair -a --yes tables

Ta bort alla tre db.sqlite-filerna först. Filerna -wal och -shm hör till databasen som Garage skapade när du startade den i steg 3, och om de ligger kvar startar Garage med den tomma databasen i stället för din ögonblickskopia: inga buckets, och No such key för varje nyckel.

Vi testade det här genom att radera meta och data helt, och en gång till på en nyuppsatt Garage: efter återställningen var alla buckets, nycklar och filer tillbaka, filen på 1 GB hade samma kontrollsumma, och restic-repositoryt från steg 8 klarade sin kontroll. Filer som laddats upp efter ögonblickskopian finns inte med i en återställd kopia.

12. Uppdatera Garage

Garage publicerar nya versioner i sitt repo. Läs versionsanteckningarna först: en ny huvudversion, som från 1.x till 2.x, har egna migreringssteg i dokumentationen. För en mindre uppdatering, som garage:

garage meta snapshot
sed -i 's|garage:v2.4.0|garage:v2.4.1|' ~/.config/containers/systemd/garage.container
systemctl --user daemon-reload
systemctl --user restart garage
garage status
podman image prune -af

Skriv versionen du kör och den nya i raden med sed. garage status visar den nya versionen i sista kolumnen, och podman image prune tar bort den gamla avbilden. Omstarten tar omkring tio sekunder, medan Garage stänger Caddys öppna anslutningar.

Felsökning

En klient misslyckas med AuthorizationHeaderMalformed ... unexpected scope. Den signerar med en annan region än garage. Ställ in regionen i klienten som i steg 6. AWS CLI försöker igen med rätt region av sig själv, så den kan fungera medan andra klienter misslyckas.

En klient misslyckas med no such host för ett namn som files.s3.example.com. Den använder virtual-host style. Slå på path style: addressing_style = path för AWS CLI, force_path_style = true för rclone.

Uppladdningar misslyckas med Forbidden: Operation is not allowed for this key. Nyckeln saknar --write för den bucketen. garage bucket info files visar varje nyckels behörigheter, R, W och O.

Garage startar inte, och loggen visar missing field `root_domain`. Avsnittet [s3_web] behöver en root_domain, även om du bara visar webbplatser under deras fullständiga namn.

En webbplats visar 404 Not Found för varje sida. Bucketens namn stämmer inte exakt med adressen, eller så är webbplatsåtkomsten avslagen: garage bucket info visar Website access: true när den är påslagen.

Det tar tio sekunder att stoppa Garage, och loggen visar resorting to SIGKILL. Raden StopTimeout=30 saknas i containerfilen.

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