Docker на VPS запускает приложения в изолированных контейнерах: каждое со своими библиотеками и версией окружения, без конфликтов на одном сервере. Установка Docker на Ubuntu 24.04 или Debian 12 занимает несколько минут, если брать пакеты из официального репозитория, а не из старых пакетов дистрибутива. Ниже показаны установка, первый контейнер, пример docker compose с политикой перезапуска, volume, логами, обновлением образов и очисткой диска.

Для понимания дальнейших команд полезно различать три понятия. Образ (image) это шаблон с файлами и окружением, например nginx:stable. Контейнер (container) это запущенный экземпляр образа. Volume (том) это место для данных, которое живет независимо от контейнера и переживает его пересоздание.

Что понадобится

  • VPS на Ubuntu 24.04 или Debian 12 с доступом по SSH. Если вы еще не подключались, см. как подключиться к VPS по SSH.
  • Пользователь с правами sudo.
  • Минимум 2 ГБ оперативной памяти и 20 ГБ диска для нескольких контейнеров. Для одного небольшого сервиса хватает меньше, но образы занимают место.

Шаг 1. Установка из официального репозитория

Пакет docker.io из стандартного репозитория Ubuntu или Debian старее версии, которую выпускает сам Docker. Официальный репозиторий дает свежие обновления безопасности и плагин docker compose. Команды ниже для Ubuntu, на Debian замените в адресе ubuntu на debian. Актуальные команды всегда есть на странице docs.docker.com, сверьтесь с ней при установке.

sudo apt update
sudo apt install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo 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" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Проверьте установку. Команда hello-world скачивает крошечный тестовый образ, запускает его и печатает приветствие. Если вы видите текст Hello from Docker!, демон работает.

sudo systemctl status docker --no-pager
sudo docker run --rm hello-world
docker compose version

Запуск без sudo: группа docker

Чтобы не писать sudo перед каждой командой, добавьте пользователя в группу docker. Затем выйдите из SSH-сессии и войдите снова, иначе группа не применится.

sudo usermod -aG docker $USER

Проверить результат можно командой groups после повторного входа: в списке должна появиться группа docker.

Шаг 2. Первый контейнер

Запустите веб-сервер Nginx в контейнере. Флаг -d отправляет его в фон, --name задает понятное имя, -p 8080:80 пробрасывает порт 80 контейнера на порт 8080 сервера, а --restart unless-stopped поднимает контейнер после перезагрузки сервера, если вы сами его не останавливали.

docker run -d --name web -p 8080:80 --restart unless-stopped nginx:stable
docker ps
curl -I http://localhost:8080

Важно: Docker сам пишет правила iptables для опубликованных портов, и эти правила обходят ufw. Если вы опубликовали порт через -p, он может стать доступен извне, даже когда ufw его не разрешал. Публикуйте наружу только порты, которые действительно нужны, и для внутренних сервисов используйте привязку к localhost, например -p 127.0.0.1:5432:5432.

Шаг 3. Docker Compose: сервис, volume и политика перезапуска

Запускать контейнеры командами удобно для теста, но для постоянного сервиса лучше описать его в файле docker-compose.yaml. Так конфигурация хранится в одном месте, ее можно поменять и пересоздать одной командой. Создайте каталог проекта и файл, в котором приложение и база данных работают вместе.

mkdir -p ~/app/site && cd ~/app
echo 'Приложение в Docker' > site/index.html
nano docker-compose.yaml
services:
  web:
    image: nginx:stable
    restart: unless-stopped
    ports:
      - "8080:80"
    volumes:
      - ./site:/usr/share/nginx/html:ro
    depends_on:
      - db

  db:
    image: postgres:17
    restart: unless-stopped
    environment:
      POSTGRES_DB: app
      POSTGRES_USER: app
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - pgdata:/var/lib/postgresql/data

volumes:
  pgdata:

Разберем ключевые строки. restart: unless-stopped задает политику перезапуска, при которой контейнер поднимается после сбоя и после перезагрузки сервера. ./site:/usr/share/nginx/html:ro монтирует каталог с файлами с хоста в контейнер только для чтения. Volume pgdata хранит данные базы отдельно от контейнера, поэтому пересоздание контейнера не удаляет данные. Пароль не пишется в файл: Compose берет значение переменной POSTGRES_PASSWORD из файла .env в том же каталоге.

echo "POSTGRES_PASSWORD=$(openssl rand -base64 24)" > .env
chmod 600 .env
docker compose up -d
docker compose ps

Файл .env не нужно коммитить в Git. Если в каталоге есть репозиторий, добавьте его в .gitignore.

Compose читает файл .env автоматически, если он лежит рядом с docker-compose.yaml, и подставляет его значения в переменные вида ${ИМЯ}. Так пароли и порты остаются вне конфигурации, и один и тот же файл compose можно использовать на разных серверах с разными значениями. Команда docker compose down останавливает и удаляет контейнеры проекта, но volume сохраняет, если не добавить флаг -v. Чтобы перезапустить один сервис, используйте docker compose restart web: это быстрее полного пересоздания.

Логи и обновление образов

Логи контейнеров доступны без входа внутрь них. Команда docker compose logs объединяет вывод всех сервисов проекта, а -f включает просмотр в реальном времени.

docker compose logs -f web
docker compose logs --tail 100 db
docker logs --tail 50 web

Чтобы обновить образы до новых версий, скачайте их и пересоздайте контейнеры. Данные в volume сохранятся.

docker compose pull
docker compose up -d
docker image prune -f

Для версий образов используйте теги вроде stable или 17, а не latest. Тег latest может однажды обновиться до версии с несовместимыми изменениями, и сервис сломается без вашего участия.

По умолчанию Docker хранит логи контейнеров в файлах формата json-file без ограничения размера. На долгоживущем сервере они способны заполнить диск, поэтому задайте ротацию в /etc/docker/daemon.json и перезапустите демон. Настройка применяется к контейнерам, которые создаются после изменения.

{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  }
}
sudo systemctl restart docker

Очистка диска

Старые образы, остановленные контейнеры и кэш сборки со временем занимают много места. Сначала посмотрите, что именно занимает диск, затем удалите лишнее.

docker system df
docker image prune -f
docker builder prune -f
docker system prune -f

Команда docker system df показывает, сколько места занимают образы, контейнеры, volume и кэш сборки. Если основное место занимают volume, сначала выясните, какому сервису они принадлежат, и только потом удаляйте что-либо. Кэш сборки удаляется безопасно, а образы можно скачать заново.

Итоги

Docker на VPS дает повторяемое окружение: установка из официального репозитория, контейнер с политикой перезапуска, docker compose с volume и отдельным файлом .env, а также регулярные обновления образов и очистка диска. Не забывайте про публикацию портов и группу docker: это два места, где ошибка стоит дороже всего. Если сервис будет принимать посетителей по домену, добавьте к нему HTTPS по инструкции Nginx и SSL на VPS, а для выбора ресурсов сервера посмотрите тарифы VPS.