Як передати образ між хостами без реєстру?
Передача Docker image без registry це відповідь, коли image на host A потрібно на host B, а ти не можеш або не хочеш пушити у registry. Пара docker save/load це канонічне рішення.
Теорія
TL;DR
docker save= експортує IMAGE (з усіма шарами + метаданими) у tar-файл або stdout.docker load= імпортує image-tar назад в інший daemon.- Відрізняється від
docker export/import, що працюють на container і дають flattened single-layer image без метаданих. - Use cases: air-gapped середовища, USB-трансфер між dev-машинами, image-promotion між безпечними мережами, offline-демо.
- Для рутинного cross-host трансферу registry значно кращий (дедуплікація, auth, pull-by-digest).
Швидкий приклад
# Вихідний host: пакуємо image
$ docker save myapp:1.0 -o myapp.tar
$ ls -lh myapp.tar
-rw------- 1 me me 256M ... myapp.tar
# Стискаємо для швидшого трансферу
$ docker save myapp:1.0 | gzip > myapp.tar.gz
# Трансфер (як завгодно)
$ scp myapp.tar.gz user@dest:/tmp/
# Цільовий host: load
$ docker load -i /tmp/myapp.tar
# АБО
$ gunzip -c /tmp/myapp.tar.gz | docker load
Loaded image: myapp:1.0
$ docker run --rm myapp:1.0Image тепер на цільовому host, ніби pull з registry, з тим самим іменем, tag і шарами.
One-liner через SSH
Без проміжного файлу:
$ docker save myapp:1.0 | gzip | ssh user@dest 'gunzip | docker load'Stream через провід, без temp-файлу. Чудово для одноразових трансферів.
save vs export
Це людей плутає.
docker save | docker export | |
|---|---|---|
| Оперує на | image | container |
| Шари | збережено (кожен шар у tar) | flattened у один шар |
| Image-метадані | збережено (CMD, ENV, history тощо) | втрачено |
| Зворотній | docker load | docker import |
| Use case | sharing image | snapshot filesystem only |
# IMAGE save → load (рекомендовано)
docker save myimg -o image.tar
docker load -i image.tar
# CONTAINER export → import (тільки filesystem)
docker export <container> -o fs.tar
docker import fs.tar imported:1.0 # створює новий image без метаданих99% часу хочеш save/load. export/import для legacy або специфічних кейсів (перебудова image filesystem з нуля).
Кілька image у одному tar
docker save myapp:1.0 myapp:1.1 nginx:1.27 -o multi.tar
docker load -i multi.tarКорисно для шиппінгу цілого стеку у air-gapped середовище одним файлом.
Типові помилки
Плутати save з export
# НЕПРАВИЛЬНО: container export, втрачає CMD/ENV/etc.
$ docker export api > api.tar
$ docker import api.tar api:fresh
$ docker run api:fresh # error: no command specified
# ПРАВИЛЬНО: image save, зберігає усе
$ docker save myimg:1.0 > myimg.tar
$ docker load < myimg.tar
$ docker run myimg:1.0 # працює як булоSave у stdout без редиректу
# НЕПРАВИЛЬНО: tar-бінар корумпує термінал
$ docker save myimg
# (термінал пішов у розноc)
# ПРАВИЛЬНО
$ docker save myimg -o myimg.tar
# АБО
$ docker save myimg > myimg.tarЗавжди -o file або > file. Дефолт це stdout, безпечно при редиректі, terminal-breaking без.
Забути, що tar величезний
Save-tar приблизно розміру image (multi-stage з 25MB фіналом → 25MB tar; 1GB image → 1GB tar). Стискай для трансферу:
docker save myimg | zstd > myimg.tar.zst # zstd швидший за gzip
docker save myimg | xz > myimg.tar.xz # xz менший, повільнішийІм'я image не зберігається при import
# `docker import` НЕ зберігає імен
$ docker export api > fs.tar
$ docker import fs.tar
sha256:abc123... # untagged
# Треба тегувати рукамиdocker load зберігає імена; docker import ні. Ще одна причина брати save/load.
Коли використовувати save/load (а коли ні)
Бери save/load коли:
- Air-gapped або offline-трансфер (без мережі між host).
- Одноразовий трансфер між двома відомими машинами.
- Маленька команда, що ще не налаштувала registry.
- Бекап/архів конкретних версій image.
- Initial bootstrap registry (load image спочатку, потім push).
Бери registry натомість коли:
- Кілька споживачів потребують image (кожен робить
docker pull). - Хочеш pull-by-digest, signing, vulnerability scan, RBAC.
- Трансфер це частина CI/CD-pipeline.
- Дбаєш про дедуплікацію між версіями (registry зберігає шари раз).
Реальне застосування
- Air-gapped enterprise: усі image течуть як save-tar між security-зонами.
- Customer-on-premise деплой: ship застосунку як tar, що
docker load'ить клієнт у своїй мережі. - Disaster recovery: save критичних image у backup-storage як tar; load, якщо registry втрачено.
- Embedded / edge devices: preload image через
docker loadз USB-stick під час виробництва. - Image-promotion через security-межі: save у dev, scan offline, load у прод через one-way diode.
Питання для поглиблення
Q: Яка різниця між save і pull?
A: pull тягне image з registry (HTTP). save експортує локальний image у tar. Протилежні напрямки руху image з різними storage-носіями.
Q: Чи зберігається історія image?
A: З docker save/load так, повна image-історія, включно з усіма шарами і метаданими. З docker export/import ні, flattened у один шар.
Q: Чи цільовий host потребує тієї самої Docker-версії?
A: OCI-сумісні tarball'и interoperable між сучасними Docker-версіями. Дуже старі daemon можуть мати формат-проблеми, але для будь-якого Docker за останні кілька років працює.
Q: Що насправді у tar?
A: Директорія на шар (кожна з tar його filesystem-змін), manifest.json, config blob і tag-інфо. Tar це zip-bundled OCI image.
Q: (Senior) Як заскриптувати multi-image трансфер для air-gapped деплою?
A: Збери manifest потрібних image (grep image: compose.yaml | awk ...), pull кожен на connected-стороні, save усі у один tar (docker save img1 img2 img3 -o stack.tar), zstd-стискай для трансферу, шипни через зазор, load на air-gapped стороні. Додай digest-верифікацію на отримуючій стороні: docker images --digests | tee received.txt порівняно з digest-списком source, щоб ловити tampered або часткові трансфери.
Приклади
Air-gapped деплой Compose-стеку
# На connected build-машині
$ docker compose -f compose.yaml pull # переконатися, що всі image present
$ images=$(grep image: compose.yaml | awk '{print $2}')
$ docker save $images -o stack-2026-04-30.tar.gz # усі image в одному файлі
# 1.2 GB ... трансфер через дозволений канал
# На air-gapped target
$ docker load -i stack-2026-04-30.tar.gz
Loaded image: nginx:1.27-alpine
Loaded image: postgres:16
Loaded image: myorg/api:1.2.3
$ docker compose up -dОдин tar, один load, увесь стек крутиться offline.
Stream через SSH
$ docker save myapp:1.0 | ssh user@destination 'docker load'
Loaded image: myapp:1.0Без temp-файлу. Корисно, коли disk-space тісний на будь-якій стороні.
Порівняння з registry-підходом
# save/load: просто, без setup, повільно для повторних трансферів
docker save myapp:1.0 | ssh dest 'docker load'
# 30 секунд, повний image передано
# Registry: dedup, швидше на повторних трансферах
docker push myreg.example.com/myapp:1.0
# На dest:
docker pull myreg.example.com/myapp:1.0
# 5 секунд, лише змінені шари переданоДля щоденних workflow registry перемагає по швидкості і tooling. Для одноразових трансферів save/load перемагає по простоті.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.
Коментарі
Ще немає коментарів