bolt Valebyte VPS от $4/мес — NVMe, запуск за 60 секунд.

Получить VPS arrow_forward
eco Начальный Туториал

Docker на VPS с нуля: установка, compose, обновления и бэкап томов

calendar_month Sep 14, 2026 schedule 23 мин. чтения visibility 11 просмотров
Docker на VPS с нуля: установка, compose, обновления и бэкап томов
info

Нужен сервер для этого гайда? Мы предлагаем выделенные серверы и VPS в 50+ странах с мгновенной настройкой.

Нужен сервер для этого гайда?

Разверните VPS или выделенный сервер за минуты.

Docker на VPS с нуля: установка, Compose, обновления и бэкап томов

TL;DR

Docker на VPS позволяет запускать сайты, базы данных, ботов, VPN, Git-сервисы и другие приложения в изолированных контейнерах, управляя всей инфраструктурой через файлы Docker Compose. В этом руководстве вы подготовите Ubuntu-сервер, установите Docker Engine и Compose, развернёте тестовое приложение с HTTPS, настроите автоматические обновления и резервное копирование Docker volumes.

  • Для большинства небольших проектов достаточно VPS с 2 vCPU, 4 ГБ RAM и 50–80 ГБ NVMe-диска.
  • Docker Engine устанавливается из официального APT-репозитория, а Compose используется как встроенный плагин docker compose.
  • Все настройки приложения, сети, тома и зависимости описываются в compose.yaml.
  • Секреты хранятся в файле .env, который не нужно добавлять в Git.
  • Для публичных сервисов HTTPS удобно выдавать через Caddy с автоматическими сертификатами Let’s Encrypt.
  • Бэкапировать нужно не контейнеры, а данные: Docker volumes, дампы баз данных, конфиги и файл .env.

Что мы настраиваем и зачем

Docker — это платформа для запуска приложений в контейнерах. Контейнер включает приложение, его библиотеки и системные зависимости, но использует ядро операционной системы хоста. В отличие от виртуальной машины, контейнер не требует отдельной гостевой ОС, быстро запускается и потребляет меньше памяти.

На практике Docker на VPS нужен, когда сервер должен запускать несколько сервисов без ручной установки зависимостей в систему. Например, можно одновременно разместить WordPress, PostgreSQL, Redis, Mattermost, n8n, Gitea, Vaultwarden, API на Python или Node.js, а также reverse proxy с TLS-сертификатами. Каждый сервис получает отдельный контейнер, сеть и постоянное хранилище.

В этом туториале будет развернут базовый, но приближенный к реальному production-стенд:

  • Docker Engine на Ubuntu 24.04 LTS;
  • Docker Compose v2 для управления стеком;
  • демонстрационное веб-приложение Nginx;
  • Caddy как reverse proxy и автоматический менеджер HTTPS-сертификатов;
  • именованный Docker volume для постоянных данных;
  • отдельная внутренняя Docker-сеть;
  • скрипт резервного копирования Docker volumes;
  • план безопасного обновления образов и контейнеров.

Что получится в итоге

После выполнения шагов у вас будет сервер, на котором приложения описываются декларативно. Вместо длинной последовательности команд вы храните конфигурацию в файлах compose.yaml, Caddyfile и .env. Перенос сервиса на другой VPS сводится к копированию этих файлов, восстановлению резервной копии томов и запуску docker compose up -d.

Такой подход особенно полезен, если вы запускаете саморазмещаемые сервисы. Контейнеры можно останавливать, удалять и пересоздавать без потери пользовательских данных: при условии, что данные лежат в Docker volumes или смонтированных каталогах хоста, а не внутри файловой системы контейнера.

Docker Compose: почему он нужен

Один контейнер можно запустить командой docker run, но для постоянного сервиса это быстро становится неудобным. Нужно помнить параметры портов, сетей, переменных окружения, политик перезапуска, томов и ограничений ресурсов. Docker Compose сохраняет всё это в YAML-файле.

Например, приложение и база данных можно описать в одном файле. Одной командой Compose создаст сеть, подготовит volumes, загрузит образы и запустит сервисы в правильном порядке. Это также делает конфигурацию проверяемой в Git и понятной другому администратору.

Self-hosted на VPS или managed-сервис

Критерий Self-hosted Docker на VPS Managed-платформа
Контроль над системой Полный: ОС, сеть, данные, версии образов Ограничен возможностями провайдера
Обслуживание Администратор отвечает за обновления и бэкапы Часть задач выполняется платформой
Стоимость нескольких сервисов Часто выгоднее на одном VPS Может расти с каждым сервисом и базой
Гибкость Можно запускать почти любой OCI-образ Зависит от поддерживаемого стека
Отказоустойчивость Нужно проектировать самостоятельно Часто включена в более дорогие тарифы

Самостоятельный Docker на VPS подходит, если вы готовы выполнять базовые операции Linux: обновлять пакеты, проверять журналы, тестировать обновления и хранить независимые резервные копии. Для одного критичного приложения с требованиями к высокой доступности может быть разумнее использовать управляемую базу данных или облачную платформу. Но для небольших команд, MVP, личных сервисов и нескольких связанных приложений Docker Compose на VPS остаётся простым и контролируемым вариантом.

Какой VPS-конфиг нужен под эту задачу

Docker сам по себе почти не определяет требования к серверу: ресурсы потребляют контейнеры. Минимальная конфигурация зависит от числа приложений, типа базы данных, объёма загружаемых файлов и ожидаемой посещаемости. Главная ошибка — выбирать VPS только по числу ядер и забывать о RAM, IOPS диска и резервном месте для образов и бэкапов.

Сценарий CPU RAM NVMe-диск Сеть
Тесты, бот, один небольшой сайт 1 vCPU 2 ГБ 25–40 ГБ 100 Мбит/с
2–5 небольших контейнеров, PostgreSQL, reverse proxy 2 vCPU 4 ГБ 50–80 ГБ 100 Мбит/с–1 Гбит/с
CRM, Mattermost, n8n, несколько веб-сервисов 4 vCPU 8 ГБ 120–200 ГБ 1 Гбит/с
Нагруженная БД, сборки CI, много пользователей 8 vCPU и выше 16 ГБ и выше 300 ГБ и выше 1 Гбит/с и выше

Для этого руководства и большинства первых Docker-проектов берите 2 vCPU, 4 ГБ RAM, 60–80 ГБ NVMe и канал от 100 Мбит/с. Такой запас позволяет запустить reverse proxy, один-два веб-сервиса, PostgreSQL или MariaDB и оставить место для образов. В качестве одного из вариантов можно взять VPS с указанными характеристиками, но перед заказом проверьте, что в тариф включены публичный IPv4-адрес, доступ по SSH и достаточный объём диска.

Почему Docker быстро занимает диск

Образы состоят из слоёв. После нескольких обновлений и экспериментов на сервере остаются неиспользуемые образы, build cache и остановленные контейнеры. Кроме того, базы данных и пользовательские файлы хранятся в volumes. Не планируйте использовать 100% диска: оставляйте минимум 20–30% свободного пространства, иначе PostgreSQL, журнал Docker или обновление ОС могут завершиться ошибкой.

Проверять использование места нужно регулярно:

# Показывает использование диска Docker: образы, контейнеры, volumes и build cache
docker system df

# Показывает свободное место в файловой системе VPS
df -h

Когда нужен dedicated, а не VPS

VPS достаточно для большинства веб-приложений, внутренних сервисов, API и небольших баз данных. Dedicated-сервер становится оправданным, когда требуется гарантированная производительность CPU и диска, очень большой объём локального хранилища, высокая нагрузка базы данных, интенсивная компиляция в CI или десятки активно работающих контейнеров.

Также dedicated стоит рассматривать для задач с постоянной высокой нагрузкой: видеокодирование, игровые серверы с большим онлайном, индексаторы блокчейна, крупные Elasticsearch-кластеры и базы данных с большим количеством IOPS. Не переходите на выделенный сервер только потому, что Docker работает медленно: сначала проверьте лимиты RAM, swap, дисковую задержку, настройки БД и реальные показатели CPU.

Как выбрать локацию

Локация влияет на задержку до пользователей, требования к хранению персональных данных и скорость соединения с внешними сервисами. Сервер для команды из Европы обычно размещают ближе к участникам команды; API для клиентов из конкретного региона — в том же регионе. Для private-сервисов ориентируйтесь на собственную задержку и юридические требования.

Перед публикацией сайта убедитесь, что домен можно направить A-записью на IPv4-адрес VPS. Для автоматической выдачи сертификатов Caddy сервер должен быть доступен из интернета по портам 80 и 443. Если эти порты блокирует внешняя сеть или DNS указывает на другой адрес, HTTPS не будет выпущен.

Подготовка сервера

Ниже используется Ubuntu Server 24.04 LTS — стабильный вариант для Docker в 2026 году. Инструкция также близка для Ubuntu 22.04 LTS, Debian 12 и Debian 13, но названия пакетов и правила firewall могут немного отличаться. Подключайтесь к новому серверу от имени root только для первоначальной настройки.

Создайте обычного пользователя с sudo

Не работайте постоянно под root. Создайте администратора, добавьте его в группу sudo и затем используйте этот аккаунт для обслуживания сервера. В примерах используется имя deploy; замените его на своё.

# Создаёт пользователя deploy с домашним каталогом и запрашивает пароль
adduser deploy

# Даёт пользователю право выполнять административные команды через sudo
usermod -aG sudo deploy

Настройте SSH-ключи

На локальном компьютере сгенерируйте ключ Ed25519, если у вас его ещё нет. Не передавайте приватный файл ключа на сервер и не публикуйте его содержимое. Пароль для ключа защищает его при потере ноутбука.

# Выполняется на локальном компьютере: создаёт пару SSH-ключей Ed25519
ssh-keygen -t ed25519 -a 100 -C "admin@my-laptop"

# Копирует публичный ключ в аккаунт deploy на VPS
ssh-copy-id deploy@SERVER_IP

# Проверяет вход по ключу; после этого пароль root больше не нужен для обычной работы
ssh deploy@SERVER_IP

Откройте второе SSH-подключение и убедитесь, что вход по ключу работает, прежде чем запрещать парольную авторизацию. Иначе можно потерять доступ к серверу.

# Открывает настройки SSH-сервера
sudo nano /etc/ssh/sshd_config.d/99-hardening.conf

Добавьте в открытый файл следующие параметры:

PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
X11Forwarding no
MaxAuthTries 3
# Проверяет синтаксис конфигурации SSH перед перезапуском
sudo sshd -t

# Применяет новую конфигурацию SSH без перезагрузки сервера
sudo systemctl reload ssh

Обновите систему и поставьте базовые утилиты

Сначала обновите индексы пакетов и установите исправления безопасности. После крупных обновлений ядра перезагрузите сервер в удобное окно. Для проверки необходимости перезагрузки Ubuntu создаёт файл /var/run/reboot-required.

# Обновляет пакеты Ubuntu до актуальных версий
sudo apt update && sudo apt full-upgrade -y

# Устанавливает утилиты для загрузки ключей, диагностики и архивов
sudo apt install -y ca-certificates curl gnupg lsb-release nano \
  unzip jq htop ncdu rsync cron fail2ban ufw

# Показывает, нужна ли перезагрузка после обновления ядра или библиотек
test -f /var/run/reboot-required && cat /var/run/reboot-required || echo "Reboot not required"

Настройте firewall

UFW закрывает входящие подключения, кроме явно разрешённых. Сначала разрешите SSH, затем HTTP и HTTPS. Если вы используете нестандартный SSH-порт, разрешите именно его до активации firewall.

# Разрешает SSH, чтобы не потерять удалённый доступ
sudo ufw allow OpenSSH

# Разрешает публичный HTTP и HTTPS для Caddy и веб-приложений
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

# Включает firewall и показывает активные правила
sudo ufw enable
sudo ufw status verbose

Не открывайте порт базы данных, например 5432 PostgreSQL или 3306 MariaDB, в интернет без реальной необходимости. Контейнеры в одной Docker-сети могут общаться по внутреннему имени сервиса. Публикуйте наружу только reverse proxy и те сервисы, которые действительно должны быть доступны пользователям.

Включите Fail2ban

Fail2ban читает журналы SSH и временно блокирует IP-адреса с повторяющимися ошибками входа. Это не заменяет SSH-ключи, но снижает шум от автоматических сканеров.

# Запускает Fail2ban сейчас и при каждой загрузке VPS
sudo systemctl enable --now fail2ban

# Проверяет, что SSH-jail активен и показывает заблокированные адреса
sudo fail2ban-client status sshd

Docker напрямую управляет правилами iptables/nftables для опубликованных портов. Поэтому не рассчитывайте только на UFW как на абсолютную изоляцию контейнеров. Основная защита — не публиковать лишние порты через Compose, использовать отдельные сети и регулярно обновлять образы.

Установка ПО — пошагово

На Ubuntu не устанавливайте пакет docker.io из стандартного репозитория, если хотите получать актуальные версии Docker Engine. Используйте официальный репозиторий Docker. На 2026 год актуальная ветка Docker Engine — 29.x, а Docker Compose распространяется как плагин v2 и запускается командой docker compose.

Удалите конфликтующие пакеты

Если на VPS уже был установлен Docker из системного репозитория или тестовой конфигурации, удалите конфликтующие пакеты. Команда не удаляет ваши Docker volumes автоматически, но на новом сервере это обычно не имеет значения.

# Удаляет старые или конфликтующие пакеты Docker, если они установлены
sudo apt remove -y docker.io docker-compose docker-compose-v2 \
  docker-doc podman-docker containerd runc 2>/dev/null || true

Добавьте официальный GPG-ключ и репозиторий Docker

APT проверяет подпись пакетов ключом Docker. Команды ниже создают защищённый каталог для ключей, загружают ключ, определяют архитектуру и кодовое имя Ubuntu автоматически.

# Создаёт каталог для ключей сторонних APT-репозиториев
sudo install -m 0755 -d /etc/apt/keyrings

# Загружает официальный GPG-ключ репозитория Docker
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
  sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

# Делает ключ читаемым для APT
sudo chmod a+r /etc/apt/keyrings/docker.gpg

# Добавляет официальный stable-репозиторий Docker для текущей Ubuntu
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
  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

Установите Docker Engine, Buildx и Compose

Устанавливайте компоненты одним набором. Пакет docker-buildx-plugin нужен для современных сборок образов, а docker-compose-plugin добавляет подкоманду docker compose. Пакет containerd.io содержит runtime контейнеров.

# Устанавливает Docker Engine 29.x, CLI, containerd, Buildx и Compose v2
sudo apt install -y docker-ce docker-ce-cli containerd.io \
  docker-buildx-plugin docker-compose-plugin

# Включает Docker и containerd в автозапуске
sudo systemctl enable --now docker containerd

# Проверяет установленные версии движка, Compose и Buildx
docker version
docker compose version
docker buildx version

Разрешите пользователю работать с Docker без sudo

По умолчанию сокет /var/run/docker.sock доступен root. Добавление пользователя в группу docker позволяет управлять контейнерами без sudo. Важно понимать: участник этой группы фактически получает root-доступ к серверу, так как может смонтировать системные каталоги в контейнер. Не добавляйте туда непривилегированных пользователей.

# Добавляет текущего пользователя в группу docker
sudo usermod -aG docker "$USER"

# Завершите SSH-сеанс и подключитесь снова, затем проверьте доступ к Docker
exit

После повторного подключения выполните тест. Контейнер hello-world будет скачан из Docker Hub, запущен и затем завершится.

# Проверяет, что Docker работает без sudo и умеет загружать образы
docker run --rm hello-world

# Показывает состояние службы Docker
systemctl status docker --no-pager

Проверьте сетевые параметры и включите log rotation

Контейнеры по умолчанию пишут логи в JSON-файлы в каталоге /var/lib/docker/containers. Без ротации один ошибочный сервис может заполнить весь диск. Настройте разумный лимит для новых контейнеров. Уже созданные контейнеры сохранят старую настройку до пересоздания.

# Создаёт конфигурацию Docker daemon с ротацией container logs
sudo tee /etc/docker/daemon.json > /dev/null <<'EOF'
{
  "log-driver": "local",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  },
  "live-restore": true
}
EOF

# Проверяет JSON и перезапускает Docker для применения конфигурации
sudo jq empty /etc/docker/daemon.json
sudo systemctl restart docker

# Проверяет, что Docker снова отвечает после перезапуска
docker info --format '{{.ServerVersion}}'

Параметр live-restore помогает уже запущенным контейнерам пережить перезапуск демона Docker. Это не освобождает от необходимости тестировать обновления: сетевые изменения, обновление ядра или несовместимый образ всё равно могут потребовать технического окна.

Создайте рабочую структуру проектов

Не храните production Compose-проекты в домашней папке случайного пользователя или в /tmp. Удобное место — /opt/stacks. Ограничьте доступ: конфиги часто содержат домены, пароли и ключи API.

# Создаёт общий каталог для Compose-проектов
sudo mkdir -p /opt/stacks/demo-app

# Передаёт каталог пользователю deploy; замените имя при необходимости
sudo chown -R deploy:deploy /opt/stacks

# Устанавливает безопасные права на каталог проектов
chmod 750 /opt/stacks /opt/stacks/demo-app

# Переходит в каталог будущего Docker Compose-стека
cd /opt/stacks/demo-app

Конфигурация Docker, Compose и HTTPS

Теперь создадим сервис, доступный по доменному имени через HTTPS. Пример использует Nginx как простое приложение и Caddy как reverse proxy. В реальном проекте вместо Nginx может быть ваш API, WordPress, Gitea, Nextcloud или другой контейнер.

Перед запуском создайте DNS A-запись, например app.example.com, указывающую на публичный IPv4 VPS. Заменяйте app.example.com и адрес электронной почты на свои значения. Проверить DNS можно командой dig +short app.example.com: результат должен совпасть с IP сервера.

Создайте файл переменных окружения

Файл .env хранит значения, которые не стоит дублировать в YAML: домен, email для уведомлений и секреты. Установите права 600. Если проект хранится в Git, добавьте .env в .gitignore, а в репозиторий положите только .env.example без реальных секретов.

# Создаёт файл окружения с параметрами конкретного сервера
cat > /opt/stacks/demo-app/.env <<'EOF'
DOMAIN=app.example.com
[email protected]
EOF

# Ограничивает чтение файла текущим владельцем
chmod 600 /opt/stacks/demo-app/.env

# Создаёт шаблон переменных для Git без приватных данных
cat > /opt/stacks/demo-app/.env.example <<'EOF'
DOMAIN=app.example.com
[email protected]
EOF

Подготовьте Caddyfile

Caddy автоматически получает и продлевает TLS-сертификаты через Let’s Encrypt или другой совместимый ACME-центр сертификации. Для этого порты 80 и 443 должны быть доступны снаружи, а DNS должен указывать на сервер. Caddy перенаправит HTTP на HTTPS и передаст запросы в контейнер приложения.

# Создаёт конфигурацию reverse proxy Caddy
cat > /opt/stacks/demo-app/Caddyfile <<'EOF'
{
    email {$ACME_EMAIL}
}

{$DOMAIN} {
    encode zstd gzip

    reverse_proxy app:80

    header {
        -Server
        X-Content-Type-Options "nosniff"
        X-Frame-Options "SAMEORIGIN"
        Referrer-Policy "strict-origin-when-cross-origin"
    }

    log {
        output stdout
        format json
    }
}
EOF

Внутри Docker-сети Caddy обращается к имени app, а не к IP-адресу. Docker DNS автоматически сопоставляет это имя с контейнером сервиса. Порт 80 у Nginx не публикуется наружу: он доступен только reverse proxy.

Создайте compose.yaml

В Compose-файле описаны два сервиса, именованный volume и внутренняя сеть. Образ Caddy хранит сертификаты в volume caddy_data; без него после пересоздания контейнера Caddy потеряет данные ACME и может слишком часто запрашивать новые сертификаты. Volume app_content демонстрирует хранение постоянных файлов приложения.

services:
  caddy:
    image: caddy:2.10-alpine
    container_name: demo-caddy
    restart: unless-stopped
    env_file:
      - .env
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - caddy_data:/data
      - caddy_config:/config
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
    networks:
      - frontend
    healthcheck:
      test: ["CMD", "caddy", "version"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 10s

  app:
    image: nginx:1.28-alpine
    container_name: demo-app
    restart: unless-stopped
    volumes:
      - app_content:/usr/share/nginx/html
    networks:
      - frontend
    healthcheck:
      test: ["CMD-SHELL", "wget -qO- http://127.0.0.1/ >/dev/null 2>&1"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 10s
    deploy:
      resources:
        limits:
          memory: 256M

networks:
  frontend:
    driver: bridge

volumes:
  caddy_data:
  caddy_config:
  app_content:

Поле deploy.resources.limits.memory поддерживается актуальным Docker Compose и ограничивает память контейнера. Ограничения особенно полезны для второстепенных сервисов: один утекший процесс не должен полностью съесть RAM VPS. Для PostgreSQL, Java-приложений и Elasticsearch лимиты нужно подбирать по документации конкретного продукта.

Добавьте начальную страницу в Docker volume

Именованный volume нельзя редактировать обычным редактором на хосте без поиска внутреннего пути Docker. Для первоначальной записи можно запустить временный контейнер Alpine и смонтировать volume в него. Этот приём также полезен для диагностики файлов в volume.

# Переходит в каталог проекта
cd /opt/stacks/demo-app

# Проверяет итоговую конфигурацию после подстановки .env
docker compose config

# Создаёт volume и записывает в него тестовую HTML-страницу
docker run --rm -v demo-app_app_content:/data alpine:3.21 \
  sh -c 'cat > /data/index.html <<EOF
<!doctype html>
<html lang="ru">
<head><meta charset="utf-8"><title>Docker VPS</title></head>
<body><h1>Docker Compose работает</h1><p>Страница отдана через Caddy и HTTPS.</p></body>
</html>
EOF'

Имя volume Compose строит из имени каталога проекта и имени volume. В данном примере каталог называется demo-app, поэтому получается demo-app_app_content. Проверить точное имя можно командой docker volume ls.

Запустите стек и проверьте контейнеры

# Скачивает образы и запускает все сервисы в фоновом режиме
docker compose up -d

# Показывает состояние контейнеров, опубликованные порты и health status
docker compose ps

# Показывает последние 100 строк журналов Caddy
docker compose logs --tail=100 caddy

# Проверяет ответ reverse proxy локально с нужным Host-заголовком
curl -I -H "Host: ${DOMAIN}" http://127.0.0.1

Если DNS уже настроен, проверьте сертификат и код ответа из внешней сети:

# Проверяет HTTPS, цепочку сертификата и HTTP-заголовки домена
curl -Iv "https://${DOMAIN}"

# Проверяет, что порт 443 действительно слушается на VPS
sudo ss -tulpn | grep ':443'

Ожидаемый результат — код HTTP/2 200 или HTTP/1.1 200 OK. При первом запуске выдача сертификата может занять несколько секунд. Смотрите docker compose logs -f caddy, если Caddy сообщает о проблеме валидации домена.

Полезные ежедневные команды Docker Compose

Команда Назначение
docker compose ps Состояние сервисов текущего проекта
docker compose logs -f app Просмотр логов приложения в реальном времени
docker compose restart app Перезапуск одного сервиса
docker compose exec app sh Открытие shell внутри работающего контейнера
docker compose pull Скачивание новых версий образов без запуска
docker compose down Остановка и удаление контейнеров и сети, но не volumes

Не запускайте docker compose down -v на production-проекте, если не понимаете последствия. Флаг -v удаляет именованные volumes, а вместе с ними могут исчезнуть базы данных, загруженные файлы и сертификаты.

Бэкапы и обслуживание

Контейнер — это воспроизводимая часть приложения, его обычно не нужно резервировать. Для восстановления важнее Compose-файлы, секреты, данные volumes, дампы баз данных и пользовательские загрузки. Правило «есть volume — есть бэкап» должно быть обязательным для каждого production-сервиса.

Что именно нужно бэкапить

  • Файлы compose.yaml, Caddyfile, конфиги приложений и шаблоны окружения.
  • Файл .env в зашифрованном или строго защищённом хранилище.
  • Именованные Docker volumes с данными приложений.
  • Логические дампы PostgreSQL, MariaDB/MySQL и других баз данных.
  • Каталоги bind mount, если данные смонтированы с хоста.
  • Список образов и версий, если вы используете собственные сборки.

Для базы данных предпочтительнее делать штатный логический дамп через pg_dump или mariadb-dump, а не копировать её файлы во время работы. Файловый архив горячего volume PostgreSQL может оказаться неконсистентным. Либо останавливайте базу на время файлового бэкапа, либо используйте средства физического резервирования, рекомендованные разработчиками СУБД.

Установите Restic

Restic — удобная утилита для дедуплицированных, зашифрованных и инкрементальных резервных копий. Она поддерживает S3-совместимые хранилища, SFTP, локальный каталог и множество облачных backend-ов. Ниже используется S3-совместимое хранилище; переменные доступа не должны попадать в Git или историю shell.

# Устанавливает Restic из репозитория Ubuntu
sudo apt install -y restic

# Создаёт каталог для скриптов и временных архивов резервных копий
sudo mkdir -p /opt/backups/docker-volumes
sudo chown -R deploy:deploy /opt/backups

# Создаёт файл с параметрами доступа к удалённому S3-хранилищу
nano /opt/backups/restic.env

Заполните /opt/backups/restic.env своими значениями. Используйте отдельный bucket и отдельные ключи доступа, ограниченные только этим bucket. Пароль репозитория Restic должен быть длинным случайным значением и храниться отдельно от сервера: без него восстановить данные невозможно.

export RESTIC_REPOSITORY="s3:https://s3.example.net/docker-vps-backups"
export RESTIC_PASSWORD="CHANGE_TO_A_LONG_RANDOM_RESTIC_PASSWORD"
export AWS_ACCESS_KEY_ID="CHANGE_ME"
export AWS_SECRET_ACCESS_KEY="CHANGE_ME"
# Ограничивает доступ к ключам и паролю владельцем файла
chmod 600 /opt/backups/restic.env

# Загружает переменные и инициализирует зашифрованный Restic-репозиторий один раз
source /opt/backups/restic.env
restic init

Создайте скрипт бэкапа Docker volumes

Скрипт ниже архивирует указанные volumes через временный контейнер Alpine, добавляет конфигурацию проекта и отправляет результат в Restic. Он предназначен для небольших и средних volumes. Для больших баз данных добавьте отдельный дамп базы перед созданием архива.

cat > /opt/backups/backup-demo-app.sh <<'EOF'
#!/usr/bin/env bash
set -euo pipefail

PROJECT_DIR="/opt/stacks/demo-app"
BACKUP_DIR="/opt/backups/docker-volumes"
DATE="$(date +%F_%H-%M-%S)"
ARCHIVE="${BACKUP_DIR}/demo-app-volumes-${DATE}.tar.gz"

source /opt/backups/restic.env

mkdir -p "${BACKUP_DIR}"

docker run --rm \
  -v demo-app_app_content:/volumes/app_content:ro \
  -v demo-app_caddy_data:/volumes/caddy_data:ro \
  -v demo-app_caddy_config:/volumes/caddy_config:ro \
  -v "${BACKUP_DIR}:/backup" \
  alpine:3.21 \
  sh -c "tar -czf /backup/$(basename "${ARCHIVE}") -C /volumes ."

restic backup \
  "${ARCHIVE}" \
  "${PROJECT_DIR}/compose.yaml" \
  "${PROJECT_DIR}/Caddyfile" \
  "${PROJECT_DIR}/.env" \
  --tag docker-demo-app \
  --tag "${DATE}"

rm -f "${ARCHIVE}"

restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune
restic check
EOF

# Делает скрипт исполняемым
chmod 700 /opt/backups/backup-demo-app.sh

# Запускает первую резервную копию вручную и проверяет отсутствие ошибок
/opt/backups/backup-demo-app.sh

В данном примере Caddy volumes тоже сохраняются. Это не критичные бизнес-данные, но их резервирование уменьшает число повторных операций с сертификатами при аварийном восстановлении. Если вы используете PostgreSQL, добавьте перед архивированием команду docker compose exec -T db pg_dump -U USER DATABASE > /opt/backups/db.sql и включите файл дампа в restic backup.

Настройте cron

Сначала выполните скрипт вручную, а затем добавьте его в cron. Не настраивайте автоматизацию, пока не убедились, что первый бэкап виден в удалённом репозитории и команда restic snapshots показывает snapshot.

# Открывает пользовательский crontab пользователя deploy
crontab -e

Добавьте ежедневный запуск в 03:30. Перенаправление вывода сохраняет журнал, по которому можно диагностировать ошибки.

30 3   * /opt/backups/backup-demo-app.sh >> /opt/backups/backup.log 2>&1
# Показывает созданные snapshots в удалённом репозитории
source /opt/backups/restic.env
restic snapshots

# Проверяет содержимое последнего snapshot без восстановления
restic ls latest | head -n 50

Проверяйте восстановление

Бэкап, который ни разу не восстанавливался, нельзя считать проверенным. Хотя бы раз в квартал разверните тестовый VPS или отдельный каталог, восстановите snapshot и убедитесь, что приложение запускается. Не восстанавливайте тестовый архив поверх production volume без остановки сервисов и понимания последствий.

# Восстанавливает последний Restic snapshot в отдельный каталог для проверки
mkdir -p /tmp/restic-restore-test
source /opt/backups/restic.env
restic restore latest --target /tmp/restic-restore-test

# Просматривает восстановленные файлы конфигурации и архив volume
find /tmp/restic-restore-test -maxdepth 3 -type f | sort

Безопасное обновление Docker и контейнеров

Разделяйте обновление ОС, Docker Engine и образов приложений. Не обновляйте всё одновременно в рабочее время. Сначала читайте release notes приложения, делайте свежий бэкап, скачивайте образы, затем пересоздавайте сервисы. Тег latest в production лучше не использовать: фиксируйте мажорную или конкретную проверенную версию, например nginx:1.28-alpine.

# Переходит в каталог Compose-проекта
cd /opt/stacks/demo-app

# Создаёт ручной бэкап перед обновлением
/opt/backups/backup-demo-app.sh

# Загружает новые образы, не меняя работающие контейнеры
docker compose pull

# Показывает, какие образы теперь используются и их размеры
docker compose images

# Пересоздаёт сервисы только при изменении образа или конфигурации
docker compose up -d --remove-orphans

# Проверяет состояние и последние ошибки после обновления
docker compose ps
docker compose logs --tail=100

Для stateless-приложений обновление обычно выглядит как rolling-процедура: несколько реплик за reverse proxy обновляются поочерёдно. Обычный Docker Compose на одном VPS не даёт полноценный rolling update с гарантиями оркестратора, поэтому для одиночного контейнера ожидайте короткий перерыв при пересоздании. Базы данных, миграции схемы и major-обновления делайте в maintenance window с проверенным планом отката.

# Обновляет пакеты Docker Engine и Compose из официального APT-репозитория
sudo apt update
sudo apt install --only-upgrade -y docker-ce docker-ce-cli containerd.io \
  docker-buildx-plugin docker-compose-plugin

# Удаляет только неиспользуемый build cache; volumes и работающие образы не затрагиваются
docker builder prune -af

Не запускайте бездумно docker system prune -a --volumes. Эта команда может удалить неиспользуемые образы, сети и volumes; ошибочно помеченный volume с важными данными может стать невосстановимым без бэкапа. Сначала используйте docker system df и удаляйте конкретные ненужные ресурсы.

Troubleshooting + FAQ

Docker выдаёт «permission denied while trying to connect to the Docker daemon socket»

Ошибка означает, что текущий пользователь не имеет доступа к сокету Docker. Проверьте группы командой id: среди них должна быть docker. Если группы нет, выполните sudo usermod -aG docker $USER, полностью выйдите из SSH-сеанса и подключитесь заново. Не исправляйте проблему постоянным использованием sudo chmod 666 /var/run/docker.sock: это открывает Docker всем пользователям системы и создаёт серьёзную угрозу безопасности.

Контейнер постоянно перезапускается, а в docker compose ps статус Restarting

Сначала прочитайте журнал конкретного сервиса: docker compose logs --tail=200 app. Обычно причина — отсутствующая переменная окружения, неверный пароль базы, занятый порт, ошибка миграции или неподходящая архитектура образа. Проверьте итоговую подстановку переменных через docker compose config. Также посмотрите потребление памяти командой free -h и системные сообщения dmesg -T | grep -i oom: процесс мог быть убит OOM killer.

HTTPS-сертификат Caddy не выпускается или сайт открывается по HTTP

Проверьте, что A-запись домена указывает на IP VPS: dig +short ваш-домен. Убедитесь, что порты 80 и 443 разрешены в UFW, опубликованы в compose.yaml и не заняты другим процессом: sudo ss -tulpn | grep -E ':80|:443'. Затем откройте docker compose logs caddy. Частые причины — проксирование DNS через сторонний сервис, IPv6-запись AAAA с неверным адресом или закрытые порты на уровне сети провайдера.

На VPS закончилось место, хотя данных приложения немного

Начните с df -h и docker system df -v. Последняя команда покажет размеры образов, volumes, контейнеров и build cache. Проверьте логи в docker compose logs и размер каталога /var/lib/docker. Если проблема в устаревшем build cache, безопаснее начать с docker builder prune -af. Не удаляйте volumes командой prune, пока не знаете, какому проекту они принадлежат и пока не проверен свежий бэкап.

Приложение не подключается к PostgreSQL или Redis внутри Compose

Контейнеры должны обращаться друг к другу по имени сервиса, например postgres://db:5432/app, а не через localhost. Внутри контейнера localhost указывает на этот же контейнер. Проверьте, что оба сервиса находятся в одной сети Compose, с помощью docker compose exec app getent hosts db. Если база ещё запускается, добавьте healthcheck и логику повторных попыток подключения в приложение: depends_on не заменяет готовность базы к запросам.

Какой VPS-конфиг минимально подойдёт?

Для обучения, одного статического сайта с Caddy и небольшого бота хватит 1 vCPU, 2 ГБ RAM и 25–40 ГБ SSD или NVMe. Для реального небольшого проекта с базой данных начинайте с 2 vCPU, 4 ГБ RAM и 50–80 ГБ NVMe. Если используется PostgreSQL, n8n, Mattermost, Java-приложение или несколько сервисов, 8 ГБ RAM будет заметно надёжнее. Всегда оставляйте свободное место под Docker images, журналы и резервные копии.

Что выбрать — VPS или dedicated для этой задачи?

Для Docker Compose, личных сервисов, MVP, небольших SaaS и командных инструментов обычно достаточно VPS. Он быстрее разворачивается, проще масштабируется по тарифу и дешевле при умеренной нагрузке. Dedicated нужен при устойчивой высокой нагрузке CPU, большом количестве дисковых операций, объёмных базах данных, CI-сборках или требованиях к гарантированной производительности. Сначала измерьте фактическое потребление через docker stats, htop и метрики диска, а не выбирайте выделенный сервер «на всякий случай».

Можно ли обновлять все контейнеры автоматически через Watchtower?

Технически можно, но для production-системы неконтролируемые обновления рискованны. Новый образ может требовать миграции базы, менять переменные окружения или содержать несовместимый релиз. Безопаснее настроить уведомления о новых версиях, обновлять образы вручную в техническое окно и делать бэкап перед пересозданием. Автообновление допустимо для неважных stateless-сервисов, но даже там фиксируйте версии образов и следите за логами после обновления.

Выводы и следующие шаги

Теперь у вас есть базовая production-схема Docker на VPS: защищённый сервер, Docker Engine, Compose-проект, HTTPS через Caddy, постоянные volumes и зашифрованные резервные копии. Этой основы достаточно, чтобы размещать большинство self-hosted приложений без ручной установки зависимостей в Ubuntu.

  1. Добавьте мониторинг: node_exporter, cAdvisor, Uptime Kuma или внешний HTTP-проверяющий сервис.
  2. Для каждого нового проекта создавайте отдельный каталог в /opt/stacks, отдельный Compose-файл, отдельный .env и отдельный план бэкапа.
  3. При росте нагрузки вынесите базу данных на отдельный сервер или managed-решение, настройте метрики, лимиты ресурсов и регулярное тестовое восстановление резервных копий.

Главное правило обслуживания остаётся простым: конфигурацию храните воспроизводимо, секреты — отдельно и защищённо, данные — в volumes или явных каталогах, а каждое обновление начинайте со свежего проверяемого бэкапа.

Был ли этот гайд полезен?

Ваш отзыв помогает нам улучшать гайды.

Поделиться записью:

Отправьте гайд тому, кому он может пригодиться.

Telegram VKVK WhatsApp Facebook LinkedIn XX

docker на vps с нуля: установка, compose, обновления и бэкап томов
support_agent
Valebyte Support
Usually replies within minutes
Hi there!
Send us a message and we'll reply as soon as possible.