Миграция Docker-контейнеров между VPS — частая задача при смене хостинга, переезде на более мощный сервер, обновлении инфраструктуры или консолидации проектов. На первый взгляд всё просто: контейнеры “упакованы”, значит их можно быстро скопировать на новый сервер. На практике же перенос почти всегда упирается не в контейнеры, а в данные, тома, переменные окружения, bind mounts, базы данных и способ запуска.
- перенос одного контейнера “как есть”;
- миграцию образов и volumes;
- перенос Docker Compose-проекта;
- потоковую миграцию без промежуточных файлов;
- массовый перенос десятков и сотен контейнеров;
- и, главное, какой способ выбрать в реальной продакшн-среде.
Что именно нужно переносить в Docker
Самая частая ошибка при миграции Docker — думать, что контейнер = приложение + все данные + вся конфигурация.
На самом деле Docker-развёртывание обычно состоит из четырёх разных слоёв:
1) Docker image (образ)
Это шаблон, из которого создаётся контейнер.
2) Container runtime config
То, как контейнер был запущен:
- переменные окружения (
ENV,--env,.env); - проброс портов (
-p); - сети;
- restart policy;
- capabilities;
- mounts и volumes.
3) Persistent data
Это самое важное — данные, которые должны пережить перезапуск и перенос:
- Docker volumes;
- bind mounts;
- базы данных;
- пользовательские файлы;
- кэш, если он нужен.
4) Orchestration / deployment logic
То, как это всё воспроизводится:
docker run;docker compose;- Ansible / Terraform;
- CI/CD;
- Swarm / Nomad / Kubernetes.
Именно поэтому контейнеры сами по себе обычно не мигрируют “как VM”. Правильный перенос — это перенос образов + конфигурации + данных. Это соответствует и рекомендациям Docker: данные нужно хранить во volumes, а контейнеры — уметь пересоздавать.
Способ №1. Перенос контейнера через docker commit + docker save
Это самый популярный “быстрый” способ, который часто рекомендуют в базовых гайдах. Его суть:
- Остановить контейнер;
- Зафиксировать его текущее состояние в новый образ;
- Сохранить образ в
.tar; - Передать файл на новый сервер;
- Загрузить образ и запустить контейнер заново.
Именно этот подход лежит в основе гайда Make Tech Easier.
Когда этот способ подходит
Он хорош, если вы переносите:
- один или несколько контейнеров;
- сервисы без сложной инфраструктуры;
- тестовые окружения;
- прототипы;
- контейнеры, которые были модифицированы вручную через
docker exec.
Когда не подходит
Он не идеален, если:
- у контейнера есть volumes;
- внутри работает база данных;
- контейнеров десятки или сотни;
- вы хотите чистый и воспроизводимый деплой.
Пошагово: перенос одного контейнера
1. Посмотреть контейнеры
docker ps
2. Остановить контейнер
docker stop myapp
3. Сохранить текущее состояние контейнера в новый образ
docker commit myapp myapp-migrated:latest
Если контейнер не хочется останавливать, можно использовать
docker commit -p=false, но это повышает риск получить неполные или несогласованные данные. Для production-сервисов лучше делать миграцию на “чистом” состоянии. Подобный риск прямо вытекает из природы commit-подхода.
4. Сохранить образ в файл
docker save -o myapp-migrated.tar myapp-migrated:latest
5. Передать файл на новый сервер
scp myapp-migrated.tar root@NEW_SERVER_IP:/root/
6. Загрузить образ на новом VPS
docker load -i /root/myapp-migrated.tar
7. Создать и запустить контейнер
docker create --name myapp -p 8080:80 myapp-migrated:latest
docker start myapp
Плюсы способа
- просто;
- быстро;
- удобно для единичных контейнеров;
- не требует реестра образов.
Минусы
- не переносит volumes;
- легко забыть параметры запуска;
- плохо масштабируется;
- не подходит как “долгосрочный DevOps-подход”.
Способ №2. Перенос volumes и данных контейнера
Вот здесь начинается настоящая миграция, потому что почти всегда важны не только образы, но и данные.
И Docker сам подчёркивает важный момент:
docker exportне экспортирует содержимое volumes.
Если у контейнера подключён volume, его данные надо переносить отдельно.
Это ключевая причина, почему перенос “одного tar-файла контейнера” часто заканчивается сюрпризом: контейнер запустился, а данных внутри нет.
Как посмотреть volumes контейнера
docker inspect myapp
Или компактнее:
docker inspect -f '{{ json .Mounts }}' myapp
Официально рекомендуемый способ бэкапа volume
Docker рекомендует использовать временный контейнер, который монтирует volume и архивирует его содержимое.
Пример: резервная копия volume
Допустим, у контейнера есть volume myapp_data.
docker run --rm \
-v myapp_data:/volume \
-v $(pwd):/backup \
ubuntu \
tar cvf /backup/myapp_data.tar /volume
На практике можно использовать и
alpine, чтобы операция выполнялась быстрее и легче.
Сжатый вариант
docker run --rm \
-v myapp_data:/volume \
-v $(pwd):/backup \
alpine \
tar czf /backup/myapp_data.tar.gz -C /volume .
Как восстановить volume на новом сервере
1. Создать volume
docker volume create myapp_data
2. Развернуть архив в volume
docker run --rm \
-v myapp_data:/volume \
-v $(pwd):/backup \
alpine \
sh -c "cd /volume && tar xzf /backup/myapp_data.tar.gz"
А что с bind mounts?
Если контейнер использует не Docker volume, а обычную папку хоста, например:
-v /opt/myapp/uploads:/app/uploads
то Docker тут вообще ни при чём — это просто директория сервера, и её нужно переносить как обычные файлы:
rsync -avz /opt/myapp/uploads root@NEW_SERVER_IP:/opt/myapp/uploads
Или:
tar -czf uploads.tar.gz /opt/myapp/uploads
scp uploads.tar.gz root@NEW_SERVER_IP:/root/
Отдельно про базы данных
Если внутри Docker крутятся:
- PostgreSQL
- MySQL / MariaDB
- MongoDB
- Redis
то лучше делать не только перенос volume, но и логический backup:
PostgreSQL
docker exec -t postgres pg_dumpall -U postgres > postgres_dump.sql
MySQL / MariaDB
docker exec mysql mysqldump -u root -p --all-databases > mysql_dump.sql
Почему это важно:
- дамп проще проверить;
- его легче восстановить на новой версии;
- он надёжнее при переносе между разными окружениями.
Способ №3. Потоковый перенос без .tar файла
Если образ большой, а места на диске мало, можно не сохранять его в промежуточный файл, а передать сразу по SSH.
Этот подход тоже упоминается в гайде Make Tech Easier и отлично подходит для VPS-миграций.
Пример
docker save myapp-migrated:latest | ssh root@NEW_SERVER_IP docker load
Что происходит:
docker saveпишет образ в stdout;- поток идёт через SSH;
- на новом сервере
docker loadсразу загружает образ.
Когда это удобно
- мало места на диске;
- нужно быстро перекинуть один образ;
- серверы уже имеют SSH-доступ друг к другу.
Ограничения
- всё равно нужно отдельно переносить volumes;
- если связь оборвётся — передача придётся начинать заново;
- неудобно для больших batch-миграций без автоматизации.
Способ №4. Перенос Docker Compose-проекта
Если у вас не один контейнер, а полноценный стек:
- приложение;
- база данных;
- Redis;
- reverse proxy;
- worker;
- cron;
то лучший способ миграции — переносить не контейнеры, а весь проект через Docker Compose.
И это уже правильный production-подход.
Docker сам позиционирует Compose как основной способ воспроизводимого развёртывания multi-container приложений.
Что нужно перенести
Обычно это:
docker-compose.yml.envnginx.conf/Caddyfile/traefik.yml- volumes
- bind mounts
- SSL / Let’s Encrypt storage
- дампы БД
Пример Compose-конфигурации
version: "3.9"services:
app:
image: myapp:latest
ports:
- "8080:80"
env_file:
- .env
volumes:
- app_data:/app/data
restart: unless-stopped db:
image: postgres:16
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: secret
volumes:
- db_data:/var/lib/postgresql/data
restart: unless-stoppedvolumes:
app_data:
db_data:
Как мигрировать Compose-проект
На старом сервере
- Скопировать папку проекта:
tar -czf myproject.tar.gz /opt/myproject
- Сохранить volumes:
docker run --rm \
-v myproject_app_data:/volume \
-v $(pwd):/backup \
alpine \
tar czf /backup/app_data.tar.gz -C /volume .
docker run --rm \
-v myproject_db_data:/volume \
-v $(pwd):/backup \
alpine \
tar czf /backup/db_data.tar.gz -C /volume .
- Передать файлы на новый VPS.
На новом сервере
- Развернуть проект:
tar -xzf myproject.tar.gz -C /
cd /opt/myproject
- Создать volumes:
docker volume create myproject_app_data
docker volume create myproject_db_data
- Восстановить данные:
docker run --rm \
-v myproject_app_data:/volume \
-v $(pwd):/backup \
alpine \
sh -c "cd /volume && tar xzf /backup/app_data.tar.gz"
- Поднять стек:
docker compose up -d
Почему Compose лучше docker commit
Потому что Compose:
- воспроизводим;
- читаем;
- масштабируем;
- подходит для CI/CD;
- позволяет быстро переехать между серверами и хостингами.
Если вы ещё запускаете всё через огромные docker run команды, миграция — отличный момент наконец перевести проект на Compose.
Способ №5. Массовая миграция 50–200+ контейнеров
Вот здесь обычные инструкции из интернета перестают работать.
Если у вас:
- десятки или сотни клиентских контейнеров;
- один и тот же Dockerfile / image;
- разные env, volumes, домены, токены;
- инфраструктура “мультиклиент” или SaaS;
то переносить всё “по одному контейнеру” — это уже антипаттерн.
В таком сценарии нужно переносить не контейнеры, а систему развёртывания.
Ключевой вопрос
У контейнеров есть уникальные данные?
Если нет, и они отличаются только env-переменными / портами / именами, то переносится:
- образ;
- список клиентов;
- шаблон запуска.
Если да, и у каждого клиента свой volume или папка, то переносится ещё и:
- volume каждого клиента;
- bind mount каждого клиента;
- его метаданные.
Production-подход: “один шаблон + генератор запуска”
Для 200 контейнеров правильная архитектура выглядит так:
Структура
/opt/clients/
client001.env
client002.env
client003.env
Один образ
my-client-app:latest
Один шаблон запуска
docker run -d \
--name client001 \
--restart unless-stopped \
--env-file /opt/clients/client001.env \
-v client001_data:/app/data \
-p 10001:8080 \
my-client-app:latest
И цикл на все контейнеры
while read client; do
docker run -d \
--name $client \
--restart unless-stopped \
--env-file /opt/clients/${client}.env \
-v ${client}_data:/app/data \
my-client-app:latest
done < clients.txt
Что нужно экспортировать при массовой миграции
1. Список контейнеров
docker ps -a --format '{{.Names}}' > containers.txt
2. Конфиги контейнеров
mkdir -p inspect
for c in $(cat containers.txt); do
docker inspect "$c" > "inspect/$c.json"
done
3. Список volumes
docker volume ls --format '{{.Name}}' > volumes.txt
4. Архив всех volumes
mkdir -p volumeswhile read vol; do
echo "Backing up volume: $vol"
docker run --rm \
-v ${vol}:/volume \
-v $(pwd)/volumes:/backup \
alpine \
tar czf /backup/${vol}.tar.gz -C /volume .
done < volumes.txt
5. Список образов
docker ps -a --format '{{.Image}}' | sort | uniq > images.txt
6. Экспорт всех кастомных образов
mkdir -p imageswhile read img; do
safe_name=$(echo "$img" | sed 's/[\/:]/_/g')
docker save "$img" -o "images/${safe_name}.tar"
done < images.txt
Как передать всё на новый сервер
Лучше использовать rsync, а не scp, особенно если переносите много данных:
rsync -avzh --progress /root/docker-migration/ root@NEW_SERVER_IP:/root/docker-migration/
Почему не стоит переносить /var/lib/docker целиком
Иногда встречается “совет” просто скопировать:
/var/lib/docker
Это действительно может сработать, но как основной способ это не рекомендуется, потому что:
- зависит от версии Docker;
- зависит от storage driver;
- может сломаться между разными дистрибутивами и ядрами;
- переносит вместе с полезными данными ещё и технический мусор.
Такой подход можно рассматривать только как аварийный snapshot, но не как правильную миграцию.
Как мигрировать Docker без потери данных и с минимальным downtime
Для production-проектов главный вопрос не “как скопировать”, а как переехать без потери данных и без долгого простоя.
Вот рабочая схема.
1. Заранее уменьшите TTL DNS
Например, до 300 секунд, чтобы смена IP прошла быстрее.
2. Подготовьте новый VPS заранее
Установите Docker, Compose, проверьте диски, порты, firewall.
3. Сделайте первичную миграцию
Скопируйте:
- проект;
- образы;
- volumes;
- bind mounts;
- дампы БД.
4. Не переключайте трафик сразу
Сначала поднимите всё на новом сервере внутренне и проверьте:
- контейнеры стартуют?
- приложение отвечает?
- база на месте?
- SSL и reverse proxy работают?
- очереди и cron-задачи живы?
5. Заморозьте запись на старом сервере
На финальном этапе нужно остановить запись в данные.
Варианты:
- выключить приложение;
- перевести сайт в maintenance mode;
- остановить API;
- остановить контейнеры.
Например:
docker compose down
или точечно:
docker stop app_container
docker stop postgres_container
6. Сделайте финальный “дельта”-бэкап
Потому что между первичной миграцией и финальным cutover данные могли измениться.
Снова:
- дамп БД;
- архив volumes;
rsyncbind mounts.
7. Восстановите финальные данные на новом VPS
И только после этого:
docker compose up -d
8. Переключите DNS / IP / reverse proxy
После проверки — уже боевой cutover.
9. Подержите старый сервер в standby
Не удаляйте его сразу. Лучше оставить на 24–72 часа, чтобы можно было быстро откатиться.
Типичные ошибки при переносе Docker на другой сервер
Вот список того, на чём чаще всего ломаются миграции.
Ошибка №1. Перенесли контейнер, но забыли volume
Это классика.
Сервис запускается, но:
- база “пустая”;
- пользовательские файлы пропали;
- CMS открывается как “свежая установка”.
Причина: был перенесён образ, но не данные.
Ошибка №2. Использовали docker export и ожидали, что всё уедет
Но docker export не включает volumes. Это прямо указано в документации Docker.
Ошибка №3. Не сохранили параметры запуска
Образ загрузился, но контейнер больше не знает:
- какие порты использовать;
- где лежат данные;
- какие env-переменные нужны;
- как называется сеть.
Решение: хранить запуск в:
docker compose.yml,- шаблоне
docker run, - Ansible / deployment scripts.
Ошибка №4. Забыли .env
Контейнер стартует, но приложение падает, потому что не хватает:
- API-ключей;
- доменов;
- токенов;
- строк подключения к БД.
Ошибка №5. Права доступа и UID/GID
После переноса приложение не может писать в директории.
Например:
chown -R 1000:1000 /opt/myapp/data
chmod -R 755 /opt/myapp/data
Ошибка №6. Порты уже заняты
На новом сервере уже слушают 80, 443, 5432, 6379 и т.д.
Проверка:
ss -tulpn | grep -E ':80|:443|:5432|:6379'
Ошибка №7. SSL / Let’s Encrypt не перенесли
Особенно часто это ломает:
- Nginx Proxy Manager;
- Traefik;
- Caddy.
Нужно переносить не только конфиги, но и:
- ACME storage;
- сертификаты;
- persistent storage reverse proxy.
Какой способ выбрать: рекомендации по сценариям
Теперь самое полезное — какой способ использовать в реальности.
Сценарий 1. Один контейнер без важных данных
Подходит:
docker commitdocker savedocker load
Идеально для:
- тестового сервиса;
- временной среды;
- небольшого self-hosted приложения.
Сценарий 2. Один контейнер с данными
Подходит:
docker save/docker load- backup volume
- backup bind mounts
Идеально для:
- WordPress;
- Nginx;
- небольших приложений;
- админок;
- CMS.
Сценарий 3. Полноценный стек из нескольких контейнеров
Подходит:
- Docker Compose
- backup volumes
- дампы БД
Это лучший вариант для:
- production-сайтов;
- API;
- SaaS;
- CRM / ERP;
- self-hosted платформ.
Сценарий 4. Десятки и сотни контейнеров
Подходит:
- шаблонный деплой;
- генерация
docker runили Compose; - Ansible / CI/CD;
- централизованный inventory клиентов.
Это лучший вариант для:
- мультиклиентских платформ;
- white-label сервисов;
- hosting-провайдеров;
- инфраструктур с 50–500 контейнерами.
Сценарий 5. Нужно “перенести всё как есть” максимально быстро
Подходит:
- snapshot-подход;
- экспорт образов;
- архив всех volumes;
- автоматизированный import.
Использовать только если:
- важна скорость;
- нет времени на рефакторинг;
- потом планируете привести инфраструктуру в порядок.
Вывод
Перенос Docker-контейнеров на другой сервер — задача несложная только в простых кейсах. Как только появляются:
- volumes,
- базы данных,
- reverse proxy,
- десятки контейнеров,
- клиентские окружения,
“скопировать контейнер” уже недостаточно.
Короткая рекомендация:
Если у вас:
- 1–3 контейнера → подойдёт
docker save/load+ перенос данных; - Docker Compose-проект → переносите весь проект и volumes;
- 50–200+ контейнеров → мигрируйте не контейнеры, а систему развёртывания.
И главное правило:
Правильная Docker-инфраструктура должна не “копироваться”, а “воспроизводиться”.
Если ваш проект можно развернуть на новом сервере из:
- образа,
- Compose-файла,
.env,- volumes / backup,
значит инфраструктура организована правильно — и миграция пройдёт без паники, простоя и ночных “почему всё пустое”.
Нужна помощь с переносом Docker на новый VPS?
Если вы переносите:
- сайт,
- CRM,
- Telegram-ботов,
- SaaS,
- 10–200 контейнеров клиентов,
- или хотите переехать с одного VPS на другой без потери данных,
команда dockerhosting.ru поможет:
- подготовить новый сервер;
- перенести Docker-проекты и volumes;
- настроить reverse proxy, SSL, firewall и бэкапы;
- минимизировать downtime;
- привести инфраструктуру в поддерживаемый вид.