Назад да блога
Жнівень 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’ам да прымянення. Ізаляцыі хапае: скампраметаваны кантэйнер праграмы не павінен трымаць сокет базы, калі сеткі падзеленыя і правы мінімальныя. Ліміты памяці не даюць адной уцечцы пакласці ўвесь VPS.

  • Увесь стэк апісаны адным файлам
  • Іменаваныя тамы перажываюць перастварэнне кантэйнераў
  • Унутраныя сеткі Docker не выстаўляюць Postgres у інтэрнэт
  • restart: unless-stopped закрывае большасць перазагрузак
  • Лёгка скапіяваць на другі VPS, калі першы стане цесным

Што патрэбна

RAM павінна хапаць на праграму плюс Postgres. Рэалістычны мінімум — тарыф на 2 ГБ, калі ёсць база і проксі. Ніжэй — Ubuntu 24.04. Не стаўце Docker з выпадковага Snap, не разабраўшыся з cgroups; крокі выкарыстоўваюць афіцыйны рэпазіторый Docker.

  • Ubuntu 22.04/24.04, root або sudo
  • Рэкамендуецца 2 ГБ RAM (1 ГБ толькі для крыхічных стэкаў без Postgres)
  • Дамен на IP VPS, калі патрэбны аўтаматычны HTTPS
  • Git або іншы спосаб даставіць праект на сервер

Крок 1. Усталюйце Docker Engine і Compose v2

Compose v2 — плагін, каманда docker compose (з прабелам), а не стары двайковы файл docker-compose на Python. Стаўце абодва кампаненты з рэпазіторыя 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, у Git не каміцьце. Пароль: 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, паглядзець праверкі жывасці, стары тэг трымаць у 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 служба. Вобразы не качаюцца — ліміты Docker Hub/ghcr або прыватны вобраз без docker login. 502 у Caddy — праграма не прайшла healthcheck або імя хоста проксі нявернае (з кантэйнера 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
  • Аўтаабнаўленні пакетаў АС усё яшчэ патрэбныя
  • Адзін праект Compose на праграму памяншае радыус паражэння

Парады

  • compose.yaml у git; .env.example без сапраўдных сакрэтаў
  • Профілі для неабавязковых воркераў (docker compose --profile workers up -d)
  • docker stats дапамагае зразумець, які тарыф VPS браць
  • Прымантаваць зыходнікі як bind у баі — не той шаблон, збірайце вобраз
  • Калі стэк раз’едзецца на некалькі VPS, тады і глядзіце аркестрацыю

Баявы Compose на адным VPS — гэта афіцыйныя пакеты Docker, закрыты .env, YAML без публікацыі базы, праверкі жывасці, проксі на 80/443, UFW, тэгі вобразаў для адкату і дампы Postgres, якія вы хоць раз аднаўлялі. Вазьміце шаблон з артыкула, падстаўце свой вобраз і ставіцеся да docker compose down -v як да rm -rf.