Перенесення Docker-контейнерів між серверами зводиться до трьох речей: образів, томів з даними та конфігурації середовища. Контейнер сам по собі не зберігає стан — усе важливе лежить у volume та в змінних .env, тому «перенести контейнер» на практиці означає перенести образ і дані, а потім підняти його заново на новому хості. Якщо Docker на новому сервері ще не встановлено, почніть із базового встановлення Docker на VPS, а для багатоконтейнерних проєктів знадобиться стаття про Docker Compose.
Метод 1: docker save + docker load
Підходить, коли між серверами немає спільного Registry і потрібно передати образ одним файлом — наприклад, під час разового переїзду на новий VPS.
# На старому сервері — зберігаємо образ з усіма шарами
docker save myapp:latest | gzip > myapp.tar.gz
# Передаємо на новий сервер
scp myapp.tar.gz root@NEW_SERVER:/tmp/
# На новому сервері — завантажуємо образ
docker load < /tmp/myapp.tar.gz
# Перевіряємо, що образ на місці і з тим самим ID
docker images | grep myapp
Мінус методу — розмір файлу: образ із товстим базовим шаром (наприклад, повний Ubuntu замість Alpine) легко виходить за кілька гігабайтів, і передавання повільним каналом займає години.
Метод 2: через Docker Registry (рекомендується)
Registry передає лише відсутні шари, підтримує відкат на попередній тег і не вимагає тримати файл образу на диску між серверами.
# Тегуємо і пушимо в Registry
docker tag myapp:latest registry.example.com/myapp:latest
docker push registry.example.com/myapp:latest
# На новому сервері
docker login registry.example.com
docker pull registry.example.com/myapp:latest
Якщо використовуєте docker-compose, для перенесення достатньо скопіювати docker-compose.yml і томи даних — образи перезберуться локально або завантажаться з Registry командою docker compose up -d.
Розбір типових помилок при перенесенні
- Error response from daemon: conflict: unable to delete ... (must be forced) — образ використовується іншим тегом чи запущеним контейнером; спершу зупиніть контейнери, що використовують образ, або додайте прапорець
-fдоdocker rmi. - open /tmp/myapp.tar.gz: no such file or directory —
scpперервався посередині передавання або вказано не той шлях; перевірте розмір файлу з обох боків командоюls -laі звірте контрольну сумуsha256sum. - unauthorized: authentication required — на новому сервері не виконано
docker loginдо приватного Registry або сплив токен доступу. - Error: No such image: myapp:latest — описка в тегу під час
docker tagабо образ не було запушено до спробиpullна іншому сервері. - toomanyrequests: You have reached your pull rate limit — анонімні запити до Docker Hub обмежені за IP; виконайте
docker loginз обліковим записом або використовуйте власний Registry.
Перенесення томів (volumes)
Образ переносить лише код і залежності застосунку. База даних, завантажені файли та логи живуть у volume і переносяться окремо.
# Експорт тому в тимчасовий архів
docker run --rm -v myapp_data:/data -v $(pwd):/backup \
alpine tar czf /backup/volume_backup.tar.gz -C /data .
# Імпорт на новому сервері у заздалегідь створений том
docker volume create myapp_data
docker run --rm -v myapp_data:/data -v $(pwd):/backup \
alpine tar xzf /backup/volume_backup.tar.gz -C /data
Порівняння способів перенесення
| Критерій | docker save/load | Docker Registry |
|---|---|---|
| Що передається | Повний архів образу | Лише нові шари |
| Чи потрібен окремий сервіс | Ні | Так, Registry |
| Відкат на попередню версію | Треба зберігати старі архіви вручну | Проста зміна тегу |
| Підходить для | Разового переїзду одного сервера | Регулярних деплоїв і кількох серверів |
Коли ці способи не підходять
- Контейнер тримає відкрите мережеве з'єднання зі станом (наприклад, підняту WebSocket-сесію) — перенесення завжди рве такі з'єднання, потрібен маршрут із graceful shutdown на рівні застосунку.
- Контейнер прив'язаний до пристроїв хоста через
--device(GPU, USB-токен) — образ переїде, але без такого самого пристрою на новому сервері частина функціональності не запрацює. - Дані змінюються під час експорту тому — якщо застосунок не зупинено,
tarна живому volume бази даних може забрати файл у неузгодженому стані; для СУБД переносьте не файли тому, а дамп засобами самої бази.
Чеклист міграції Docker
| Крок | Статус |
|---|---|
| Встановити Docker на новому сервері | ⬜ |
| Перенести docker-compose.yml | ⬜ |
| Перенести образи або налаштувати pull з Registry | ⬜ |
| Перенести томи даних | ⬜ |
| Оновити .env файли | ⬜ |
| Перевірити на новому сервері | ⬜ |
| Перемкнути DNS | ⬜ |
Перевірка після перенесення
Перед перемиканням DNS переконайтеся, що стек працює на новому сервері так само, як на старому.
# Усі контейнери підняті і без перезапусків
docker compose ps
# Логи без помилок за останні 5 хвилин
docker compose logs --since=5m
# Застосунок відповідає на внутрішньому порту
curl -I http://127.0.0.1:8080/health
# Дані збігаються: звіряємо кількість рядків у ключовій таблиці
docker exec -it myapp_db psql -U app -c "SELECT count(*) FROM orders;"
Якщо лічильники й логи збігаються зі старим сервером, перемикайте DNS і лише після цього зупиняйте контейнери на старій машині.
Перед зупинкою контейнерів на старому сервері переконайтеся, що всі дані синхронізовані — особливо якщо використовуються persistent volumes з базою даних: останній запис, зроблений після зняття дампу, на новому сервері відсутній.