Як разгортваць праграмы праз Docker Compose на VPS
Практычнае кіраўніцтва для продакшэну па Docker Compose v2 на Ubuntu: усталёўка рухавіка, файл Compose з сеткамі і тамамі, сакрэты, зваротны проксі, абнаўленні без панікі і рэзервовыя копіі іменаваных тамоў.

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, каб прыходзілі абнаўленні бяспекі.
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 не павінен быць даступны ўсім на запіс.
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 — крыніца раптоўных паломак.
# /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.
# /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.
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 выдаляе тамы — гэта сціранне, а не перазапуск.
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. Адзін раз аднавіце копію, інакш у вас не копіі, а файлы, на якія вы спадзяецеся.
# 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.