Эта страница описывает реальный продакшен-деплой с таргетом vps в versola-cli: Linux-сервер с нативным PostgreSQL, на котором CLI запускает сервисы Versola, их хранилище секретов (OpenBao) и реверс-прокси перед ними. Если вы просто хотите попробовать Versola, см. Установку — эта страница про то, как поднять (или передеплоить) настоящий инстанс.
Что CLI настраивает за вас
На vps versola-cli запускает всё, кроме базы данных:
central,auth,edge— Docker-контейнеры сnetwork_mode: host, каждый привязан к127.0.0.1.- OpenBao — хранилище, в котором лежат все секреты. CLI его запускает и при первом запуске сам инициализирует и настраивает.
- Реверс-прокси — официальный образ
nginx, вся конфигурация которого генерируется CLI. Он маршрутизирует ваш домен наauth/edge, отдаёт админ-консоль по/central/admin/и — если у вас ещё нет своего веб-сервера — занимает порты 80/443 и сам получает сертификат Let’s Encrypt.
Что остаётся на вас:
- PostgreSQL — нативная установка на сервере, не контейнер. CLI её не создаёт и ей не владеет; роль, базу и схемы вы создаёте один раз (SQL для роли CLI печатает сам).
Всё это работает как один Docker Compose-проект (versola-vps), поэтому versola status, down и uninstall охватывают весь деплой.
Требования
- Linux VPS с Docker и плагином Docker Compose v2.
- Нативный PostgreSQL 14+ и доступ суперпользователя к нему (
sudo -u postgres psql), чтобы создать роль, базу и схемы Versola. - Домен для Versola (например,
id.example.com) с DNS-записьюA(иAAAA, если у сервера есть IPv6), указывающей на сервер. - Либо свободные порты 80 и 443 (их займёт прокси Versola и сам займётся TLS), либо ваш собственный веб-сервер на 80/443, который будет пробрасывать домен в Versola — см. Шаг 2.
versola-cli, установленный на самом VPS. Проверка памяти дляvpsчитает/proc/meminfoна той машине, где запущена, а в режимеexternalпрокси слушает только127.0.0.1сервера.- Достаточно свободной памяти: примерно 1 ГиБ, если Versola уже запущена (передеплой), и примерно 3 ГиБ для холодного старта — чуть больше, если OpenBao тоже ещё не поднят.
Шаг 0 — установка или обновление versola-cli
curl -fsSL https://raw.githubusercontent.com/versolauth/versola-cli/main/install.sh | sh
export PATH="$HOME/.local/bin:$PATH" # установщик не может изменить текущий shell
CLI ставится в ~/.local/bin. Если этой папки ещё нет в PATH, строка export делает versola доступной в текущем shell; добавьте её и в стартовый файл вашего shell (~/.bashrc, или ~/.zshrc для zsh), как подсказывает установщик, чтобы CLI находился и в новых терминалах.
Если CLI уже установлен, versola upgrade проверит последний релиз и заменит бинарник на месте (сборку из исходников, dev, он трогать откажется).
Шаг 1 — запустите doctor
versola doctor --target vps
Проверяет, что Docker доступен, плагин Compose v2 на месте и свободной памяти и места на диске хватает. configure/migrate/up делают те же проверки сами, так что шаг необязательный, но показывает полную картину заранее.
Шаг 2 — выберите, как Versola доступна снаружи
Флаг --proxy у configure/bootstrap решает, где слушает прокси Versola.
--proxy nginx (по умолчанию) — Versola занимает 80/443
Для сервера, на котором больше ничего не работает на веб-портах. Прокси слушает 80 и 443; при https:// в --auth-url он сам получает и продлевает сертификат Let’s Encrypt (ACME-модуль самого nginx, без certbot) и перенаправляет HTTP на HTTPS.
- Для TLS
--auth-urlдолжен быть ровноhttps://<домен>— без порта и пути, не IP-адрес и неlocalhost. (Обычныйhttp://тоже работает, но без сертификата — только для тестов.) - DNS уже должен указывать на сервер, а порты 80/443 — быть доступны из интернета; если что-то уже занимает порт,
configureсразу упадёт и назовёт его. - Сертификаты хранятся в Docker-томе
versola-acme-vps, поэтому передеплой не запрашивает новый. - Для тестового деплоя добавьте
--acme-staging: сертификат придёт из staging-среды Let’s Encrypt — браузеры ему не доверяют, зато нет лимитов продакшена.
--proxy external — за вашим веб-сервером
Для сервера, который уже обслуживает другие сайты на 80/443 со своим TLS. Тогда прокси Versola слушает только 127.0.0.1:2821 (обычный HTTP), а ваш веб-сервер пробрасывает на него домен и сам продолжает заниматься TLS. Он должен работать на том же хосте и передавать нужные заголовки — для nginx:
server {
server_name id.example.com;
listen 443 ssl;
# ssl_certificate ... — ваша существующая настройка TLS
client_max_body_size 8m;
location / {
proxy_pass http://127.0.0.1:2821;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Connection "";
}
}
Host— передаётся как есть. Versola ему не доверяет: issuer, адреса редиректов иhtuдля DPoP берутся из--auth-url, а не из заголовков запроса.X-Forwarded-For— ему доверяют только от127.0.0.1; Versola восстанавливает из него реальный адрес клиента и использует его для rate limiting.X-Forwarded-Proto— схема, по которой пришёл браузер; стандартный заголовок для прокси, терминирующего TLS (сама Versola берёт схему из--auth-url).client_max_body_size 8m— админ-консоль загружает темы и ключи; ваш сервер должен пропускать такой же размер тела, как прокси Versola.
Вся маршрутизация Versola (какой путь идёт в auth, какой в edge, админ-консоль) — внутри прокси Versola; ваш веб-сервер просто пробрасывает всё по домену.
Шаг 3 — configure
versola configure vps <version> \
--auth-url https://id.example.com \
--postgres-host 127.0.0.1:5432
# добавьте --proxy external для второго варианта из Шага 2
--auth-url должен в точности совпадать с origin, который видит браузер — схема и хост (порт только если он нестандартный, и никогда в режиме nginx). CLI отклоняет пути и query-строки (а хост приводит к нижнему регистру): несовпадение молча ломает редиректы и passkeys.
configure проверяет машину, тянет ghcr.io/versolauth/versola-tools:<version>, чтобы сгенерировать конфигурацию и админ-консоль этого релиза, пишет конфигурацию прокси и разрешает все секреты через OpenBao. Он не запускает ни одного сервиса Versola и не трогает базу — его можно запускать сколько угодно раз. Как и migrate и up, на vps он сначала просит подтверждение.
OpenBao настраивается автоматически. При первом запуске configure поднимает контейнер versola-openbao-vps и настраивает его: инициализация с одним ключом распечатки, распечатка, KV v2, аутентификация AppRole и политика, ограниченная секретами Versola. В ~/.versola/openbao/ он хранит два файла:
vps.json— учётные данные AppRole, с которыми CLI читает и пишет секреты. Они также печатаются при каждом запуске, включая secret ID, — учитывайте это, прежде чем куда-то вставлять выводconfigure;vps-admin.json— root token и ключ распечатки OpenBao. После каждого перезапуска OpenBao поднимается запечатанным;configureпересоздаёт контейнер, если он был остановлен, и распечатывает его этим ключом. Сохраните оба значения в надёжном месте вне сервера — восстановить их нельзя.
Каждый секрет — ключи подписи, ключи сессий, пароль Postgres, bootstrap-пароль админа — генерируется один раз, хранится в OpenBao и переиспользуется всеми последующими configure. Сами сервисы с OpenBao во время работы не общаются: configure записывает готовые значения рядом со сгенерированной конфигурацией, и OpenBao нужен только на время его запуска.
Хотите вести OpenBao сами? См. Управление OpenBao вручную.
Шаг 4 — создайте роль, базу и схемы Postgres
При первом configure пароль Postgres генерируется, поэтому роль можно создать только после этого запуска. configure печатает готовые команды:
Postgres: this was the first configure against this OpenBao, so a new password was
generated for Versola's Postgres role and stored there. Set it on the Postgres server
before `versola migrate` -- as the postgres superuser (e.g. sudo -u postgres psql):
CREATE ROLE "versola_app" WITH LOGIN PASSWORD '…'; -- if the role doesn't exist yet
ALTER ROLE "versola_app" WITH PASSWORD '…'; -- if it already exists
От имени суперпользователя Postgres выполните строку с CREATE ROLE (или ALTER ROLE, если вы создали роль заранее со своим паролем), затем базу и по одной схеме на сервис:
CREATE DATABASE auth OWNER versola_app;
\c auth
CREATE SCHEMA IF NOT EXISTS auth AUTHORIZATION versola_app;
CREATE SCHEMA IF NOT EXISTS central AUTHORIZATION versola_app;
CREATE SCHEMA IF NOT EXISTS edge AUTHORIZATION versola_app;
Выполняйте по одной команде — блок, в котором смешаны мета-команды psql (\c) и SQL, даёт непонятные ошибки разбора. Три сервиса используют одну базу, но у каждого своя схема; не пропускайте схемы — каждый сервис хранит историю миграций внутри своей схемы, и два сервиса в одной схеме падают с FlywayValidateException.
Пароль печатается только при этом первом запуске; дальше переиспользуется сохранённый.
Шаг 5 — бэкап базы (при передеплое)
Миграции нельзя откатить. При первой установке бэкапить ещё нечего; перед каждым следующим migrate снимайте дамп:
(umask 077; sudo -u postgres pg_dump -Fc auth > ~/versola-backup-$(date +%Y%m%d-%H%M%S).dump)
pg_dump работает от postgres, но пишет в stdout, поэтому файл создаёт ваш собственный shell — в вашей домашней папке, от вашего имени и с доступом только для вас (umask 077: в дампе хеши паролей и зашифрованные секреты). Храните копию где-то надёжно, а не только на сервере.
Шаг 6 — migrate
versola migrate
Применяет миграции каждого сервиса (auth, central и edge владеют каждый своей схемой) через один одноразовый контейнер, в котором есть миграции всех трёх сервисов и который завершается по окончании. Ни один сервер не запускается. Запуск записывается в ~/.versola, и status его показывает. Сами сервисы при старте миграции не применяют — они проверяют схему и отказываются стартовать на устаревшей.
Также доступно:
versola migrate --service auth # мигрировать по одному сервису
versola migrate --dry-run # проверить без применения
В релизах Versola до 0.6.2 включительно --dry-run сообщает о миграциях, которые просто ещё не применены, как об ошибке проверки («Detected resolved migration not applied to database») — это известная проблема dry-run, а не вашей базы; обычный versola migrate применяет их нормально.
Шаг 7 — up
versola up
Запускает стек и ждёт, пока он реально начнёт отвечать: сначала central (auth и edge синхронизируют с ним конфигурацию), затем auth и edge, последним — реверс-прокси; в конце проверяет, что запрос через прокси доходит до auth.
В режиме nginx с HTTPS, если сертификат не выпустился примерно за две минуты (DNS ещё не обновился, порт 80 недоступен), up выводит предупреждение, а не падает — сервисы подняты, а nginx продолжает попытки получить сертификат; смотрите docker logs versola-proxy.
При успехе печатает адрес, логин админа и файл с ADMIN_BOOTSTRAP_PASSWORD. Этот пароль временный, действует 24 часа: при первом входе с ним система попросит задать постоянный. Если он истёк до входа, перезапустите auth (docker restart versola-auth) — появится новый на 24 часа. В режиме external up также напомнит направить ваш веб-сервер на 127.0.0.1:2821.
Или всё сразу: bootstrap
versola bootstrap vps <version> --auth-url … --postgres-host … [--proxy external] выполняет Шаги 3, 6 и 7 за один раз. Если при первом запуске генерируется пароль Postgres, он останавливается перед миграциями (с ненулевым кодом выхода), чтобы вы выполнили SQL из Шага 4, — затем продолжите versola migrate и versola up.
Шаг 8 — проверка
versola status
Deployed version: 0.6.2 (target: vps)
Migrations applied: 2026-09-27 17:11:51
NAME IMAGE … STATUS
versola-auth ghcr.io/versolauth/versola-auth:0.6.2 … Up 2 minutes
versola-central ghcr.io/versolauth/versola-central:0.6.2 … Up 3 minutes
versola-edge ghcr.io/versolauth/versola-edge:0.6.2 … Up 2 minutes
versola-openbao-vps openbao/openbao:2.5.4 … Up 10 minutes
versola-proxy nginx:1.30-alpine … Up 1 minute
(Таблица — это docker compose ps деплоя, здесь в сокращённом виде.)
Затем снаружи:
AUTH_URL=https://id.example.com # ровно тот --auth-url, что передавали в configure
curl -s -o /dev/null -w "%{http_code}\n" "$AUTH_URL/.well-known/openid-configuration" # 200
и войдите в админ-консоль по <auth-url>/central/admin/, прежде чем считать деплой завершённым.
Передеплой
Новая версия — те же три шага с новым номером версии и теми же флагами, что при первом деплое: configure их не запоминает. В частности, не забудьте --proxy external, если использовали его: без него прокси вернётся в режим nginx по умолчанию, и configure остановится на портах 80/443 вашего веб-сервера.
versola upgrade # держите CLI свежим
# бэкап базы (Шаг 5)
# уберите --proxy external, если первый деплой был в режиме nginx по умолчанию
versola configure vps <new-version> --auth-url https://id.example.com --postgres-host 127.0.0.1:5432 --proxy external
versola migrate
versola up
configure заново генерирует конфигурацию, прокси и админ-консоль из этого релиза; секреты берутся из OpenBao. configure и migrate не трогают работающие сервисы, поэтому старая версия продолжает обслуживать запросы, пока идут миграции, — перерыв только на время, пока up пересоздаёт контейнеры.
Откат
Если новый релиз только добавлял миграции и предыдущая версия нормально работает на новой схеме, откат — это просто versola configure vps <предыдущая-версия> … с вашими обычными флагами и затем versola up (при необходимости поставьте подходящий CLI со страницы релизов).
Если миграции нужно отменить, восстановите базу до запуска предыдущей версии — никогда не запускайте её на схеме, которую понимает только новый релиз:
versola down # во время восстановления ничего не должно писать в базу
sudo -u postgres psql -c "DROP DATABASE auth;"
sudo -u postgres psql -c "CREATE DATABASE auth OWNER versola_app;"
sudo -u postgres pg_restore -d auth < ~/versola-backup-<timestamp>.dump # файл открывает ваш shell, а не postgres
# уберите --proxy external, если первый деплой был в режиме nginx по умолчанию
versola configure vps <предыдущая-версия> --auth-url https://id.example.com --postgres-host 127.0.0.1:5432 --proxy external
versola migrate # ничего не сделает: в дампе уже предыдущая схема
versola up
Всё, что было записано между дампом (Шаг 5) и откатом, теряется — поэтому дамп снимают прямо перед migrate.
Управление OpenBao вручную
Если у вас своя схема работы с OpenBao, передайте --setup-openbao в configure/bootstrap: тогда CLI никогда не обращается к admin API OpenBao, а настройку и сохранение учётных данных AppRole вы делаете сами. Распечатка после перезапуска тогда тоже на вас.
Порядок такой: один раз выполните versola configure vps … --setup-openbao — он запустит контейнер versola-openbao-vps и остановится с ошибкой «no OpenBao credentials stored», так и должно быть, — выполните шаги ниже, сохраните учётные данные через versola secrets login и снова запустите тот же configure. TLS на этом OpenBao выключен (он слушает только 127.0.0.1:8200), но CLI bao по умолчанию ходит по https, поэтому каждой команде нужен BAO_ADDR:
# 1. Инициализация — сохраните ОБА напечатанных значения; восстановить их нельзя.
docker exec -it -e BAO_ADDR=http://127.0.0.1:8200 versola-openbao-vps \
bao operator init -key-shares=1 -key-threshold=1
# 2. Распечатка — снова после каждого перезапуска контейнера.
docker exec -it -e BAO_ADDR=http://127.0.0.1:8200 versola-openbao-vps \
bao operator unseal <ключ распечатки>
# 3–4. KV v2 и AppRole.
docker exec -it -e BAO_ADDR=http://127.0.0.1:8200 -e BAO_TOKEN=<root token> versola-openbao-vps \
bao secrets enable -path=secret kv-v2
docker exec -it -e BAO_ADDR=http://127.0.0.1:8200 -e BAO_TOKEN=<root token> versola-openbao-vps \
bao auth enable approle
# 5. Политика, ограниченная секретами Versola.
docker exec -i -e BAO_ADDR=http://127.0.0.1:8200 -e BAO_TOKEN=<root token> versola-openbao-vps \
bao policy write versola-vps - <<'EOF'
path "secret/data/versola/vps/*" {
capabilities = ["create", "read", "update"]
}
EOF
# 6. Роль AppRole, привязанная к ней (долгоживущие учётные данные для автоматического инструмента).
docker exec -it -e BAO_ADDR=http://127.0.0.1:8200 -e BAO_TOKEN=<root token> versola-openbao-vps \
bao write auth/approle/role/versola-vps \
token_policies="versola-vps" token_ttl=1h token_max_ttl=4h \
secret_id_ttl=0 token_num_uses=0
# 7. Учётные данные, которые нужны CLI.
docker exec -it -e BAO_ADDR=http://127.0.0.1:8200 -e BAO_TOKEN=<root token> versola-openbao-vps \
bao read auth/approle/role/versola-vps/role-id
docker exec -it -e BAO_ADDR=http://127.0.0.1:8200 -e BAO_TOKEN=<root token> versola-openbao-vps \
bao write -f auth/approle/role/versola-vps/secret-id
Затем сохраните их — secret ID запрашивается скрытым вводом, поэтому не попадает в историю shell:
versola secrets login vps http://127.0.0.1:8200 <role-id>
Пишете политику на Windows? Сохраните HCL-файл в кодировке ASCII, а не в UTF-8 по умолчанию — BOM ломает парсер OpenBao с ошибкой
illegal char at 1:1. Создайте файл локально, скопируйте его черезdocker cpи выполнитеbao policy write versola-vps /путь/внутри/контейнера.hcl.
Изменение сохранённого секрета
Секреты лежат в secret/versola/vps/{auth,central,edge}. Используйте bao kv patch — kv put заменяет весь путь и сотрёт остальные ключи — с root token из ~/.versola/openbao/vps-admin.json:
docker exec -it -e BAO_ADDR=http://127.0.0.1:8200 -e BAO_TOKEN=<root token> versola-openbao-vps \
bao kv patch -mount=secret versola/vps/auth SOME_KEY='<значение>'
Затем выполните versola configure vps <текущая-версия> с вашими обычными флагами и versola up. POSTGRES_PASSWORD общий для всех трёх сервисов и должен совпадать в auth, central и edge — если значения расходятся, configure откажется продолжать.
Решение проблем
configure падает, потому что порт 80 или 443 занят. Там уже что-то слушает (обычно веб-сервер). Либо остановите его, либо используйте --proxy external за ним.
OpenBao не поднимается после перезагрузки или запечатан. Работающие сервисы это не затрагивает — OpenBao им во время работы не нужен. Следующий configure пересоздаст и распечатает его. С --setup-openbao распечатайте его сами (bao operator unseal).
«container name already in use». Контейнер с одним из фиксированных имён Versola существует в другом Compose-проекте. Проверьте через docker ps и docker compose ls, остановите и удалите эти контейнеры по имени и повторите запуск.
502 через ваш веб-сервер (режим external). Проверьте, что прокси отвечает локально: curl -H 'Host: id.example.com' http://127.0.0.1:2821/.well-known/openid-configuration. Если отвечает — проблема в пробросе вашего веб-сервера; если нет — смотрите versola status и docker logs versola-proxy.
Админ-консоль отдаёт 404 сразу после up. Обновите страницу — первый запрос сразу после старта может попасть на ещё прогревающийся сервис.
image not found. Проверьте формат версии — у тегов нет v в начале: 0.6.2, а не v0.6.2. Опубликованные версии — на странице пакета versola-tools.