Перейти к содержимому
VPS и серверы · 6 мин

Docker и Docker Compose на VPS — и ловушка с фаерволом

Почти любой самохостинг сегодня поставляется в виде docker-compose.yml. Поставить Docker на VPS — одна команда. А чтобы он работал безопасно, нужно знать ещё одну вещь, которую пропускают почти все инструкции: опубликованные порты Docker обходят ufw.

Какой тариф

Сам Docker лёгкий, а вот то, что вы в нём запускаете, — нет. Ориентир такой: одно приложение с базой помещается на Pulse (2 vCPU, 2 ГБ), связка из трёх-пяти сервисов — на Drive (4 ГБ, $15 в месяц) или Forge (8 ГБ, $22 в месяц). Образы и тома тихо растут, так что следите не только за памятью, но и за диском.

1. Установка

Официальный скрипт ставит Docker Engine и плагин Compose из репозитория Docker:

curl -fsSL https://get.docker.com | sh
docker run --rm hello-world
docker compose version

Работаете под обычным пользователем — выполните usermod -aG docker имя и перелогиньтесь. Помните: членство в группе docker по сути равно root на сервере.

2. Настоящий стек

/opt/app/docker-compose.yml: приложение и PostgreSQL. Приложение опубликовано только на localhost — к нему будет проксировать nginx на самом сервере.

services:
  db:
    image: postgres:16
    restart: unless-stopped
    environment:
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    volumes:
      - db:/var/lib/postgresql/data

  app:
    image: ghcr.io/example/app:latest
    restart: unless-stopped
    depends_on: [db]
    environment:
      DATABASE_URL: postgres://postgres:${DB_PASSWORD}@db:5432/postgres
    ports:
      - "127.0.0.1:8080:8080"

volumes:
  db:
cd /opt/app && echo 'DB_PASSWORD=смените-пароль' > .env && chmod 600 .env
docker compose up -d
docker compose ps
docker compose logs -f app

restart: unless-stopped поднимает контейнеры после падения и после перезагрузки сервера.

3. Ловушка с фаерволом

На этом каждую неделю кто-нибудь попадается. Вы включаете ufw, разрешаете только SSH и HTTPS, публикуете контейнер с ports: - "5432:5432" — и база открыта всему интернету. Docker пишет свои правила iptables, и они срабатывают раньше правил ufw.

Две привычки спасают от этого:

  • Публикуйте на localhost (127.0.0.1:8080:8080) всё, к чему должны ходить только nginx или вы через SSH-туннель.
  • Порты баз данных не публикуйте вообще. Контейнеры одного compose-проекта видят друг друга по имени сервиса (db:5432 в примере выше) без всяких ports:.

Проверьте снаружи, что реально открыто: nmap -Pn <IP сервера> со своего компьютера.

4. Логи и диск

По умолчанию Docker хранит логи контейнеров вечно, и болтливое приложение может забить диск за пару недель. Ограничьте их в /etc/docker/daemon.json:

{ "log-driver": "json-file", "log-opts": { "max-size": "20m", "max-file": "3" } }
systemctl restart docker
docker system df            # что занимает место
docker image prune -a       # удалить неиспользуемые образы

Ограничение действует для контейнеров, созданных после изменения, поэтому пересоздайте свои: docker compose up -d --force-recreate.

Чего Docker за вас не сделает

Не сохранит тома. docker compose down -v удаляет их, как и неосторожный prune. Для PostgreSQL минимум — ночной дамп docker compose exec -T db pg_dump -U postgres postgres > dump.sql с копированием за пределы сервера.

Docker и Docker Compose на VPS — и ловушка с фаерволом · dedisrv