Повратак на блог
Август 19, 2026Водичи

Како распоређивати апликације са Docker Compose на VPS

Практичан продукциони водич за Docker Compose v2 на Ubuntu: инсталација механизма, Compose датотека са мрежама и волуменима, тајне, реверзни прокси испред, ажурирања без панике и резервне копије именованих волумена.

Како распоређивати апликације са Docker Compose на VPS

Docker Compose је уобичајен начин да на једном VPS водите мали стек: веб апликација, база, Redis кеш и реверзни прокси у једној YAML датотеци. То није Kubernetes. Нећете добити failover на више чворова. Добићете поновљиво, прегледиво подешавање које за минуте подигнете на новом Hiddence VPS — управо оно што већини споредних пројеката и малих производа треба.

Овај чланак претпоставља да желите нешто ближе продукцији од docker run у петак увече. Инсталираћемо Docker Engine и Compose додатак, направити корисника за распоређивање без root у свакодневици, написати Compose датотеку са healthcheck-овима и политиком рестарта, тајне држати ван 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; кораци испод користе званични Docker apt репозиторијум.

  • 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. Healthcheck-ови спречавају реверзни прокси да шаље саобраћај апликацији која још мигрира базу. Закачите ознаке имиџа; 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 треба само да реверзно проксира на име услуге апликације у 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, гледајте healthcheck-ове, држите претходну ознаку у 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 је добар. Логички dump 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 значи да апликација није здрава или је hostname проксија погрешан (из 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 који не објављује базу, healthcheck-ови, реверзни прокси на 80/443, UFW, ознаке имиџа на које можете да се вратите и dump-ови Postgres-а које сте бар једном вратили. Почните од шаблона у овом чланку, замените имиџ апликације и docker compose down -v третирајте као деструктивну команду са истим поштовањем као rm -rf.