Înapoi la blog
August 19, 2026Ghiduri

Cum să deplasezi aplicații cu Docker Compose pe un VPS

Un ghid practic de producție pentru Docker Compose v2 pe Ubuntu: instalează motorul, scrie un fișier Compose cu rețele și volume, gestionează secretele, pune un reverse proxy în față, actualizează cu lansări fără panică și fă backup la volumele denumite.

Cum să deplasezi aplicații cu Docker Compose pe un VPS

Docker Compose este modul obișnuit de a rula un teanc mic pe un VPS: o aplicație web, o bază de date, un cache Redis și un reverse proxy, descrise într-un fișier YAML. Nu e Kubernetes. Nu vei avea failover pe mai multe noduri. Vei avea o configurație repetabilă, verificabilă, pe care o poți recrea pe un VPS Hiddence nou în câteva minute — exact ce au nevoie majoritatea proiectelor laterale și produselor mici.

Acest articol presupune că vrei ceva mai aproape de producție decât docker run într-o vineri seara. Vom instala Docker Engine și pluginul Compose, vom crea un utilizator de deploy fără root, vom scrie un fișier Compose cu healthcheck-uri și politici de repornire, vom ține secretele în afara Git, vom publica pe internet doar porturile reverse proxy și vom defini o rutină de actualizare și backup. Teancul exemplu e o aplicație web tipică plus PostgreSQL plus Caddy, dar același model funcționează pentru Node, PHP, Python sau o grămadă de workeri.

De ce Compose pe un singur VPS încă are sens

Oamenii sar la Kubernetes pentru că e la modă, apoi petrec weekendul pe YAML pentru un site. Compose rămâne lizibil. Versionezi fișierul, documentezi variabilele de mediu și poți face diff unei modificări înainte să o aplici. Izolarea e suficientă: un container de aplicație compromis nu ar trebui să țină socket-ul UNIX al bazei dacă ai folosit rețele și utilizatori cu privilegii minime. Limitele de resurse opresc o scurgere de memorie să înghețe tot VPS-ul.

  • Un fișier descrie tot teancul
  • Volumele denumite supraviețuiesc recreării containerelor
  • Rețelele Docker interne țin Postgres departe de internetul public
  • restart: unless-stopped acoperă majoritatea repornirilor
  • Ușor de copiat pe un al doilea VPS când crești din primul

Cerințe

Folosește un VPS cu suficient RAM pentru aplicație plus Postgres. Un plan de 2 GB e un minim realist pentru aplicație + bază + proxy. Se presupune Ubuntu 24.04. Nu instala Docker dintr-un Snap aleatoriu fără să citești ce face cu cgroups; pașii de mai jos folosesc depozitul apt oficial Docker.

  • Ubuntu 22.04/24.04, root sau sudo
  • 2 GB RAM recomandat (1 GB doar pentru teancuri minuscule fără Postgres)
  • Un domeniu care pointează către VPS dacă vrei HTTPS automat
  • Git sau alt mod de a copia proiectul pe server

Pasul 1: Instalează Docker Engine și Compose v2

Compose v2 e un plugin invocat ca docker compose (cu spațiu), nu vechiul binar Python docker-compose. Instalează ambele din depozitul Docker ca să primești patch-uri de securitate actuale.

bash
ssh root@YOUR_VPS_IP
apt update && apt -y install ca-certificates curl gnupg
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
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/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null

apt update
apt -y install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

docker version
docker compose version

Pasul 2: Utilizator de deploy și aspectul directoarelor

Rularea Compose ca root funcționează, dar un utilizator dedicat cu membru în grupul docker e mai curat. Pune proiectul în /opt sau /srv, nu în /root, ca backup-urile și permisiunile să fie evidente. Nu face niciodată scriibil pentru toți directorul care conține .env.

bash
adduser --disabled-password --gecos '' deploy
usermod -aG docker deploy
mkdir -p /srv/app
chown deploy:deploy /srv/app
chmod 750 /srv/app

# Log in as deploy for the rest of the file editing:
# su - deploy
# cd /srv/app

Pasul 3: Scrie un fișier Compose gândit pentru producție

Fișierul de mai jos e un șablon. Înlocuiește imaginea aplicației cu a ta. Postgres nu e publicat pe 0.0.0.0:5432 — doar Caddy e publicat pe 80 și 443. Healthcheck-urile opresc un reverse proxy să trimită trafic către o aplicație care încă migrează baza. Fixează etichetele de imagine; latest e modul în care apar stricăciuni prin surpriză.

bash
# /srv/app/compose.yaml
services:
  caddy:
    image: caddy:2.8-alpine
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy_data:/data
      - caddy_config:/config
    depends_on:
      app:
        condition: service_healthy

  app:
    image: ghcr.io/example/webapp:1.4.2
    restart: unless-stopped
    env_file: .env
    environment:
      DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}
    depends_on:
      db:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "curl", "-fsS", "http://127.0.0.1:8000/health"]
      interval: 10s
      timeout: 3s
      retries: 10
    networks:
      - frontend
      - backend

  db:
    image: postgres:16-alpine
    restart: unless-stopped
    env_file: .env
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
      interval: 10s
      timeout: 5s
      retries: 10
    networks:
      - backend

networks:
  frontend: {}
  backend: {}

volumes:
  pgdata:
  caddy_data:
  caddy_config:

Pasul 4: Secrete, .env și Caddyfile

Pune parolele în .env pe server, chmod 600, și nu face niciodată commit la acel fișier. Generează parole cu openssl rand -base64 32. Caddyfile trebuie doar să reverse-proxy către numele de serviciu al aplicației pe rețeaua Docker.

bash
# /srv/app/.env (permissions 600)
POSTGRES_USER=app
POSTGRES_PASSWORD=change-me-long-random
POSTGRES_DB=app
# plus any APP_SECRET / NEXTAUTH_SECRET your image needs

# /srv/app/Caddyfile
app.example.com {
    encode gzip
    reverse_proxy app:8000
}

chmod 600 /srv/app/.env
cd /srv/app
docker compose up -d
docker compose ps
docker compose logs -f --tail=100

Pasul 5: Firewall, jurnale și limite de resurse

UFW ar trebui să permită doar 22, 80 și 443. Docker ocolește uneori UFW pentru porturi publicate; dacă ai nevoie de un firewall strict pe gazdă, uită-te la interacțiunea curentă Docker + UFW pentru versiunea ta de Ubuntu sau publică porturi doar pe 127.0.0.1 și pune un Caddy pe gazdă în față. Setează limite de memorie în Compose ca Postgres să nu poată mânca VPS-ul.

bash
ufw allow OpenSSH
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable

# Optional in compose.yaml under app or db:
#    deploy:
#      resources:
#        limits:
#          memory: 512M

# Logs (do not let json-file grow forever):
# { "log-driver": "json-file", "log-opts": { "max-size": "10m", "max-file": "3" } }
# in /etc/docker/daemon.json then: systemctl restart docker

Pasul 6: Actualizări și rollback

Actualizarea plictisitoare care funcționează: trage imagini noi, up -d, urmărește healthcheck-urile, păstrează eticheta anterioară în git ca să poți reveni. Nu rula docker compose down pe o bază de producție decât dacă intenționezi să oprești acceptarea traficului. down -v șterge volumele — e o ștergere, nu o repornire.

bash
cd /srv/app
git pull   # if the Compose file lives in git

# Change the image tag in compose.yaml, then:
docker compose pull
docker compose up -d
docker compose ps

# Rollback: set the old tag, pull, up -d again
# NEVER: docker compose down -v   # this deletes named volumes

Fă backup la volumele denumite

Un snapshot VPS e bun. Un dump logic Postgres e mai bun dacă trebuie să restaurezi pe altă mașină. Programează asta cu cron. Testează o restaurare o dată, altfel nu ai backup-uri — ai fișiere despre care speri că sunt backup-uri.

bash
# Postgres dump while the db container is running:
docker compose exec -T db pg_dump -U app app | gzip > /var/backups/app-$(date +%F).sql.gz

# Copy off-box
# scp /var/backups/app-*.sql.gz backup-host:~

# Restore sketch (maintenance window):
# gunzip -c app-2026-08-19.sql.gz | docker compose exec -T db psql -U app app

Depanare

Citește docker compose ps și docker compose logs service. Eșecurile de pull de imagine sunt de obicei limite de rată ghcr/docker hub sau o imagine privată fără autentificare. 502 de la Caddy înseamnă că aplicația nu e healthy sau numele de gazdă al proxy-ului e greșit (folosește numele de serviciu Compose, nu localhost, din interiorul containerului Caddy). Erorile de permisiuni pe volume înseamnă adesea că imaginea rulează ca uid 1000 în timp ce directorul gazdă e root.

  • compose: command not found — ai instalat vechiul nume de binar; folosește docker compose
  • port is already allocated — altceva deține 80/443 (Apache, Nginx, alt Caddy)
  • database connection refused — aplicația a pornit înainte ca Postgres să fie gata; folosește healthcheck + depends_on condition
  • disk full — docker system df, apoi prune imagini nefolosite cu grijă
  • permission denied on docker.sock — utilizatorul nu e în grupul docker sau ai nevoie de o sesiune de autentificare nouă

Securitate

Nu publica porturi de bază de date. Nu rula privileged: true. Nu monta /var/run/docker.sock într-un container de aplicație decât dacă scrii deliberat un manager de containere. Ține motorul actualizat. Scanează propriile imagini dacă le construiești. .env pe disc e tot un fișier secret — restricționează cine poate face ssh la cutie.

  • Fără 5432 / 3306 / 6379 public
  • Fixează digest-uri de imagine sau cel puțin etichete imuabile
  • chmod 600 .env
  • Unattended-upgrades pentru OS-ul gazdei încă contează
  • Un proiect Compose per aplicație ține raza de explozie mai mică

Sfaturi

  • Pune compose.yaml în git; păstrează .env.example fără secrete reale
  • Folosește profiluri pentru workeri opționali (docker compose --profile workers up -d)
  • Urmărește docker stats când dimensionezi VPS-ul
  • Pentru bind mounts de cod sursă, acesta nu e modelul de producție — coace o imagine
  • Dacă teancul crește la mai multe VPS, atunci uită-te la orhestrare — nu înainte

O configurație Compose aproape de producție pe un VPS e: pachete Docker oficiale, un .env blocat, un fișier YAML care nu publică baza, healthcheck-uri, un reverse proxy pe 80/443, UFW, etichete de imagine pe care le poți da înapoi și dump-uri Postgres pe care le-ai restaurat cu adevărat o dată. Pornește de la șablonul din acest articol, înlocuiește imaginea aplicației și tratează docker compose down -v ca pe o comandă distructivă cu același respect ca rm -rf.