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

Отримати VPS arrow_forward
eco Початковий Туторіал

Docker на VPS з нуля: встановлення, Compose, оновлення та резервне копіювання томів

calendar_month Sep 14, 2026 schedule 23 хв. читання visibility 15 переглядів
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.

Такий підхід особливо корисний, якщо ви запускаєте self-hosted сервіси. Контейнери можна зупиняти, видаляти та пересоздавати без втрати користувацьких даних: за умови, що дані зберігаються у 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 і видаляйте конкретні непотрібні ресурси.

Усунення несправностей + 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 або явних каталогах, а кожне оновлення починайте зі свіжого перевірюваного бекапу.

Чи був цей гайд корисним?

Ваш відгук допомагає нам покращувати гайди.

Share this post:

Надішліть гайд тому, кому він може стати в пригоді.

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.