[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"docs-recipes\u002Fdb-dump-restore":3,"docs-tabs-recipes\u002Fdb-dump-restore":6},{"content":4,"lastmod":5},"# Загрузка дампа БД на сервер\n\n🗄️ **Сложность:** medium | **Скоупы:** vibe:infra | **Стек:** cURL, psql \u002F pg_restore\n\nСценарий: восстановить дамп PostgreSQL (или другой СУБД) в базу, доступную с BLACKHOLE-сервера. BLACKHOLE (BH) — сервер за защищённым туннелем, к которому идут запросы `exec`, `upload` и `logs`. Порядок из четырёх шагов: файл на сервер, инструменты нужной версии, восстановление в фоновом режиме, проверка счётчиков.\n\nВо всех примерах подставьте свои значения: `$VIBE_URL` — базовый URL `https:\u002F\u002Fvibecode.bitrix24.tech`, `$SERVER_ID` — идентификатор сервера, `$VIBE_API_KEY` — API-ключ Вайбкод со скоупом `vibe:infra`. Плейсхолдеры подключения к БД — `DB_HOST`, `DB_USER`, `DB_NAME`.\n\n### Шаг 0. Каталог для данных — вне \u002Fopt\u002Fapp\n\nКаталог `\u002Fopt\u002Fapp` очищается при каждом деплое (`cleanDeploy: true` по умолчанию), а `\u002Fopt\u002Fdata` и установленные среды выполнения переживают редеплои. Дамп и рабочие файлы кладите в `\u002Fopt\u002Fdata`:\n\n```bash\ncurl -sS -X POST \"$VIBE_URL\u002Fv1\u002Finfra\u002Fservers\u002F$SERVER_ID\u002Fexec\" \\\n  -H \"X-Api-Key: $VIBE_API_KEY\" -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\"command\": \"mkdir -p \u002Fopt\u002Fdata\"}'\n```\n\n### Шаг 1. Загрузить дамп через \u002Fupload\n\nМегабайты base64 не помещаются в аргумент shell-команды из-за лимита `ARG_MAX` — соберите тело запроса в файл и отправьте его через `--data-binary`. Скрипт кодирует `db.dump` в base64 и складывает в `upload.json`, затем `curl` отправляет этот файл:\n\n```bash\npython3 - \u003C\u003C'EOF'\nimport base64, json\ndata = base64.b64encode(open('db.dump', 'rb').read()).decode()\njson.dump({'content': data, 'path': '\u002Fopt\u002Fdata\u002Fdb.dump', 'mode': '0644'}, open('upload.json', 'w'))\nEOF\n\ncurl -sS -X POST \"$VIBE_URL\u002Fv1\u002Finfra\u002Fservers\u002F$SERVER_ID\u002Fupload\" \\\n  -H \"X-Api-Key: $VIBE_API_KEY\" -H \"Content-Type: application\u002Fjson\" \\\n  --data-binary @upload.json\n```\n\nАльтернатива — поле `url` вместо `content`: агент скачает файл по ссылке сам, без base64.\n\n### Шаг 2. Инструменты той же мажорной версии, что целевая БД\n\nВосстановление падает, если мажорная версия клиента не совпадает с версией целевой базы. Дамп из `pg_dump` версии 17 в базе версии 16 даёт ошибку класса `unsupported version`: формат архива 1.16 не читается, а внутри дампа встречаются метакоманды `SET transaction_timeout` и `\\restrict`, которых нет в 16-й версии. Установите клиент нужной мажорной версии один раз — каталог `\u002Fusr` переживает деплой, повторная установка при редеплоях не нужна:\n\n```bash\ncurl -sS -X POST \"$VIBE_URL\u002Fv1\u002Finfra\u002Fservers\u002F$SERVER_ID\u002Fexec\" \\\n  -H \"X-Api-Key: $VIBE_API_KEY\" -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\"command\": \"apt-get install -y postgresql-client-17\"}'\n```\n\nЕсли установка тяжёлая и выходит за лимит `timeout` (максимум 600 секунд), запустите её фоновой задачей через `systemd-run` — задача получает собственную cgroup и не прерывается по таймауту `exec`:\n\n```bash\ncurl -sS -X POST \"$VIBE_URL\u002Fv1\u002Finfra\u002Fservers\u002F$SERVER_ID\u002Fexec\" \\\n  -H \"X-Api-Key: $VIBE_API_KEY\" -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\"command\": \"systemctl reset-failed install-pg 2>\u002Fdev\u002Fnull; systemd-run --unit=install-pg \u002Fbin\u002Fbash -c \\\"apt-get install -y postgresql-client-17\\\"\", \"timeout\": 30}'\n\ncurl -sS -H \"X-Api-Key: $VIBE_API_KEY\" \\\n  \"$VIBE_URL\u002Fv1\u002Finfra\u002Fservers\u002F$SERVER_ID\u002Flogs?service=install-pg&lines=50\"\n```\n\n### Шаг 3. Restore — фоновой задачей, с остановкой приложения\n\nВосстановление большого дампа занимает минуты, а на таймауте `exec` агент завершает всю группу процессов сигналом `SIGKILL` без паузы на корректное завершение. Поэтому восстановление всегда идёт фоновой задачей. Оформите его отдельным скриптом и загрузите через `\u002Fupload` с правами `mode: \"0755\"` — так же, как дамп в шаге 1:\n\n```bash\n#!\u002Fbin\u002Fbash\nset -euo pipefail\nexport PGPASSWORD=\"…\"           # либо ~\u002F.pgpass; НЕ в command — команды журналируются\nPSQL=(psql -h DB_HOST -p 5432 -U DB_USER -d DB_NAME)\n\nsystemctl stop app               # запросы приложения держат блокировки — TRUNCATE\u002FCOPY встанут в очередь\n\"${PSQL[@]}\" -c 'TRUNCATE TABLE t1, t2 RESTART IDENTITY CASCADE;'\npg_restore --data-only --no-owner --no-privileges \\\n  -h DB_HOST -p 5432 -U DB_USER -d DB_NAME \u002Fopt\u002Fdata\u002Fdb.dump\nsystemctl start app\necho \"RESTORE COMPLETE\"\n```\n\nПароль базы передаётся через переменную окружения `PGPASSWORD` или файл `~\u002F.pgpass`, но не через текст команды — агент журналирует первые ~200 символов команды, и секрет попал бы в журнал. Приложение останавливается на время восстановления: его запросы держат блокировки, и без остановки `TRUNCATE` и `COPY` встанут в очередь.\n\nДва нюанса `pg_restore --data-only`, которые всплывают на связанных таблицах:\n\n- **Порядок таблиц — алфавитный, не по зависимостям.** Данные льются в алфавитном порядке имён таблиц, поэтому дочерняя таблица может пойти раньше родительской и упасть на нарушении внешнего ключа. Лекарство — переупорядоченный список объектов: `pg_restore --list db.dump > toc.list`, переставьте строки данных так, чтобы родительские таблицы шли первыми, и восстанавливайте с `pg_restore -L toc.list …`.\n- **Схему миграций ORM исключите из восстановления.** Drizzle, Prisma и похожие инструменты держат собственную таблицу миграций в отдельной схеме (например, `drizzle.__drizzle_migrations`): дамп несёт её данные, а приложение при старте могло уже вставить туда свежую строку — `COPY` упадёт на дубликате ключа. Исключайте схему целиком, например `pg_restore -N drizzle …`, — таблицей миграций управляет само приложение.\n\nОстановка `systemctl stop app` в скрипте выше закрывает и третий капкан: юнит с `Restart=on-failure` перезапустил бы упавшее приложение в промежутке между `TRUNCATE` и стартом `COPY`, и оно успело бы записать свои строки — одной секунды достаточно. Если во время долгого восстановления возможен внешний запуск приложения (например, параллельный `\u002Fdeploy`), усильте остановку до `systemctl mask app`, а перед финальным запуском снимите маску: `systemctl unmask app && systemctl start app`.\n\nЗапуск и контроль. Единица `systemd-run` даёт задаче собственную cgroup, поэтому она переживает таймаут `exec`. Перед повторным запуском под тем же именем сбросьте прежнее состояние через `systemctl reset-failed`:\n\n```bash\ncurl -sS -X POST \"$VIBE_URL\u002Fv1\u002Finfra\u002Fservers\u002F$SERVER_ID\u002Fexec\" \\\n  -H \"X-Api-Key: $VIBE_API_KEY\" -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\"command\": \"systemctl reset-failed restore-db 2>\u002Fdev\u002Fnull; systemd-run --unit=restore-db \u002Fbin\u002Fbash \u002Fopt\u002Fdata\u002Frestore.sh\", \"timeout\": 30}'\n\ncurl -sS -H \"X-Api-Key: $VIBE_API_KEY\" \\\n  \"$VIBE_URL\u002Fv1\u002Finfra\u002Fservers\u002F$SERVER_ID\u002Flogs?service=restore-db&lines=50\"\n```\n\nАтомарный вариант — `psql --single-transaction -v ON_ERROR_STOP=1`: восстановление идёт одной транзакцией, ошибка видна сразу и откатывает загрузку целиком. Без него возможна ситуация «задача выполнялась часами, а загрузилось 0 строк», потому что `pg_restore` продолжает работу после ошибки.\n\n### Шаг 4. Проверить счётчики\n\nПосле восстановления сверьте количество строк в таблицах. Пароль базы здесь передаётся через поле `env`, а не в тексте команды:\n\n```bash\ncurl -sS -X POST \"$VIBE_URL\u002Fv1\u002Finfra\u002Fservers\u002F$SERVER_ID\u002Fexec\" \\\n  -H \"X-Api-Key: $VIBE_API_KEY\" -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\"command\": \"psql -h DB_HOST -U DB_USER -d DB_NAME -tAc \\\"SELECT count(*) FROM t1\\\"\", \"env\": {\"PGPASSWORD\": \"…\"}, \"timeout\": 60}'\n```\n\n## Смотрите также\n\n- [Выполнение команд на сервере](\u002Fdocs\u002Finfra\u002Fdeploy\u002Fexec) — раздел «Фоновые задачи»\n- [Загрузка файлов на сервер](\u002Fdocs\u002Finfra\u002Fdeploy\u002Fupload)\n- [Чтение логов сервера](\u002Fdocs\u002Finfra\u002Fdeploy\u002Flogs)\n","2026-07-08",{}]