
Docker Compose pentru incepatori: volume, retele si .env
Mai devreme sau mai tarziu, orice ghid de self-hosting iti da un bloc de YAML si iti spune: “salveaza asta ca compose.yaml si ruleaza docker compose up -d”. Merge, si tocmai asta e problema. In ziua in care trebuie sa schimbi o parola, sa muti datele, sa actualizezi o imagine sau sa intelegi de ce un container se restarteaza la fiecare zece secunde, fisierul lipit fara sa-l citesti devine o cutie neagra. In acest tutorial Docker Compose deschidem cutia. Pe un VPS proaspat construim un stack mic cu doua containere, citim compose.yaml rand cu rand, tinem datele intr-un volum care supravietuieste restarturilor si update-urilor, mutam parolele intr-un fisier .env si exersam cele cateva comenzi pe care le vei folosi saptamanal: pornire, verificare, loguri, update si curatenie, fara sa pierzi nimic.
Docker Compose este instrumentul oficial Docker pentru a descrie aplicatii cu mai multe containere intr-un singur fisier. Documentatia e pe docs.docker.com/compose , iar codul sursa pe github.com/docker/compose . Tot ce facem aici se aplica direct aplicatiilor pe care chiar vrei sa le rulezi mai tarziu, de la WordPress si Nextcloud la Gitea, Uptime Kuma sau propriul tau cod: odata ce stii sa citesti un fisier compose, le poti citi pe toate.
Ce ai nevoie
- Un VPS cu Debian 13 sau Ubuntu 24.04. 1 GB RAM si 10 GB disc sunt mai mult decat suficiente pentru acest tutorial. Un VPS KVM e cea mai simpla varianta pentru Docker (vezi nota despre LXC de la Pasul 1).
- Un utilizator normal cu drepturi sudo si acces SSH. Nu lucram niciodata direct ca root.
- Un terminal pe calculatorul tau (Linux, macOS sau Windows 10/11 cu clientul OpenSSH inclus) pentru tunelul SSH de la Pasul 5.
- Putina familiaritate cu
cd,lssi un editor de text precumnano. Atat, restul il explicam pe parcurs.
Docker Compose pe intelesul tuturor
Containere si imagini
Un container este un proces izolat pe serverul tau care vine cu tot ce ii trebuie ca sa ruleze: fisierele, bibliotecile si setarile proprii, dar foloseste kernelul gazdei. Gandeste-te la el ca la o cutie usoara, de unica folosinta, pentru un singur program. O imagine este sablonul read-only din care se creeaza containerul, de exemplu postgres:17, publicat pe Docker Hub. Partea de dupa doua puncte este tag-ul, o eticheta care de obicei inseamna o versiune.
Modelul mental care te scuteste de multe probleme: containerele sunt de unica folosinta, imaginile se pot inlocui oricand, iar singurul lucru care conteaza pe termen lung sunt datele tale. Tot tutorialul e construit in jurul acestei idei.
Ce aduce Compose in plus
Cu Docker simplu pornesti fiecare container cu o comanda docker run lunga, plina de optiuni. Pentru un container merge, pentru trei devine chinuitor. Docker Compose iti permite sa descrii toata aplicatia (toate containerele, setarile, stocarea si legaturile dintre ele) intr-un singur fisier YAML numit compose.yaml. O comanda creeaza sau actualizeaza totul, alta sterge totul. Fisierul devine documentatie pe care o poti citi, versiona in Git si copia pe alt server.
S-ar putea sa vezi inca vechea comanda docker-compose, cu cratima. Aceea era Compose v1, un program Python separat, ajuns la sfarsitul vietii in 2023. Astazi Compose este un plugin al Docker CLI si scrii docker compose, cu spatiu. La fel, linia version: din capul fisierelor mai vechi este depasita si ignorata, asa ca nu o mai scriem.
Piesele de baza: servicii, volume, retele, porturi si .env
- Serviciu: o intrare din compose.yaml care descrie un container: ce imagine, ce setari, cum se restarteaza. Stack-ul nostru are doua servicii,
db(o baza de date PostgreSQL) siadminer(o interfata web mica pentru baze de date). - Volum: stocare gestionata de Docker, care traieste in afara containerului. Cand un container e sters si recreat, volumul ramane. Aici trebuie sa stea bazele de date, fisierele urcate si configurarile.
- Retea: o retea virtuala privata intre containere. Containerele din aceeasi retea se gasesc dupa numele serviciului, asa ca Adminer se conecteaza pur si simplu la hostname-ul
db. - Port: o “usa” numerotata pe o adresa IP, unde asculta un program, de exemplu 22 pentru SSH sau 443 pentru HTTPS. A “publica” un port inseamna a lega un port al serverului de un port din interiorul containerului.
- Fisierul .env: un fisier text simplu, langa compose.yaml, care tine variabile precum parolele. Compose il citeste automat si inlocuieste valorile in YAML, asa ca secretele nu ajung in fisierul pe care il impartasesti.
De ce PostgreSQL si Adminer? Pentru ca impreuna arata fiecare concept aproape fara zgomot: o baza de date care trebuie sa-si pastreze datele, o aplicatie web care trebuie sa gaseasca baza de date, o parola care trebuie sa ramana secreta si un port care nu are ce cauta in internet. Inlocuieste-le mai tarziu cu WordPress si MariaDB, sau Nextcloud si Redis, si structura ramane exact aceeasi.
Planul: tutorial Docker Compose in 8 pasi
Pasul 1: Instalezi Docker Engine si pluginul Compose
Atat Debian cat si Ubuntu vin cu propriul pachet docker.io, dar de obicei e in urma versiunii oficiale si impacheteaza Compose separat. Noi folosim repository-ul oficial Docker, ca sa se potriveasca comenzile cu documentatia oficiala. Blocul de mai jos merge pe ambele distributii: citeste /etc/os-release si foloseste $ID (debian sau ubuntu) si $VERSION_CODENAME (trixie pe Debian 13, noble pe Ubuntu 24.04) ca sa aleaga repository-ul corect.
sudo apt update
sudo apt install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
. /etc/os-release
sudo curl -fsSL "https://download.docker.com/linux/${ID}/gpg" -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/${ID} ${VERSION_CODENAME} stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Ce face: instaleaza uneltele necesare pentru descarcari prin HTTPS, salveaza cheia de semnare Docker in /etc/apt/keyrings ca apt sa poata verifica pachetele, adauga repository-ul, apoi instaleaza motorul (docker-ce), clientul de linie de comanda, runtime-ul de containere si pluginul Compose. Verificam ca totul functioneaza:
docker --version
docker compose version
sudo docker run --rm hello-world
Ar trebui sa vezi cate o linie cu versiunea fiecarui tool (ceva de genul Docker Compose version v2.x.x) si un mesaj “Hello from Docker!” de la containerul de test, care se sterge imediat datorita optiunii --rm.
Pe Ubuntu, asigura-te ca nu folosesti pachetul snap de Docker: daca which docker afiseaza /snap/bin/docker, sterge-l cu sudo snap remove docker inainte sa continui, pentru ca versiunea snap ruleaza izolat si se comporta diferit cu fisierele si volumele. Debian nu foloseste snap, deci acolo nu e cazul.
Ca sa rulezi comenzi Docker fara sa scrii sudo de fiecare data, adauga-ti utilizatorul in grupul docker, apoi iesi si reconecteaza-te prin SSH ca schimbarea sa intre in vigoare:
sudo usermod -aG docker "$USER"
exit
Dupa reconectare, comanda groups ar trebui sa afiseze si docker. Atentie: apartenenta la acest grup echivaleaza cu root pe server (detalii in sectiunea de securitate). Daca preferi, sari peste si pune sudo in fata fiecarei comenzi docker de mai jos.
Nota despre LXC: intr-un container LXC neprivilegiat, Docker are nevoie de functia “nesting” (si adesea “keyctl”) activata pe container, altfel serviciul nu porneste sau containerele nu pot fi create. Pe un VPS LXC DreamServer poti cere echipei de suport sa o activeze, dar pentru Docker un VPS KVM e alegerea mai simpla si mai previzibila.
Pasul 2: Creezi un folder de proiect
mkdir -p ~/compose-demo
cd ~/compose-demo
Fiecare proiect Compose traieste in folderul lui, iar toate comenzile docker compose se ruleaza din interiorul lui. Implicit, Compose foloseste numele folderului ca nume de proiect si il pune ca prefix la tot ce creeaza. Noi vom seta numele explicit in fisier, asa ca containerele se vor numi demo-db-1 si demo-adminer-1 indiferent de folder. Un folder pentru fiecare aplicatie tine serverul ordonat: ~/wordpress, ~/gitea si asa mai departe, fiecare cu compose.yaml si .env propriu.
Pasul 3: Pastrezi secretele intr-un fisier .env
Parolele nu au ce cauta in compose.yaml. Vei copia fisierul in notite, il vei trimite unui coleg sau il vei urca in Git, iar secretele au obiceiul sa scape din fisierele care circula. Asa ca facem un fisier .env separat si generam direct in el o parola aleatoare puternica:
cat > .env <<EOF
POSTGRES_USER=demo
POSTGRES_DB=demo
POSTGRES_PASSWORD=$(openssl rand -hex 24)
EOF
chmod 600 .env
echo ".env" >> .gitignore
cat .env
Bucata $(openssl rand -hex 24) este inlocuita cu 48 de caractere hexazecimale aleatoare in momentul in care se scrie fisierul. chmod 600 face fisierul lizibil doar pentru utilizatorul tau, iar linia din .gitignore impiedica Git sa-l includa vreodata daca versionezi folderul mai tarziu. Ultima comanda afiseaza fisierul ca sa vezi parola generata; iti va trebui o data, la Pasul 5.
Cateva reguli pentru formatul .env: cate un CHEIE=valoare pe linie, fara spatii in jurul lui =, fara ghilimele pentru valori simple, iar liniile care incep cu # sunt comentarii. Parolele hexazecimale evita caractere precum $, # sau ghilimele, care ar cere atentie speciala.
O distinctie importanta: Compose citeste acest fisier .env pentru substitutia variabilelor din compose.yaml (acele ${...} pe care le vezi imediat). Valorile ajung intr-un container doar daca compose.yaml i le transmite, ceea ce facem explicit. Asa, YAML-ul iti arata mereu exact ce primeste fiecare container.
Pasul 4: Scrii compose.yaml si intelegi fiecare rand
Deschide editorul cu nano compose.yaml, lipeste continutul de mai jos, apoi salveaza cu Ctrl+O, Enter si iesi cu Ctrl+X.
name: demo
services:
db:
image: postgres:17
restart: unless-stopped
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Seteaza POSTGRES_PASSWORD in .env}
POSTGRES_DB: ${POSTGRES_DB}
volumes:
- db_data:/var/lib/postgresql/data
networks:
- backend
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
adminer:
image: adminer:5
restart: unless-stopped
ports:
- "127.0.0.1:8080:8080"
environment:
ADMINER_DEFAULT_SERVER: db
depends_on:
db:
condition: service_healthy
networks:
- backend
- web
volumes:
db_data:
networks:
backend:
internal: true
web:
YAML in doua minute
YAML exprima structura prin indentare. O linie cheie: valoare seteaza o valoare, un bloc indentat mai adanc apartine liniei de deasupra, iar liniile care incep cu - sunt elemente dintr-o lista. Indenteaza doar cu spatii, niciodata cu tab-uri; conventia este doua spatii pe nivel. Majoritatea problemelor de tip “nu merge Compose” la incepatori se reduc la un spatiu pus gresit, iar la Pasul 5 il lasam pe Compose sa verifice fisierul pentru noi.
Nivelul de sus
name: demoseteaza numele proiectului. Containerele, retelele si volumele primesc prefixuldemo, deci volumul devinedemo_db_data.services:contine cate un bloc pentru fiecare container. Numeledbsiadminerle alegi tu, iar ele sunt si hostname-uri in retea.volumes:declara volumul numitdb_data. Valoarea goala inseamna “setari implicite”: Docker il creeaza in/var/lib/docker/volumes/la prima pornire.networks:declara doua retele.backendareinternal: true, adica containerele din ea vorbesc intre ele, dar nu au iesire spre internet.webeste o retea normala.
Serviciul db, rand cu rand
image: postgres:17foloseste imaginea oficiala PostgreSQL, versiunea majora 17. Daca fixezi versiunea majora,docker compose pulladuce corecturi de bug-uri si patch-uri de securitate (17.x), dar niciodata un upgrade major care ar cere migrarea datelor.:lateste comod pana in ziua in care sare pe tacute la o versiune majora noua si baza de date refuza sa porneasca pe datele vechi.restart: unless-stoppedreporneste containerul daca se blocheaza si dupa un reboot al serverului, mai putin cand l-ai oprit intentionat. Alte optiuni suntno,alwayssion-failure.environment:transmite variabile in container. Imaginea PostgreSQL le citeste doar la prima pornire, cand folderul de date e gol, si creeaza utilizatorul, parola si baza de date.${POSTGRES_USER}se completeaza din .env. Sintaxa:?din${POSTGRES_PASSWORD:?...}face variabila obligatorie: daca lipseste, Compose se opreste cu mesajul respectiv in loc sa porneasca o baza de date cu parola goala.volumes: - db_data:/var/lib/postgresql/datamonteaza volumul numit in folderul in care PostgreSQL 17 isi tine fisierele. Formatul este mereusursa:destinatie in container. Fara acest rand, datele ar sta in container si ar disparea la recrearea lui.networks: - backendleaga baza de date doar de reteaua interna. Observa cadbnu are deloc sectiuneaports:: baza de date e accesibila celorlalte containere dinbackendsi nimanui altcuiva.healthcheck:ii spune lui Docker cum sa decida daca serviciul e chiar pregatit, nu doar pornit.pg_isreadyeste o unealta mica inclusa in imagine; Docker o ruleaza la fiecare 10 secunde si marcheaza containerulhealthycand reuseste.start_periodii da bazei de date 30 de secunde de gratie la prima pornire, cat isi initializeaza folderul de date.
Serviciul adminer, rand cu rand
image: adminer:5este imaginea oficiala Adminer, versiunea majora 5.ports: - "127.0.0.1:8080:8080"publica portul. Citeste-l caIP gazda:port gazda:port container. Adminer asculta pe 8080 in container, iar noi il facem disponibil pe portul 8080 al serverului, dar doar pe127.0.0.1, adresa loopback, accesibila de pe server si nu din internet. Pune mereu maparile de porturi intre ghilimele: YAML poate interpreta valori fara ghilimele precum22:22ca numere in baza 60.ADMINER_DEFAULT_SERVER: dbprecompleteaza campul de server din formularul de login.dbeste numele serviciului, rezolvat de DNS-ul intern Docker in reteaua comuna.depends_oncucondition: service_healthyil face pe Compose sa astepte pana cand baza de date trece de healthcheck, abia apoi porneste Adminer. Undepends_onsimplu, fara conditie, controleaza doar ordinea de pornire, nu si daca serviciul e gata.networks: - backend - webpune Adminer in ambele retele:backendca sa ajunga la baza de date, siwebpentru ca un port publicat functioneaza doar printr-o retea care nu e interna.
Pasul 5: Validezi si pornesti stack-ul
Mai intai il lasam pe Compose sa citeasca fisierul, sa inlocuiasca valorile din .env si sa verifice sintaxa:
docker compose config --quiet
Daca nu apare nimic, fisierul e valid. Daca exista o greseala, primesti o eroare cu numarul liniei. Ruleaza docker compose config fara --quiet ca sa vezi fisierul complet rezolvat, dar tine minte ca afiseaza parola in clar, deci nu lipi niciodata acel output undeva public.
Acum pornim totul in fundal (-d vine de la detached):
docker compose up -d
La prima rulare se descarca imaginile, ceea ce dureaza cam un minut, in functie de conexiune. Apoi vezi aproximativ asta, cu mici bife in terminal:
[+] Running 5/5
Network demo_backend Created
Network demo_web Created
Volume "demo_db_data" Created
Container demo-db-1 Healthy
Container demo-adminer-1 Started
Observa ordinea: intai retelele si volumul, apoi baza de date, iar Adminer abia dupa ce db a raportat Healthy. Asta e depends_on la treaba.
Deschizi Adminer printr-un tunel SSH
Adminer asculta doar pe adresa loopback a serverului, asa ca aducem portul pe laptopul tau prin SSH. Ruleaza comanda pe calculatorul tau, nu pe VPS:
ssh -L 8080:127.0.0.1:8080 utilizatorul-tau@ip-ul-vps-ului
Cat timp sesiunea SSH ramane deschisa, http://localhost:8080 din browser trece prin tunelul criptat pana la portul 8080 de pe VPS. In formularul de login alege PostgreSQL ca sistem, lasa db ca server, scrie demo la utilizator si la baza de date, si lipeste parola din .env. Te uiti acum la o baza de date care ruleaza intr-un container, administrata de o aplicatie web din alt container, legate printr-o retea privata, si nimic din toate astea nu e expus in internet.
Pasul 6: Verifici statusul, logurile si resursele
Trei comenzi raspund la majoritatea intrebarilor de zi cu zi:
docker compose ps
docker compose logs --tail=20
docker compose logs -f db
docker compose ps listeaza containerele proiectului:
NAME IMAGE SERVICE STATUS PORTS
demo-adminer-1 adminer:5 adminer Up 2 minutes 127.0.0.1:8080->8080/tcp
demo-db-1 postgres:17 db Up 2 minutes (healthy) 5432/tcp
Outputul real are cateva coloane in plus, cum ar fi comanda si momentul crearii. (healthy) este rezultatul healthcheck-ului, iar 5432/tcp fara adresa inseamna ca portul exista in reteaua Docker, dar nu e publicat pe gazda.
docker compose logs --tail=20 arata ultimele 20 de linii din fiecare serviciu, fiecare prefixata cu numele containerului. docker compose logs -f db urmareste live logul bazei de date, primul lucru la care te uiti cand ceva nu merge; apasa Ctrl+C ca sa opresti urmarirea (containerul continua sa ruleze). Un PostgreSQL sanatos se termina cu database system is ready to accept connections.
Poti rula si comenzi in interiorul unui container pornit, cu exec, de exemplu ca sa deschizi consola PostgreSQL:
docker compose exec db psql -U demo -d demo
Scrie \l ca sa listezi bazele de date si \q ca sa iesi.
Dovada ca datele stau in volum
Cream un tabel, stergem ambele containere si verificam ca datele au supravietuit:
docker compose exec db psql -U demo -d demo -c "CREATE TABLE notes (id serial PRIMARY KEY, body text);"
docker compose exec db psql -U demo -d demo -c "INSERT INTO notes (body) VALUES ('salut din volum');"
docker compose down
docker compose up -d
docker compose exec db psql -U demo -d demo -c "SELECT * FROM notes;"
docker compose down sterge containerele si retelele, iar up -d creeaza altele complet noi. SELECT-ul final returneaza in continuare salut din volum, pentru ca datele n-au fost niciodata in container: ele stau in volumul demo_db_data. Il vezi cu docker volume ls si ii afli locatia cu docker volume inspect demo_db_data, care arata un Mountpoint in /var/lib/docker/volumes/. Te poti uita, dar nu modifica fisiere acolo cat timp ruleaza baza de date.
Volum numit sau bind mount? Pe langa volumele numite, Compose suporta si bind mount-uri, de exemplu ./data:/var/lib/postgresql/data, care mapeaza un folder ales de tine pe gazda. Bind mount-urile sunt ideale pentru fisiere de configurare pe care le editezi de mana (de exemplu ./nginx.conf:/etc/nginx/nginx.conf:ro, unde :ro inseamna doar citire), iar volumele numite sunt alegerea mai sigura pentru fisierele bazelor de date, pentru ca Docker se ocupa de proprietar si permisiuni.
Cat consuma?
docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}"
Comanda afiseaza cate o linie pentru fiecare container, cu procesorul si memoria folosite in acel moment. Daemonul Docker nu apare aici; systemctl status docker containerd iti arata memoria lui pe linia Memory:.
In repaus, tot stack-ul, inclusiv daemonul Docker, incape in aproximativ 150 MB de RAM, deci ruleaza linistit pe cel mai mic VPS. Aplicatiile reale precum WordPress sau Nextcloud au nevoie de mai mult, dar overhead-ul lui Docker si Compose ramane mic.
Pasul 7: Actualizezi containerele fara sa pierzi date
Intai backup
Inainte de orice update, fa un dump logic al bazei de date. E un fisier SQL simplu, pe care il poti restaura oriunde:
mkdir -p ~/backups
docker compose exec -T db sh -c 'pg_dump -U "$POSTGRES_USER" -d "$POSTGRES_DB"' > ~/backups/demo-$(date +%F).sql
ls -lh ~/backups
-T dezactiveaza pseudo-terminalul, ca outputul sa poata fi redirectionat curat intr-un fisier. Ghilimelele simple fac ca variabilele sa fie expandate in container, unde sunt setate, nu pe gazda, unde nu exista. Ca sa restaurezi mai tarziu, trimiti fisierul inapoi (foloseste numele fisierului tau):
docker compose exec -T db sh -c 'psql -U "$POSTGRES_USER" -d "$POSTGRES_DB"' < ~/backups/demo-2026-11-10.sql
Un backup care sta doar pe acelasi VPS e o comoditate, nu un plan de recuperare in caz de dezastru, asa ca copiaza-l regulat in afara serverului.
Pull si recreare
docker compose pull
docker compose up -d
docker image prune -f
pull descarca imagini mai noi pentru tag-urile din fisier, de exemplu un patch nou 17.x. up -d compara ce ruleaza cu ce descriu fisierul si imaginile si recreeaza doar containerele care s-au schimbat; volumul e reatasat neatins. docker image prune -f sterge imaginile vechi, ramase fara tag, care altfel ti-ar umple incet discul. Verifica apoi docker compose ps si logurile, ca sa te asiguri ca totul a revenit healthy.
Ai modificat .env sau compose.yaml?
Dupa ce editezi oricare dintre ele, ruleaza din nou docker compose up -d. Nu te baza pe docker compose restart pentru asta: reporneste containerele existente cu configuratia veche si ignora modificarile tale.
Upgrade de versiune majora
Schimbarea din postgres:17 in postgres:18 nu e un update, e o migrare. Fisierele de date ale unei versiuni majore nu pot fi deschise de urmatoarea, iar imaginea PostgreSQL 18 si-a schimbat si calea implicita pentru date. Drumul sigur: faci dump cu comanda de mai sus, pornesti noua versiune cu un volum nou, gol, apoi restaurezi dump-ul in el. Aceeasi prudenta se aplica la MariaDB, MySQL si majoritatea bazelor de date, asa ca citeste notele de lansare inainte sa atingi un tag major.
Pasul 8: Opresti, stergi si faci curatenie in siguranta
Comenzile de curatenie seamana intre ele, dar au consecinte foarte diferite:
docker compose stop # opreste containerele, pastreaza tot
docker compose start # le porneste din nou
docker compose down # sterge containerele si retelele, pastreaza volumele
docker compose down -v # sterge SI volumele numite: datele tale dispar
docker system df # cat spatiu pe disc foloseste Docker
docker image prune -a # sterge toate imaginile nefolosite de vreun container
docker system prune # containere oprite, retele nefolosite, imagini dangling, cache de build
docker compose down nu iti atinge datele si e comanda pe care o folosesti cand vrei sa reconstruiesti un stack de la zero. De down -v trebuie sa te feresti: foloseste-o doar cand chiar vrei sa arunci datele, de exemplu la finalul acestui tutorial, si de preferat dupa un backup. docker system prune nu atinge volumele numite decat daca adaugi --volumes, si chiar si atunci sterge doar volumele pe care nu le foloseste niciun container, asa ca gandeste-te de doua ori inainte sa adaugi acea optiune pe un server de productie.
Cand ai terminat de experimentat si vrei sa dispara complet demo-ul:
cd ~/compose-demo
docker compose down -v
docker image prune -a -f
Probleme frecvente si greseli comune
- “permission denied while trying to connect to the Docker daemon socket”: utilizatorul tau nu e inca in grupul
docker, sau nu te-ai deconectat si reconectat dupa ce l-ai adaugat. Reconecteaza-te sau folosestesudo. - “docker: ‘compose’ is not a docker command”: lipseste pluginul Compose. Instaleaza
docker-compose-plugindin repository-ul Docker, ca la Pasul 1. - “yaml: line 12: did not find expected key” sau ceva asemanator: o problema de indentare, adesea un tab sau un spatiu in plus.
docker compose config --quietiti arata linia. - “required variable POSTGRES_PASSWORD is missing a value”: Compose nu a gasit fisierul .env. Trebuie sa fie in folderul proiectului, langa compose.yaml, si trebuie sa rulezi comenzile din acel folder (sau sa folosesti
--project-directory). - “port is already allocated” sau “address already in use”: altceva foloseste deja portul 8080 pe gazda. Schimba doar partea din stanga, de exemplu
"127.0.0.1:8081:8080", si foloseste 8081 si in tunelul SSH. - Login-ul esueaza dupa ce ai schimbat parola in .env: imaginea PostgreSQL aplica
POSTGRES_PASSWORDdoar cand volumul e gol. Schimb-o in baza de date (ALTER USER demo WITH PASSWORD 'parola-noua';in psql), apoi actualizeaza si .env, sau ia-o de la capat cudown -vdaca datele nu conteaza. - Un container se restarteaza la nesfarsit:
docker compose psarataRestarting. Citestedocker compose logs db(sau serviciul respectiv); motivul e aproape mereu in ultimele linii. - Discul se umple in timp: suspectii obisnuiti sunt imaginile vechi si logurile containerelor. Ruleaza
docker system df, sterge imaginile nefolosite si limiteaza marimea logurilor creand/etc/docker/daemon.jsoncu continutul{"log-driver": "local"}, apoi ruleazasudo systemctl restart dockersidocker compose up -d --force-recreate, pentru ca setarea se aplica doar containerelor nou create. Driverullocalroteste logurile automat. - Docker nu porneste pe un VPS LXC: nesting-ul nu e activat pe container, vezi nota de la Pasul 1.
Note de securitate
- Porturile publicate ocolesc ufw. Docker isi scrie propriile reguli de firewall, iar un port publicat ca
"8080:8080"e accesibil din internet chiar dacaufwspune ca e blocat. Comportamentul e acelasi pe Debian si pe Ubuntu. De aceea legam portul de127.0.0.1si folosim tunel SSH. Pentru aplicatii web publice, publica doar un reverse proxy (Caddy, Nginx, Traefik) pe porturile 80 si 443 si tine restul intern. - Grupul docker inseamna root. Oricine poate rula comenzi Docker poate monta sistemul de fisiere al gazdei intr-un container si poate schimba orice. Adauga in el doar administratori de incredere.
- Variabilele de mediu nu sunt invizibile. Oricine are acces la Docker le poate citi cu
docker inspect. Pentru o izolare mai buna, Compose suporta secrete montate ca fisiere, iar imaginea PostgreSQL acceptaPOSTGRES_PASSWORD_FILE:
services:
db:
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
secrets:
db_password:
file: ./db_password.txt
- Tine bazele de date intr-o retea interna, fara porturi publicate, cum am facut aici. Doar serviciile care trebuie accesate din afara primesc un port.
- Nu lasa uneltele de administrare pornite. Adminer e util, dar n-are de ce sa ruleze tot timpul.
docker compose stop admineropreste doar acel serviciu;docker compose start admineril porneste cand ai nevoie. - Foloseste imagini oficiale sau bine intretinute, fixeaza versiunea majora si actualizeaza regulat. O imagine neactualizata e un server nepatch-uit, doar intr-o cutie mai mica.
Deci, merita sa inveti Docker Compose?
Categoric, iar acum ai imaginea completa: compose.yaml descrie serviciile, volumele pastreaza datele, retelele decid cine cu cine vorbeste, porturile decid ce se vede din afara, iar .env tine secretele departe de fisier. Cu up -d, ps, logs, pull si down poti rula aproape orice aplicatie self-hosted, iar cand ceva se strica stii unde sa te uiti in loc sa o iei de la capat. Pasul natural urmator este un reverse proxy cu HTTPS automat in fata aplicatiilor, ca sa le poti pune pe un domeniu real.
Poti exersa tot ce e mai sus pe un VPS din data center-ul nostru din Romania in cateva minute. Iar daca preferi ca altcineva sa-ti proiecteze, sa-ti securizeze si sa-ti intretina stack-urile Docker, echipa noastra de administrare servere le poate configura de la zero.
Intrebari frecvente
Care e diferenta dintre docker-compose si docker compose?
docker-compose (cu cratima) este vechiul Compose v1, un tool Python separat, care nu mai e intretinut. docker compose (cu spatiu) este Compose v2, un plugin al Docker CLI. Formatul fisierului e practic acelasi, dar foloseste v2 pentru orice proiect nou.
Fisierul se numeste compose.yaml sau docker-compose.yml?
Merg amandoua. compose.yaml este numele preferat de documentatia actuala, iar Compose gaseste in continuare docker-compose.yml in proiectele mai vechi. Doar nu le tine pe amandoua in acelasi folder, altfel vei edita unul in timp ce Compose il citeste pe celalalt.
Mai am nevoie de linia version:?
Nu. Este depasita in Compose v2, e ignorata, iar versiunile actuale afiseaza un avertisment cand o gasesc. Incepe fisierul direct cu services: (sau cu name:, cum am facut noi).
Pot rula mai multe proiecte Compose pe acelasi VPS?
Da. Da-i fiecaruia folderul si numele de proiect propriu si ai grija ca doua proiecte sa nu publice niciodata acelasi port pe gazda. Fiecare proiect primeste retelele lui, deci sunt izolate unul de altul daca nu le conectezi intentionat.
Unde sunt stocate volumele si cum le fac backup?
Volumele numite stau in /var/lib/docker/volumes/<nume>/_data. Pentru baze de date, prefera un dump logic, ca la Pasul 7. Pentru alte volume, de exemplu fisiere urcate de utilizatori, opreste serviciul si arhiveaza volumul cu un container temporar, de exemplu docker run --rm -v aplicatia_mea_uploads:/data -v "$PWD":/backup debian:13-slim tar czf /backup/uploads.tar.gz -C /data ., apoi copiaza arhiva in afara serverului.