Здесь два локальных сценария: перенести dev-данные в prod-local и продолжить разработку на другом компьютере. Для переноса на сервер есть отдельная полная инструкция dev → VPS.
Нужны дамп PostgreSQL и архив изображений. Они относятся к одной версии данных. Инструкция рассчитана на Local media → Local media. Пользователи и их пароли переедут вместе с БД; существующие и переносимые данные не объединяются.
Все блоки одного этапа выполняйте в том же терминале: переменные с путями и именами контейнеров сохраняются только в нём. На компьютере назначения задайте переменные заново по своей выбранной ветке.
Сначала: сделайте копию dev
Все команды этого раздела выполняйте на исходном компьютере, из корня сгенерированного проекта. Остановите make dev через Ctrl+C и свои задачи, которые пишут в БД или меняют изображения. PostgreSQL оставьте работающим:
make up
make wait-db
docker ps --format '{{.Names}}'
Из списка возьмите имя dev-контейнера PostgreSQL, не prod-local. Ниже пример example_project-postgres-1. Значения POSTGRES_DB и POSTGRES_USER возьмите из своей .env, а DEV_MEDIA — из MEDIA_ROOT, если вы его переопределяли. По умолчанию папка изображений показана в примере:
DEV_POSTGRES=example_project-postgres-1
DEV_DB=example_project
DEV_USER=postgres
DEV_MEDIA="$HOME/.local/share/example_project/media"
umask 077
TRANSFER_DIR="$HOME/example-transfer-$(date +%Y%m%d-%H%M%S)"
mkdir -m 700 "$TRANSFER_DIR"
Сохраните БД, затем все изображения и готовые размеры:
docker exec "$DEV_POSTGRES" \
pg_dump -U "$DEV_USER" -d "$DEV_DB" -Fc \
> "$TRANSFER_DIR/database.dump"
COPYFILE_DISABLE=1 tar -czf "$TRANSFER_DIR/media.tar.gz" -C "$DEV_MEDIA" .
printf 'Копия сохранена: %s\n' "$TRANSFER_DIR"
Переходите дальше только после успешного завершения обеих команд. Если изображений в БД никогда не было, архив и все действия с media ниже пропустите. Если изображения были, а папка пропала, одного дампа недостаточно: найдите копию файлов.
Папка копии находится вне проекта и не должна попадать в Git. Пока оба файла не готовы, dev не запускайте. Дальше выберите один из двух сценариев.
Вариант A. Импорт в prod-local
A1. Сначала запустите пустой prod-local
На компьютере назначения должен быть тот же код и те же миграции, что у дампа. В Generator UI создайте профиль local с preset prod-local, сохраните и выполните Generate. Если prod-local ещё не запускали:
bash ./deploy/control provision --env local
bash ./deploy/control up --env local
bash ./deploy/control status --env local --json
Дождитесь успешного запуска: status должен показывать current и не иметь активной операции. Импорт выполняют после этого, не между provision и первым up. Иначе БД окажется непустой без зарегистрированного первого релиза.
Для уже работающего prod-local повторять provision не нужно. Если в нём есть данные, которые надо сохранить, сначала сделайте штатный backup:
bash ./deploy/control backup --env local --confirm local
Сохраните ID копии. Следующий импорт заменяет таблицы, записи и аккаунты целевой БД. Для свежего пустого окружения этот backup можно пропустить.
A2. Выберите контейнеры и остановите приложение
Далее все команды — на компьютере с prod-local, в одном терминале до завершения A6. Если это другая машина, сначала передайте на неё папку копии и проект через свой обычный способ передачи файлов. Здесь scp не нужен, если копия уже на том же компьютере.
docker ps --format '{{.Names}}'
Подставьте имена local, не dev-контейнеров. TRANSFER_DIR — абсолютный путь к папке, где лежат database.dump и media.tar.gz:
TRANSFER_DIR=/absolute/path/to/example-transfer
TARGET_BACKEND=example_project-local-backend-1
TARGET_FRONTEND=example_project-local-frontend-1
TARGET_POSTGRES=example_project-local-postgres-1
TARGET_DB=app
docker stop "$TARGET_FRONTEND" "$TARGET_BACKEND"
PostgreSQL не останавливайте. app — имя БД нового prod-local; если ранее выполняли restore, используйте имя действующей БД. Остановите также свои внешние процессы записи.
A3. Восстановите БД
docker exec -i "$TARGET_POSTGRES" \
pg_restore -U postgres -d "$TARGET_DB" \
--clean --if-exists --no-owner --no-privileges --single-transaction \
< "$TRANSFER_DIR/database.dump"
--clean заменяет содержимое из дампа, --single-transaction отменяет частичное восстановление при SQL-ошибке. Если команда завершилась ошибкой, не переходите к запуску приложения.
A4. Восстановите изображения
Этот шаг нужен, если в дампе есть изображения. Узнайте путь внутри backend и соответствующую папку компьютера:
export BACKEND_MEDIA=$(docker inspect "$TARGET_BACKEND" \
--format '{{range .Config.Env}}{{println .}}{{end}}' \
| sed -n 's/^MEDIA_ROOT=//p')
TARGET_MEDIA=$(docker inspect "$TARGET_BACKEND" \
| jq -er --arg path "$BACKEND_MEDIA" \
'.[0].Mounts[] | select(.Destination == $path and .Type == "bind") | .Source')
TARGET_IMAGE=$(docker inspect "$TARGET_BACKEND" --format '{{.Config.Image}}')
test -n "$BACKEND_MEDIA" && test -d "$TARGET_MEDIA"
Распакуйте архив и назначьте владельца backend. Временный контейнер использует уже установленный образ: приложение в нём не запускается. Такой способ работает и с Docker Desktop, и на Linux, без ручного изменения владельца всей папки проекта:
docker run --rm -i --platform linux/amd64 --user 0 \
--entrypoint tar \
--mount "type=bind,src=$TARGET_MEDIA,dst=/media" \
"$TARGET_IMAGE" -xzf - -C /media < "$TRANSFER_DIR/media.tar.gz"
docker run --rm --platform linux/amd64 --user 0 \
--entrypoint chown \
--mount "type=bind,src=$TARGET_MEDIA,dst=/media" \
"$TARGET_IMAGE" -R 10001:10001 /media
A5. Обновите привязку к папке изображений
БД перенесена из dev и помнит старый путь. После копирования файлов этот блок вычислит новое значение по пути backend; вручную рассчитывать или сверять идентификаторы не нужно. Нужен Python 3 на компьютере:
MEDIA_ID=$(python3 - <<'PYTHON'
import hashlib, os, posixpath
root = os.environ['BACKEND_MEDIA']
assert root.startswith('/') and root != '/', 'Проверьте BACKEND_MEDIA'
print(hashlib.sha256(b'local\0' + posixpath.normpath(root).encode() + b'\0').hexdigest())
PYTHON
)
docker exec -i "$TARGET_POSTGRES" \
psql -U postgres -d "$TARGET_DB" \
-v ON_ERROR_STOP=1 -v media_id="$MEDIA_ID" <<'SQL'
BEGIN;
SELECT pg_advisory_xact_lock(731947120);
INSERT INTO admingen_media.store_identity (singleton, identity)
VALUES (true, :'media_id')
ON CONFLICT (singleton) DO UPDATE SET identity = EXCLUDED.identity;
COMMIT;
SQL
Эта операция не восстанавливает картинки — она применяется только после A4 к соответствующей копии файлов.
A6. Запустите prod-local
docker start "$TARGET_BACKEND"
docker logs --tail 50 "$TARGET_BACKEND"
docker exec "$TARGET_BACKEND" migrate --verify
Если backend запустился без ошибки и миграции подтверждены:
docker start "$TARGET_FRONTEND"
bash ./deploy/control status --env local --json
Войдите в local-админку аккаунтом из dev. Откройте записи и картинки. При необходимости заново войдите: серверные ключи подписи не копировались вместе с БД. Сохраняйте дамп и архив, пока не убедитесь в результате.
Вариант B. Продолжить dev на другом компьютере
B1. Передайте проект и копию
На новый компьютер передайте исходники того же коммита и папку TRANSFER_DIR. В проекте нужны .admingen/project.yaml, custom-код и вся backend/migrations, включая manifest. Файлы можно передать через Git и отдельное копирование архива. Дамп не добавляйте в Git.
На новом компьютере откройте корень проекта. Все блоки B1–B4 выполняйте в одном терминале. Настройте его .env; если файла ещё нет:
make local-environment
Настройка создаёт локальные параметры этой машины. Она не заменяет импорт данных. Запустите только PostgreSQL, пока без make dev:
make up
make wait-db
docker ps --format '{{.Names}}'
Выберите dev-контейнер и заполните значения из новой .env:
TRANSFER_DIR=/absolute/path/to/example-transfer
TARGET_POSTGRES=example_project-postgres-1
TARGET_DB=example_project
TARGET_USER=postgres
B2. Восстановите пустую БД
Этот вариант рассчитан на пустую целевую dev-БД:
docker exec -i "$TARGET_POSTGRES" \
pg_restore -U "$TARGET_USER" -d "$TARGET_DB" \
--no-owner --no-privileges --single-transaction \
< "$TRANSFER_DIR/database.dump"
Если таблицы уже существуют, команда сообщит об ошибке. Не добавляйте --clean, пока не решили заменить имеющиеся данные: это другой сценарий, а не исправление формата команды.
B3. Распакуйте изображения и задайте новый путь
Если изображений не было, переходите к B4. Иначе на новом компьютере:
export MEDIA_ROOT="$HOME/.local/share/example_project/media"
mkdir -p "$MEDIA_ROOT"
tar -xzf "$TRANSFER_DIR/media.tar.gz" -C "$MEDIA_ROOT"
Архив из начала этой статьи содержит файлы без внешней папки media. Распаковка именно такая. После неё обновите привязку БД:
MEDIA_ID=$(python3 - <<'PYTHON'
import hashlib, os
from pathlib import Path
root = Path(os.environ['MEDIA_ROOT']).resolve(strict=True)
assert root.is_dir(), 'MEDIA_ROOT должен быть папкой'
print(hashlib.sha256(b'local\0' + str(root).encode() + b'\0').hexdigest())
PYTHON
)
docker exec -i "$TARGET_POSTGRES" \
psql -U "$TARGET_USER" -d "$TARGET_DB" \
-v ON_ERROR_STOP=1 -v media_id="$MEDIA_ID" <<'SQL'
BEGIN;
SELECT pg_advisory_xact_lock(731947120);
INSERT INTO admingen_media.store_identity (singleton, identity)
VALUES (true, :'media_id')
ON CONFLICT (singleton) DO UPDATE SET identity = EXCLUDED.identity;
COMMIT;
SQL
B4. Запустите разработку
Из того же терминала, где установлен MEDIA_ROOT:
make dev
Войдите с перенесённым аккаунтом, проверьте записи и изображения. При следующем запуске используйте тот же MEDIA_ROOT; если это стандартный путь, отдельная настройка не требуется. Для своего пути сохраните его в окружении запуска backend.
Если backend сообщает media storage cannot change while assets exist, сначала проверьте, какой путь он получает и выполнен ли шаг B3 после распаковки. Не удаляйте изображения и таблицу привязки для обхода ошибки.