Обратно към блога
Август 19, 2026Ръководства

Как да разгръщате приложения с Docker Compose на VPS

Практическо продуктово ръководство за Docker Compose v2 на Ubuntu: инсталиране на двигателя, файл Compose с мрежи и томове, тайни, обратен прокси отпред, обновявания без паника и резервни копия на именувани томове.

Как да разгръщате приложения с Docker Compose на VPS

Docker Compose е обичайният начин да въртите малък стек на един VPS: уеб приложение, база, кеш Redis и обратен прокси в един YAML. Това не е Kubernetes. Няма да получите отказ при много възли. Ще получите повтаряема, преглеждаема схема, която за минути вдигате на нов VPS на Hiddence — точно това трябва на повечето странични проекти и малки продукти.

Статията приема, че искате нещо по-близо до продукция от docker run в петък вечер. Ще инсталираме Docker Engine и приставката Compose, ще създадем потребител за разгръщане без root в ежедневието, ще напишем файл Compose с проверки за здраве и политика за рестарт, ще държим тайните извън Git, ще публикуваме в интернет само портовете на обратния прокси и ще опишем рутина за обновяване и копие. Примерният стек е типично уеб приложение плюс PostgreSQL плюс Caddy, но същият шаблон работи за Node, PHP, Python или набор работници.

Защо Compose на един VPS все още има смисъл

Хората скачат към Kubernetes, защото е модерно, после прекарват уикенда в YAML за един сайт. Compose остава четим. Версионирате файла, документирате променливите на средата и можете да видите diff на промяна преди да я приложите. Изолацията стига: компрометиран контейнер на приложението не трябва да държи UNIX сокета на базата, ако сте ползвали мрежи и потребители с минимални права. Лимитите на ресурси спират един изтичане на памет да замрази целия VPS.

  • Един файл описва целия стек
  • Именуваните томове оцеляват пресъздаването на контейнери
  • Вътрешните мрежи на Docker държат Postgres извън публичния интернет
  • restart: unless-stopped покрива повечето рестарти
  • Лесно се копира на втори VPS, когато първият стане тесен

Изисквания

Ползвайте VPS с достатъчно RAM за приложението плюс Postgres. План 2 GB е реалистичен минимум за приложение + база + прокси. Предполага се Ubuntu 24.04. Не инсталирайте Docker от случаен Snap, без да четете какво прави с cgroups; стъпките по-долу ползват официалното apt хранилище на Docker.

  • Ubuntu 22.04/24.04, root или sudo
  • Препоръчват се 2 GB RAM (1 GB само за мънички стекове без Postgres)
  • Домейн към VPS, ако искате автоматичен HTTPS
  • Git или друг начин да копирате проекта на сървъра

Стъпка 1. Инсталирайте Docker Engine и Compose v2

Compose v2 е приставка, извиквана като docker compose (с интервал), не старият python двоичен docker-compose. Инсталирайте и двете от хранилището на Docker, за да получавате текущи кръпки по сигурността.

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

Стъпка 2. Потребител за разгръщане и подредба на каталози

Compose като root работи, но отделен потребител в групата docker е по-чисто. Слагайте проекта в /opt или /srv, не в /root, за да са очевидни копията и правата. Никога не давайте световен запис в каталога с .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

Стъпка 3. Напишете файл Compose с мисъл за продукция

Файлът по-долу е шаблон. Сменете образа на приложението със своя. Postgres не се публикува на 0.0.0.0:5432 — навън е само Caddy на 80 и 443. Проверките за здраве спират обратния прокси да праща трафик към приложение, което още мигрира базата. Фиксирайте таговете на образите; latest е как стават изненадващи счупвания.

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:

Стъпка 4. Тайни, .env и Caddyfile

Паролите сложете в .env на сървъра, chmod 600, и никога не комитвайте този файл. Генерирайте пароли с openssl rand -base64 32. Caddyfile трябва само да reverse-proxy към името на услугата на приложението в мрежата 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

Стъпка 5. Защитна стена, логове и лимити на ресурси

UFW трябва да разрешава само 22, 80 и 443. Docker понякога заобикаля UFW за публикувани портове; ако ви трябва строга защитна стена на хоста, вижте текущото взаимодействие Docker + UFW за вашата Ubuntu или публикувайте портове само на 127.0.0.1 и сложете Caddy на хоста. Задайте лимити за памет в Compose, за да не изяде Postgres целия VPS.

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

Стъпка 6. Обновявания и връщане назад

Скучното обновяване, което работи: дръпнете нови образи, up -d, гледайте проверките за здраве, дръжте предишния таг в git, за да се върнете. Не правете docker compose down на бойна база, освен ако искате да спрете приема на трафик. down -v трие томове — това е изтриване, не рестарт.

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

Архивирайте именувани томове

Снимка на VPS е добра. Логически дъмп на Postgres е по-добър, ако възстановявате на друга машина. Планирайте го с cron. Тествайте възстановяване веднъж, иначе нямате копия — имате файлове, на които се надявате, че са копия.

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

Отстраняване на проблеми

Четете docker compose ps и docker compose logs услуга. Провалите при дърпане на образ обикновено са лимити на ghcr/Docker Hub или частен образ без вход. 502 от Caddy значи, че приложението не е здраво или името на хоста на проксито е грешно (от контейнера Caddy ползвайте името на услугата Compose, не localhost). Грешки за права на томове често значат, че образът върви като uid 1000, а каталогът на хоста е на root.

  • compose: command not found — инсталирахте старото име на двоичния файл; ползвайте docker compose
  • port is already allocated — нещо друго държи 80/443 (Apache, Nginx, друг Caddy)
  • database connection refused — приложението стартира преди Postgres да е готов; healthcheck + depends_on с condition
  • дискът е пълен — docker system df, после внимателен prune на неизползвани образи
  • permission denied на docker.sock — потребителят не е в групата docker или ви трябва нова сесия за вход

Сигурност

Не публикувайте портове на базата. Не включвайте privileged: true. Не монтирайте /var/run/docker.sock в контейнер на приложение, освен ако нарочно пишете мениджър на контейнери. Дръжте двигателя обновен. Сканирайте собствените образи, ако ги строите. .env на диска все още е секретен файл — ограничете кой може да ssh към кутията.

  • Няма публични 5432 / 3306 / 6379
  • Фиксирайте digest на образи или поне неизменяеми тагове
  • chmod 600 .env
  • Unattended-upgrades на хостовата ОС все още имат значение
  • Един проект Compose на приложение намалява радиуса на поражение

Съвети

  • compose.yaml в git; .env.example без истински тайни
  • Профили за незадължителни работници (docker compose --profile workers up -d)
  • Гледайте docker stats, когато оразмерявате VPS
  • Bind mount на изходен код не е продуктовият шаблон — изпечете образ
  • Когато стекът нарасне до няколко VPS, тогава гледайте оркестрация — не преди това

Почти продуктов Compose на един VPS е: официални пакети Docker, заключен .env, YAML, който не публикува базата, проверки за здраве, обратен прокси на 80/443, UFW, тагове на образи, към които можете да се върнете, и дъмпове на Postgres, които поне веднъж сте възстановявали. Започнете от шаблона в тази статия, сменете образа на приложението и се отнасяйте към docker compose down -v като към разрушителна команда със същото уважение като към rm -rf.