Mai multe proiecte Supabase pe același calculator
Al doilea proiect Supabase local se ciocnește pe aceleași porturi, iar porturile mutate manual se despart de configurația aplicației. Vezi ce alocă acum CLI-ul și cât costă.

Mai multe proiecte Supabase locale pe același calculator se strică într-un mod precis și previzibil, iar mecanismul merită înțeles înainte să cauți o soluție. Primul proiect merge. Al doilea fie refuză să pornească pentru că un port e deja ocupat, fie pornește pe porturi alese de tine manual, iar de atunci două aplicații, două fișiere .env și două seturi de scripturi trebuie să cadă de acord ce număr aparține cărui proiect. Când nu mai cad de acord, nimic nu crapă. Aplicația se conectează la baza de date a celuilalt proiect, răspunde cu 200, iar greșeala iese la iveală mult mai târziu.
Din ce este făcut un proiect Supabase local
supabase init scrie un director supabase/ cu un config.toml în el. supabase start pornește stack-ul local: „a local Postgres database, Auth, Storage, and other Supabase features", cum spune ghidul de dezvoltare locală. Stack-ul rulează în containere, deci un runtime compatibil cu Docker este obligatoriu; documentația enumeră Docker Desktop, OrbStack, Rancher Desktop și Podman. Pe Linux și pe macOS cu Apple silicon, comenzile experimentale supabase stack pot rula serviciile și ca procese simple, iar la asta revenim mai târziu.
Serviciile sunt accesibile prin porturi, iar în interiorul unui proiect nu există un port pentru fiecare serviciu. Pagina de început a CLI-ului arată un singur gateway pe 54321, cu totul în spatele unor prefixe de cale: /rest/v1, /graphql/v1, /auth/v1, /storage/v1, /realtime/v1 și /functions/v1. Postgres ascultă pe 54322, Studio pe 54323, interfața de mail pe 54324.
Restul valorilor implicite sunt în referința de configurare a CLI-ului:
api.port54321, gateway-ul.db.port54322 șidb.shadow_port54320, baza de date umbră.studio.port54323.- Secțiunea de mail,
inbucketîn referință: interfața web pe 54324, SMTP pe 54325, POP3 pe 54326. analytics.port54327 șianalytics.vector_port54328.db.pooler.port54329 pentru connection pooler.edge_runtime.inspector_port8083 pentru depanarea Edge Functions.
Numerele în sine nu sunt partea interesantă. Important este că fiecare dintre ele e o adresă fixă pe care o știe ceva din afara Supabase: aplicația ta, runner-ul de teste, scriptul de seed, o comandă din README.
De ce se ciocnesc cele două proiecte
supabase init scrie aceleași porturi în config.toml-ul fiecărui proiect, iar fișierul e făcut să fie commit-uit. Ghidul fluxului local îl trece în tabelul directorului ca „Local stack configuration (ports, auth settings, etc.)" și îl marchează ca fișier de commit. Așadar două proiecte de pe același calculator cer aceleași porturi, iar ghidul de dezvoltare locală spune rezultatul fără ocolișuri: „With the default ports, supabase start runs one local project per machine."
Eșecul e cel puțin zgomotos. Secțiunea de troubleshooting a ghidului îl numește, „Another local Supabase project is using the default ports", iar soluția oferită e să rulezi supabase stop în directorul celuilalt proiect. E în regulă când alternezi între proiecte. E inutil când ai nevoie de amândouă simultan, lucru obișnuit astăzi: un API și adminul lui, un monorepo cu două servicii sau câțiva agenți de coding care lucrează în paralel, în worktree-uri separate.
Prima abordare: ții o listă de porturi
Cea mai ieftină variantă e un tabel în README care spune că proiectul A e pe 54321 și proiectul B pe 54421, sau aceleași informații ținute în cap. Nu costă nimic la instalare și aproape nimic la rulare, până când devine greșit. Porturile scrise în config.toml sunt cereri exacte, deci în momentul în care două rânduri se suprapun, sau apare un al treilea proiect, pornirea eșuează și cineva editează tabelul. Tabelul trebuie replicat și peste tot unde apare adresa: fișierul de mediu al aplicației, scripturile de migrare, configurarea testelor, comenzile curl din runbook.
Comanda care face treaba suportabilă este supabase status, care afișează URL-urile și cheile proiectului pornit. supabase status --env --output-format text le scrie într-un fișier dotenv, iar variabilele pot fi redenumite pentru framework-ul tău în același apel:
supabase status --env --override-name API_URL=NEXT_PUBLIC_SUPABASE_URL,ANON_KEY=NEXT_PUBLIC_SUPABASE_ANON_KEY
Folosită în locul valorilor scrise de mână, comanda elimină derivarea din interiorul unui proiect. Nu îți spune însă în ce proiect ești.
A doua abordare: editezi porturile în fiecare config.toml
Reparația evidentă e să dai fiecărui proiect propriul bloc. Schimbi api.port, db.port, studio.port și restul într-unul dintre cele două fișiere, apoi repornești cu supabase stop și supabase start ca să intre în vigoare valorile noi. Ghidul pentru mai multe proiecte listează fiecare cheie care trebuie mutată: api.port; db.port și db.shadow_port; db.pooler.port; studio.port; port, smtp_port și pop3_port din secțiunea SMTP; analytics.port și vector_port; și edge_runtime.inspector_port. Dacă ratezi una, proiectul continuă să ceară un port pe care îl ține celălalt.
Costul e că decalajele ajung acum în commit. Un coleg care clonează repository-ul moștenește alegerile tale de porturi, iar valorile ajung în CI, unde un container le poate folosi deja. Trebuie schimbat și fiecare consumator: URL-ul aplicației, URL-ul bazei de date din scriptul de seed, URL-ul pooler-ului dacă îl folosești. Funcționează, și de aici pornesc majoritatea echipelor. E un set de numere întreținut manual în două locuri care nu se pot verifica reciproc.
A treia abordare: decalaj prin variabile de mediu
Porturile pot veni și din mediu, de exemplu SUPABASE_API_PORT și SUPABASE_DB_PORT. Sunt cereri exacte la fel ca cele din fișier, cu un plus: dacă portul e ținut de alt proiect local, eroarea numește proiectul, nu numărul. O coliziune devine o reparație de două minute, nu o vânătoare. Nu scapă însă de ținerea evidenței și nu ajută pe un calculator unde variabila nu e setată.
Toate trei au aceeași slăbiciune. Adresa este identitatea proiectului. Schimbi un capăt și nu pe celălalt, iar aplicația continuă să meargă, pe baza de date greșită.
Ce face CLI-ul acum și ce nu repară
Din CLI v2.119.0 există un răspuns mai nou: comenzile experimentale supabase stack. Cu stack = true sub [experimental] în config.toml, sau cu SUPABASE_EXPERIMENTAL_STACK=1 în mediu, comenzile de nivel superior start, status și stop rulează variantele de stack. Un proiect local e identificat prin trei lucruri: cel mai apropiat director care conține supabase/config.toml, branch-ul git curent și un nume dat cu --stack. Identități diferite nu împart niciodată containere, procese, porturi sau date, deci o singură aplicație poate rula supabase start --stack dev și supabase start --stack test una lângă alta.
Porturile sunt partea care contează aici. CLI-ul le alocă din intervalul 20000–32767 și le păstrează stabile între reporniri. Porturile fixe din config.toml au prioritate, deci un proiect existent trebuie să le scoată mai întâi: ghidul spune să rulezi supabase stop, să ștergi cheile și să pornești din nou, iar de la v2.121.0 CLI-ul mută endpoint-urile și păstrează datele. Același ghid numește comenzile experimentale, avertizează că flag-urile și output-ul lor se pot schimba și spune că promisiunea de compatibilitate a CLI-ului nu le acoperă.
Urmează două consecințe. Prima: numărul e acum alocat, nu ales. Documentația promite stabilitate, nu o anumită valoare, deci 54321 încetează să mai fie răspunsul chiar și pentru proiectul care îl deținea. A doua: tot ce are nevoie de adresă ar trebui să o ceară. supabase start --output-format json întoarce un obiect endpoints indexat după serviciu și endpoint, fiecare intrare cu protocol, adresă, port și URL. supabase stack list arată toate proiectele locale de pe calculator, iar supabase stack stop --all le oprește pe toate dintr-o comandă. Pentru o echipă e un câștig real. Pentru un script care încă are scris http://127.0.0.1:54321, e o rupere tăcută.
Sunt și limite pe care merită să le știi înainte să migrezi. Comenzile de bază de date precum db și migration nu acceptă --stack, deci acționează asupra proiectului implicit al branch-ului curent. Trecerea pe alt branch în același director creează alt proiect local, iar primul rămâne pornit până îl oprești. Redenumirea sau mutarea directorului aplicației creează și ea un proiect nou, pentru că traseul face parte din identitate. supabase stack destroy șterge datele și nu face backup. Nimic din toate astea nu e un motiv să eviți comenzile. E un motiv să citești ghidul o dată.
Eșecul care chiar costă timp
Coliziunile de porturi sunt enervante, dar vizibile. Eșecul scump e o adresă rămasă în urmă. Să presupunem că cineva mută un proiect pe 54421 și pune NEXT_PUBLIC_SUPABASE_URL acolo. Mai târziu decalajele se schimbă, sau proiectul trece pe comenzile de stack și primește un port din intervalul 20000. Valoarea veche din .env.local încă se rezolvă, pentru că acolo ascultă acum alt proiect. Aplicația pornește, ecranul de login se randează, iar supabase db reset rulează pe baza de date greșită. Nimic din stack nu verifică dacă proiectul la care ai ajuns e cel la care voiai să ajungi.
Obiceiul care previne asta e ieftin. După ce îndrepți o aplicație spre un stack local, rulează o interogare la care doar proiectul corect poate răspunde: un rând din seed, o versiune de migrare, un nume de tabel. Citește portul din supabase status, nu dintr-o notă. Dacă lucrează două persoane pe același repository, pune aplicația să își citească URL-ul din fișierul de mediu pe care îl scrie CLI-ul, nu dintr-o valoare commit-uită acum câteva luni.
O ușă de intrare în locul unui port
Cealaltă ieșire e să nu mai tratezi portul ca nume. Pornești un proxy mic pe un port fix, dai fiecărui proiect un hostname sau o cale, iar proxy-ul redirează către portul la care e proiectul azi. Aplicațiile arată atunci spre http://billing.localhost și nu se mai schimbă, iar corespondența dintre nume și port stă într-un singur loc.
Supabase Local Manager este o implementare a ideii, în early access. Un singur serviciu Node și Express provisionează și administrează mai multe stack-uri Supabase locale simultan; fiecare proiect rezervă un bloc de 11 porturi din banda 60000; o ușă de intrare rutată pe cale ascultă pe 6000, iar un proxy pe subdomenii pe 9000. CLI-ul Supabase open source se ocupă de administrarea de bază a bazei de date, iar produsul adaugă serviciile, API-urile și interfața pe care le cere un stack Supabase complet, astfel încât întregul stack rulează local, administrat dintr-un singur loc. Are nevoie de Docker. Este independent de Supabase, Inc., codul sursă este privat, iar accesul se cere prin GitHub.
Cum alegem noi
- Un singur proiect odată: folosește valorile implicite. Al doilea proiect e la un
supabase stopdistanță. - Două sau trei proiecte, un singur dezvoltator: comenzile experimentale de stack, cu porturile fixe scoase, și
supabase statusca singura sursă de adrese. - Mai multe servicii, mai mulți agenți sau o echipă: dă fiecărui proiect un nume și pune o ușă de intrare în fața porturilor, ca nicio aplicație să nu stocheze un număr.
Dacă legi Supabase de un produs și vrei ca mediul local să supraviețuiască unui al doilea dezvoltator, proiectele noastre de software la comandă acoperă genul acesta de fundație, iar tu poți programa un apel introductiv gratuit. Am scris și despre alegerea opusă, un site fără bază de date, dacă încă decizi cât backend îți trebuie.
Întrebări frecvente
- De ce nu pot rula două proiecte Supabase local în același timp?
- Fiecare proiect generat de supabase init scrie aceleași porturi în config.toml, iar fișierul e făcut să fie commit-uit. CLI-ul tratează valorile ca cereri exacte, deci al doilea proiect cere porturi pe care primul le ține deja. Documentația Supabase o spune direct: cu porturile implicite, supabase start rulează un singur proiect local pe calculator.
- Ce porturi folosește supabase start implicit?
- Gateway-ul API ascultă pe 54321 și servește REST, GraphQL, Auth, Storage, Realtime și Functions sub prefixe de cale. Postgres folosește 54322, baza de date umbră 54320, Studio 54323, iar interfața de mail 54324. Referința de configurare mai listează SMTP pe 54325, POP3 pe 54326, analytics pe 54327 și 54328, pooler-ul pe 54329 și inspectorul Edge Functions pe 8083.
- Cum rulez mai multe proiecte Supabase locale cu CLI-ul?
- Pe CLI v2.119.0 sau mai nou, activează comenzile experimentale de stack adăugând stack = true sub [experimental] în config.toml. Apoi scoate cheile de port fix, pentru că CLI-ul alocă fiecărui proiect porturi proprii între 20000 și 32767. Pornește fiecare proiect în directorul sau worktree-ul lui și citește adresele din supabase status, nu le presupune.
- Sunt comenzile supabase stack destul de stabile pentru lucrul zilnic?
- Documentația Supabase le numește experimentale și spune că flag-urile și output-ul lor se pot schimba între versiuni, iar promisiunea de compatibilitate a CLI-ului nu le acoperă. Porturile sunt alocate între 20000 și 32767 și rămân stabile între reporniri. Datele proiectelor de stack sunt separate de tot ce a fost pornit cu supabase start implicit, iar activarea setării nu copiază baza de date mai veche.
- Cum eliberez portul 54321 de la un proiect Supabase vechi?
- Rulează supabase stop în directorul proiectului care ține portul, soluția dată de ghidul de troubleshooting Supabase pentru un conflict de porturi. Oprirea păstrează baza de date locală, deci celălalt proiect poate porni. Dacă ai nevoie de amândouă, mută unul pe propriul bloc de porturi sau pe comenzile de stack, în loc să oprești celălalt de fiecare dată.
- Cum verific cu ce proiect Supabase local vorbește aplicația mea?
- Rulează o interogare la care doar proiectul corect poate răspunde, de exemplu un rând din seed, o versiune de migrare sau un nume de tabel care nu există altundeva. Citește portul din supabase status, nu dintr-o notă, și scrie mediul aplicației din supabase status --env. Un URL rămas în urmă se rezolvă în continuare dacă alt proiect ascultă acum pe portul acela, deci faptul că conexiunea reușește nu dovedește nimic singur.


