Development · 6 min čitanja
Kako smo složili backup baza tako da ga može održavati AI agent
Backup za self-hosted Supabase i Postgres podešavamo u Git repozitoriju, a ne u dashboardu, pa AI agent ima sve što mu treba da ga mijenja. Četiri mjeseca nakon što smo zadnji put dirali kod, novu bazu dodali smo u backup za otprilike pet minuta.
Ranije ovog mjeseca dodali smo novu Postgres bazu u backup. Trebalo je otprilike pet minuta, a većinu tog vremena AI agent je čitao repozitorij. Kod nismo otvorili od svibnja.
Činilo nam se da to vrijedi opisati, dijelom da pokažemo kako smo to složili, a dijelom zbog jedne odluke koju smo donijeli usput: gotovo sve postavke su u običnim tekstualnim datotekama u Gitu, a ne u dashboardu koji smo također napravili, jer agent najbolje radi upravo s tekstualnim datotekama.
Problem
Na jednom Hetznerovom VPS-u vrtimo nekoliko Postgres baza, sve deployane preko Coolifyja. Jedna je self-hosted Supabase za aplikaciju jednog klijenta, a ostale su obični Postgres za naše projekte.
Ako Supabase koristite kao hostanu uslugu, backup i restore rješava Supabase. Ako ga hostate sami, ne rješava: dobijete Docker Compose stack, a sve ostalo je na vama.
Noćni pg_dump je par linija shella, ali nedostajalo nam je sve oko njega: da znamo da sinoćnji backup stvarno postoji i izvan servera, i da ga možemo vratiti u Supabase koji radi, a da ne improviziramo u najgorem mogućem trenutku. Zato smo to napravili sami, a na kraju otišli i malo dalje nego što smo planirali.
Prvo skripta
Prva verzija bila je shell skripta koja napravi dump baze i kopira ga na Hetzner Storage Box, i druga koja se pokrene nekoliko sati kasnije i provjeri je li najnoviji backup dobro odrađen. Ako nešto ne štima, javi nam Uptime Kuma. Raspored je u repozitoriju, a na server ga instalira GitHub Actions, pa nitko ne mora ručno uređivati crontab.
Repozitorij smo od početka pisali za dva čitatelja: za nas za šest mjeseci i za AI agenta. Jedna datoteka sadrži pravila kojih se agent mora držati (nikad ne commitaj tajne, ne diraj cron izravno, ažuriraj dokumentaciju kad se ponašanje promijeni). Druga je ono što prvo pročitaš kad se vratiš nakon pauze, s naredbama za provjeru svakog dijela i popisom stvari na kojima smo već zapeli.
To je bilo dovoljno da imamo backup kojem vjerujemo. Onda smo ga htjeli i vidjeti.
Zašto imamo Go agenta
Htjeli smo dashboard koji pokazuje koji backupi postoje, gdje su, koliko su veliki i kad ide sljedeći, s gumbom da ga pokrenemo odmah.
Problem je u tome što skripte za backup trebaju puni pristup serveru: ulaze u druge kontejnere, čitaju pristupne podatke i drže ključ za Storage Box. Web aplikacija koja ih može pokrenuti u principu može pokrenuti bilo što. Naša bi bila iza auth proxyja, ali nismo htjeli da jedno krivo postavljeno pravilo na proxyju bude jedino što stoji između interneta i shella na serveru.
Zato smo to podijelili na dva dijela. Mali servis u Go-u radi izravno na serveru, a dashboard, koji kao i sve ostalo radi u kontejneru, može razgovarati samo s tim servisom.
Taj servis je zapravo popis onoga što je dopušteno. Ne prima naredbe, nego samo imena akcija definiranih u repozitoriju, a svaka od njih vodi na jednu točno određenu skriptu. Izvana mu se ne može pristupiti i odgovara samo na zahtjeve s tokenom koji ima backend dashboarda. Netko tko bi prošao auth proxy i dalje bi mogao samo ono što dashboard već nudi.
Prvu verziju i servisa i dashboarda napisao je AI agent. Kad je proradila, i cron smo prebacili na Go servis, pa backup po rasporedu i onaj pokrenut iz dashboarda prolaze kroz isti kod i završe u istoj povijesti. Tada servis više nije bio dodatak dashboardu, nego ono što pokreće sve.
Postavke u datotekama, namjerno
Svaka baza ima kratku YAML datoteku u repozitoriju. Ovo je jedna od njih, skraćena:
id: app-db
label: App Production Database
type: postgres-selfhosted
enabled: true
# Backups run twice a week; the longest gap is Thursday -> Monday (4 days).
verification:
max_age_hours: 102
min_size_bytes: 100000
schedules:
postgres_backup:
action: postgres_backup
cron: "30 2 * * 1,4"
postgres_check:
action: postgres_check
cron: "50 7 * * 1,4"
Kad pushamo na main, GitHub Actions osvježi repozitorij na serveru, ponovno builda Go servis i iz tih datoteka iznova generira raspored za cron.
Dashboard sve to može prikazati, preuzeti backup ili pokrenuti restore, ali postavke ne može mijenjati. Mogli smo napraviti forme za dodavanje baze ili uređivanje rasporeda, ali odlučili smo da nećemo. AI agent dobro čita mapu punu YAML-a i Markdowna, napravi malu promjenu, objasni je u commit poruci i testira je. Klikanje po stranici s postavkama ide mu puno lošije, a promjena napravljena tako ne ostavlja diff koji bi itko mogao pregledati.
Vraćanje iz backupa
Kad je sve to bilo gotovo, na red je došao restore. Za razliku od backupa, restore prepisuje bazu koja radi, pa krivi potez može obrisati upravo ono što pokušavamo spasiti. Zato dashboard prije svakog restorea pokaže što će se točno dogoditi i provjeri sve što se može provjeriti, od toga je li restore za tu bazu uopće dopušten do toga ima li dovoljno mjesta na disku. Prije nego što išta dira, napravi backup trenutnog stanja, za slučaj da smo odabrali krivi. Restore radi kroz isti Go servis i isti dashboard kao i sve ostalo.
Povratak u listopadu
Repozitorij nismo dirali od svibnja, a onda nam je trebao backup za novu bazu, jedan naš projekt na istom serveru.
Otvorili smo repozitorij u Claude Codeu i zamolili ga da doda backup za novu bazu. Agent je slijedio upute iz repozitorija, sam zaključio koji kontejner treba backupirati i dao nam jednu naredbu za pokrenuti na serveru, da se spreme pristupni podaci. To je bio jedini korak koji smo napravili ručno.
Sve je trajalo otprilike pet minuta. U istoj sesiji promjenom jedne linije pauzirao je i backup za klijentovu bazu, čija se aplikacija trenutno ne koristi. Ništa nismo morali ispravljati.
Agent nije napravio ništa osobito pametno. Sve što mu je trebalo već je bilo zapisano u datotekama koje je mogao pročitati.
Koliko je to trajalo
Četiri dana u svibnju, s tim da su većinu koda napisali AI agenti dok smo mi radili na klijentskim projektima. To je oko 5500 linija Go-a, 3800 JavaScripta, 1100 shella i 4300 Markdowna.
Dokumentacije je gotovo koliko i koda. Toliko je za interni alat nikad ne bismo napisali ručno, a upravo je zbog toga promjena u listopadu bila tako brza.
Prije nekoliko godina ovo uopće ne bismo radili. Tjedne rada na internom dashboardu za backup teško je opravdati u tvrtki naše veličine, pa bismo ostali na skripti i crontabu. Odluke su i dalje naše: što ide u backup, što restore smije napraviti i što nikad ne ide u Git. Promijenilo se to koliko je jeftino te odluke provesti do kraja, sve do testova, dokumentacije i sučelja.
Ako self-hosted Supabase vrtite na jednom serveru, vjerojatno vam ne trebaju ni Go servis ni dashboard. Ono što bismo preporučili je dio zbog kojeg je listopad prošao tako lako: držite skripte, rasporede i upute u jednom repozitoriju i pišite upute za onoga tko ga sljedeći otvori. Danas je to često agent.