К содержанию
ActionPulse

Конфигурация

Переменные окружения on-prem-установки ActionPulse по группам — обязательность, назначение, поведение при неверном значении и то, что нельзя менять после первого запуска.

Кому
Операторам установки
Проверено
На этой странице

Вся конфигурация установки — это переменные окружения. Файла настроек внутри сервисов нет, административного экрана для этих значений тоже нет: то, что описано ниже, задаётся до запуска и меняется перезапуском. Эта страница — справочник по всем группам переменных: что обязательно, зачем нужно и что произойдёт при неверном значении.

Как читается конфигурация

В поставляемом комплекте конфигурация лежит в одном файле .env рядом с docker-compose.onprem.yml. Compose подставляет значения в описания сервисов и передаёт в контейнер только те переменные, которые в описании этого сервиса перечислены. Переменная, добавленная в .env «на всякий случай», до приложения не дойдёт, если её никто не передаёт.

install -m 0600 .env.example .env
# отредактируйте .env
./preflight.sh .env
docker compose -f docker-compose.onprem.yml --env-file .env config --quiet

Проверка идёт тремя независимыми слоями, и все три работают на отказ, а не на «запустимся как-нибудь»:

  1. Compose. Обязательные переменные объявлены так, что без них docker compose отказывается выполнить любую команду — и config, и up. В выводе будет имя переменной и подсказка.
  2. ./preflight.sh .env. Права и целостность файла, длина и формат секретов, точные ссылки на образы, роли в строках подключения, права на внешние файлы секретов. Успешный прогон заканчивается строкой о том, что окружение, точные ссылки на образы и внешние файлы секретов прошли проверку.
  3. Сам сервис на старте. Форматы адресов, длины ключей, взаимные запреты. Неверное значение — это упавший на старте контейнер с сообщением, которое начинается с config:, а не тихая работа в ослабленном режиме.

Требования к самому файлу: обычный файл (не символьная ссылка), права 0600, и каждый ключ встречается в нём ровно один раз. Две строки с одним именем — остановка на preflight, а не «победит последняя».

Значения не перечитываются на ходу. Изменили .env — перезапустите затронутые сервисы. Единственное исключение — ключ лицензии: он загружается через API и подхватывается всеми сервисами не позже чем через секунду.

В шаблоне публичные адреса записаны через ${PP_DOMAIN}, поэтому домен достаточно указать в одном месте.

Адреса, порты и сети

Переменная Обязательна Назначение Если задать неверно
PP_DOMAIN да Домен установки. От него зависит выпуск сертификата и правила безопасности документов Compose не запустится; несуществующий домен — сертификат не будет выпущен
APP_BASE_URL да Публичный адрес кабинета. Определяет флаг Secure у cookie аутентификации и ссылки в письмах Схема http на реальном домене — cookie сессии без Secure
PUBLIC_INGEST_URL да Публичный адрес приёма событий, объявленный установкой Compose не запустится; расхождение с фактическим адресом путает интеграторов
ADMIN_APP_BASE_URL да Отдельный origin служебного кабинета вендора. В on-prem он не монтируется, значение задаётся явно, чтобы приложение не унаследовало небезопасный локальный адрес по умолчанию Compose не запустится
CORS_ORIGINS да Список origin, которым разрешены запросы к API с передачей учётных данных Слишком широкий список ослабляет защиту от межсайтовых запросов
QUERY_TRUSTED_PROXY_CIDRS нет Диапазоны прямых обратных прокси, чьим заголовкам с адресом клиента доверяет кабинет и аутентификация Пусто — учитывается только адрес сокета; лишний диапазон позволяет подделать адрес клиента
INGEST_TRUSTED_PROXY_CIDRS нет То же для приёма событий То же
PP_EDGE_SUBNET, PP_EDGE_PROXY_IP нет Изолированная сеть и постоянный адрес обратного прокси Пересечение с сетью узла или VPN — недоступность сервисов
PP_APP_SUBNET, PP_DATA_SUBNET, PP_OBSERVABILITY_SUBNET нет Внутренние сети приложений, хранилищ и наблюдаемости То же

Значения по умолчанию для сетей приходят в шаблоне. Доверенные диапазоны по умолчанию равны адресу поставляемого прокси с маской /32: доверяется ровно один сосед. Если вы меняете адрес прокси или ставите перед установкой свой — меняйте оба списка вместе с ним.

Адрес клиента вычисляется так: берётся адрес сокета, и только если он попал в доверенный диапазон, разбирается заголовок с цепочкой пересылки — справа налево, до первого недоверенного узла. Поэтому лишний диапазон в списке — это возможность подставить чужой адрес в ограничение частоты запросов и в журнал безопасности, а недостающий — один общий адрес на всех посетителей.

Порты сервисов в поставляемом compose фиксированы: кабинет и API — 8080, приём событий — 8081, метрики — 9090. Наружу публикуются только 80 и 443 на обратном прокси; см. TLS и секреты.

Хранилища и очереди

Каждому сервису положено иметь собственную учётную запись в каждом хранилище. Пока переменные с ролями пустые, все сервисы работают под общей учётной записью и каждый старт печатает предупреждение с датой отсечки. Роли создаются скриптом ./postgres/roles.sql, файлом списка доступа Redis и конфигурацией NATS.

PostgreSQL

Переменная Обязательна Назначение Если задать неверно
PG_PASSWORD да Пароль общей учётной записи. Из него собираются строки подключения сервисов, у которых не задана своя роль Короче 24 символов или похоже на заглушку — остановка на preflight
PP_MIGRATOR_PG_DSN нет Строка подключения роли миграций — единственной, которой разрешены изменения схемы Роль в строке не совпадает с ожидаемой — остановка на preflight
PP_QUERY_PG_DSN нет Роль кабинета и API То же
PP_INGEST_PG_DSN нет Роль приёма событий, самая узкая по правам То же
PP_WRITER_PG_DSN нет Роль записи в аналитическое хранилище То же
PP_WORKER_PG_DSN нет Роль фоновых задач То же

Preflight проверяет не только формат, но и имя роли внутри строки: опечатка не приведёт к тихому возврату на общую учётную запись.

Аналитическое хранилище (ClickHouse)

Переменная Обязательна Назначение Если задать неверно
CH_DEFAULT_PASSWORD да Пароль совместимой учётной записи. Тем же значением аутентифицируется точка входа образа при первой инициализации Расхождение с паролем в конфигурации пользователей — первая установка падает с ошибкой аутентификации
CH_MIGRATE_PASSWORD да Пароль роли миграций Остановка на preflight при длине меньше 24 символов
CH_WRITE_PASSWORD да Пароль роли записи То же
CH_READ_PASSWORD да Пароль роли чтения То же
CH_BACKUP_PASSWORD да Пароль роли резервного копирования, только чтение То же
CH_CONSOLE_PASSWORD да Пароль пользователя SQL-консоли. Создаётся миграцией и выдаётся только одноразовому контейнеру миграций То же; смена после первой миграции не меняет пароль в хранилище
CH_MIGRATE_DSN да Строка подключения роли миграций Другая роль или другой хост — остановка на preflight
CH_WRITE_DSN да Строка подключения роли записи То же
CH_READ_DSN да Строка подключения роли чтения То же
CH_CONSOLE_DSN да в compose Строка подключения SQL-консоли. В Helm-чарте необязательна: без неё консоль выключена Другая роль — остановка на preflight

Пароли внутри строк подключения должны быть закодированы для URL. Каждая строка обязана указывать на внутреннее имя хоста хранилища и содержать ровно свою роль — это проверяется до запуска.

Очередь событий (NATS с JetStream)

Переменная Обязательна Назначение Если задать неверно
NATS_URL да Адрес очереди с учётными данными, схема nats или tls Без учётных данных или с секретом короче 16 символов — остановка на preflight
NATS_CONFIG_FILE да Путь к конфигурации сервера очереди вне репозитория, права 0600 Отсутствие файла, симлинк или лишние права — остановка на preflight
PP_INGEST_NATS_URL, PP_QUERY_NATS_URL, PP_WRITER_NATS_URL, PP_WORKER_NATS_URL нет Отдельные учётные записи сервисов с правами только на свои темы Чужое имя учётной записи — остановка на preflight
PP_NATS_BOOTSTRAP_URL нет Одноразовая учётная запись, создающая потоки. Долгоживущему сервису её выдавать нельзя Появление этой учётной записи в списке выше — остановка на preflight
PP_LIFECYCLE_NATS_URL нет Привилегированная учётная запись очистки при удалении организации, выдаётся только фоновым задачам То же
NATS_STREAM_MAX_BYTES, NATS_STREAM_MAX_MSGS нет Верхние границы потока событий Значение ниже уже накопленного объёма — отказ на старте, а не молчаливая потеря
NATS_REPLAY_STREAM_MAX_BYTES, NATS_REPLAY_STREAM_MAX_MSGS нет То же для потока записей сессий То же
NATS_IMPORT_FENCE_STREAM_MAX_BYTES, NATS_IMPORT_FENCE_STREAM_MAX_MSGS нет То же для потока маркеров исторического импорта То же

Пустые границы сохраняют текущие лимиты потока. Прежде чем понижать их, измерьте фактический объём: старт отвергнет границу, которая уже меньше накопленного.

Кэш (Redis)

Переменная Обязательна Назначение Если задать неверно
REDIS_URL да Адрес кэша с учётными данными, схема redis или rediss Без учётных данных или с секретом короче 16 символов — остановка на preflight
REDIS_ACL_FILE да Путь к файлу списка доступа вне репозитория, права 0600 Анонимный пользователь не выключен или нет включённого пользователя с паролем — остановка на preflight
PP_INGEST_REDIS_URL, PP_QUERY_REDIS_URL, PP_WRITER_REDIS_URL, PP_WORKER_REDIS_URL нет Отдельные учётные записи сервисов с доступом только к своим префиксам ключей Чужое имя учётной записи — остановка на preflight

Проверка готовности кэша ждёт отказа в доступе без аутентификации. Обычный ответ на пинг здесь считается ошибкой конфигурации: он означает, что анонимный доступ остался открытым.

Почта, уведомления и наблюдаемость

Почта

Письма аутентификации — подтверждение адреса, восстановление и смена пароля, приглашения, события второго фактора — ставятся в устойчивую очередь той же транзакцией, что и сам факт, и отправляются фоновой задачей. Поэтому установка без SMTP допустима: письма не теряются, а накапливаются, приглашения при этом раздаются ссылкой вручную.

Переменная Обязательна Назначение Если задать неверно
SMTP_HOST нет Адрес почтового relay. Пусто — отправка выключена, старт пишет предупреждение о деградации Недоступный адрес — письма остаются в очереди и растёт задержка
SMTP_PORT нет Порт relay, по умолчанию 587 Несовпадение с режимом TLS — отказ доставки
SMTP_USER, SMTP_PASS нет Учётные данные relay Неверные — доставка отказывает с классом ошибок аутентификации, есть алерт
SMTP_FROM нет Адрес отправителя Домен без права отправки — письма попадут в спам
SMTP_FROM_NAME, SMTP_REPLY_TO нет Отображаемое имя и адрес для ответа
SMTP_TLS_MODE нет Требование к транспорту: starttls по умолчанию, implicit, opportunistic, disabled starttls при relay без поддержки — доставка отказывает; disabled принимается только для петлевого адреса
EMAIL_OUTBOX_ENC_KEY нет Отдельный ключ шифрования содержимого очереди писем. Пусто — выводится из JWT_SECRET Смена ключа делает недоставленные письма нечитаемыми
EMAIL_WORKER_INTERVAL нет Период работы задачи доставки, по умолчанию 15 секунд Значение вне диапазона от 1 секунды до 1 часа — отказ на старте
EMAIL_WORKER_BATCH_SIZE нет Писем за один проход, по умолчанию 25 Значение вне диапазона от 1 до 500 — отказ на старте
EMAIL_MAX_ATTEMPTS нет После какого числа попыток письмо считается недоставленным, по умолчанию 8 Слишком мало — письма умирают при коротком сбое relay
EMAIL_RETENTION нет Хранение завершённых строк очереди, по умолчанию 720 часов Меньше 24 часов — отказ на старте
DEV_DELIVERY_LOG_SECRETS нет Только для локальной проверки: печать содержимого письма в журнал true при непетлевом адресе кабинета — отказ на старте

Режим starttls по умолчанию требует, чтобы сервер объявил поддержку шифрования, и отказывает в доставке иначе. Молчаливого понижения до открытого текста нет: одноразовые коды и ссылки восстановления не должны идти по сети в открытом виде. Для внутреннего relay без поддержки нужен осознанный opportunistic.

Каналы уведомлений об алертах, помимо почты: SMS_PROVIDER (disabled по умолчанию, также mock, aero, smsc) с парами SMSAERO_EMAIL и SMSAERO_APIKEY либо SMSC_LOGIN и SMSC_PASSWORD, а также TG_BOT_TOKEN.

Наблюдаемость

Стек метрик, логов и трассировки поднимается вместе с приложением обычным запуском — отдельного профиля у него нет. Ни один его сервис не публикует порт на узел: доступ только через контролируемый временный проброс или VPN.

Переменная Обязательна Назначение Если задать неверно
GRAFANA_ADMIN_PASSWORD да Пароль администратора панелей Пусто или заглушка — остановка на preflight
PROMETHEUS_RETENTION нет Хранение метрик, по умолчанию 15 дней Слишком большое значение упирается в диск, есть алерты на свободное место
ELASTIC_PASSWORD да Пароль хранилища логов Пусто или короче 24 символов — остановка на preflight
KIBANA_SYSTEM_PASSWORD да Служебный пароль интерфейса логов То же
ELASTICSEARCH_FILEBEAT_USERNAME да Имя учётной записи доставки логов с минимальными правами Недопустимые символы — остановка на preflight
ELASTICSEARCH_FILEBEAT_PASSWORD да Её пароль Пусто или короче 24 символов — остановка на preflight
ELASTIC_CA_FILE, ELASTICSEARCH_CERT_FILE, KIBANA_CERT_FILE да Пути к удостоверяющему центру и сертификатам внутреннего TLS Файла нет, он пустой или это симлинк — остановка на preflight
ELASTICSEARCH_KEY_FILE, KIBANA_KEY_FILE да Пути к закрытым ключам, права 0600 Лишние права — остановка на preflight
LOG_RETENTION_DAYS нет Срок хранения логов, по умолчанию 30 дней. В шаблоне не выведена — добавьте строку сами, если нужно другое значение Не целое положительное — одноразовая настройка хранилища логов падает
ES_JAVA_OPTS нет Размер памяти хранилища логов, по умолчанию 1 ГиБ Больше доступной памяти — контейнер не поднимется
OTEL_EXPORTER_OTLP_ENDPOINT нет Приёмник трассировки, по умолчанию внутренний Недоступный адрес — трассировка молча не собирается
GRAFANA_URL, KIBANA_URL, JAEGER_URL нет Достижимые для вас адреса интерфейсов. Пусто — ссылки скрыты Недостижимый адрес — нерабочая ссылка на служебном экране
LOG_LEVEL нет Уровень журналирования приложений, по умолчанию info debug в продакшене увеличивает объём логов

Без одноразовой настройки хранилища логов ничего не удаляет старые данные: она же создаёт учётную запись доставки и политику срока хранения. Приёмник трассировки в комплекте держит трейсы в памяти — после перезапуска их нет.

Приём, лицензия и режим работы

Публичный адрес приёма

PUBLIC_INGEST_URL — публичный адрес, по которому установка принимает события. В поставляемой конфигурации приём и кабинет живут на одном origin: запросы к путям приёма уходят на сервис приёма, остальное — на кабинет и API. Значение обязательно и должно совпадать с адресом, который вы действительно публикуете, иначе сниппеты и серверные интеграции будут смотреть не туда.

Лимит приёма — 1000 единиц в секунду на префикс ключа, где единица это одно событие или один КиБ тела запроса, что больше. Бюджет общий для всех реплик. Поставляемый compose не передаёт переменную этого лимита в контейнер, поэтому в этой конфигурации он не настраивается.

Обогащение и исходящие соединения

Переменная Обязательна Назначение Если задать неверно
GEOIP_DB_DIR нет Каталог на узле с базой «страна по адресу». В шаблоне закомментирован Указан не тот каталог — база не окажется в контейнере
GEOIP_DB_PATH нет Путь к базе внутри контейнера. Пусто — определение страны выключено Путь не совпадает с примонтированным файлом — обогащение молча не работает
ALERT_WEBHOOK_ENC_KEY для исходящих уведомлений Шифрование секрета подписи исходящих уведомлений Не ровно 32 байта до кодирования — канал с секретом не создаётся и доставка отказывает; старт при этом проходит
ALERT_WEBHOOK_ALLOW_PRIVATE нет Разрешить уведомления на адреса внутренней сети, по умолчанию выключено Включение расширяет поверхность исходящих запросов
ALERT_WEBHOOK_ALLOW_HTTP нет Разрешить уведомления без TLS, по умолчанию выключено То же
CLICKHOUSE_IMPORT_ALLOW_PRIVATE нет Разрешить импорт из аналитического хранилища во внутренней сети, по умолчанию выключено То же

Сама база «страна по адресу» в комплект не входит — её предоставляет оператор. Определение идёт только до страны. Послабления для внутренней сети доступны исключительно в режиме on-prem; адреса служб метаданных облачных провайдеров запрещены при любых значениях этих переменных.

Ключи исторического импорта

Четыре независимых, попарно различных материала. Ни один из них не выводится из JWT_SECRET, и приложение отказывается стартовать, если значения совпадают между собой или с JWT_SECRET.

Переменная Обязательна Назначение
IMPORT_ENC_KEY да Шифрование сохранённых строк подключения к источникам импорта
IMPORT_HMAC_CURRENT_VERSION, IMPORT_HMAC_CURRENT_SECRET да Подпись передачи импортированных строк между сервисами
IMPORT_FENCE_HMAC_CURRENT_VERSION, IMPORT_FENCE_HMAC_CURRENT_SECRET да Подпись маркеров устойчивости импорта и подтверждений
IMPORT_PRIVACY_HMAC_CURRENT_VERSION, IMPORT_PRIVACY_HMAC_CURRENT_SECRET да Устойчивые обезличенные токены субъектов для удаления данных
IMPORT_HMAC_PREVIOUS_KEYS, IMPORT_FENCE_HMAC_PREVIOUS_KEYS, IMPORT_PRIVACY_HMAC_PREVIOUS_KEYS нет Приём прежних версий во время ротации, формат версия:секрет, не больше четырёх записей
IMPORT_PRIVACY_HMAC_SECRET нет Совместимость с первой версией. На новой установке оставьте пустым

Секреты не могут содержать запятые, пробелы и управляющие символы, и должны быть не короче 32 символов. Порядок ротации — в статье TLS и секреты.

Лицензия и первый владелец

Переменная Обязательна Назначение Если задать неверно
LICENSE_PUBKEY да Публичный ключ вендора для офлайн-проверки лицензии Без него запись данных отклоняется с ошибкой недействительной лицензии
BOOTSTRAP_EMAIL до первого владельца Адрес первого владельца, создаётся при первом старте Домен вида example.com или адрес не в нижнем регистре — остановка на preflight
BOOTSTRAP_PASSWORD до первого владельца Его пароль, не короче 16 символов Задан без адреса или наоборот — остановка на preflight

Сам ключ лицензии в .env не хранится: он загружается владельцем через кабинет или методом API — см. раздел лицензии в справочнике API. Без действующей лицензии операции записи возвращают отказ, а чтение продолжает работать; после истечения даётся неделя в режиме только для чтения.

После появления первого владельца очистите оба значения BOOTSTRAP_*, оставив ключи в файле пустыми, и перезапустите сервис кабинета. Preflight требует, чтобы эти две переменные были заполнены или пусты вместе.

Режим работы

Режим — это MODE, и в поставляемом compose он жёстко задан значением onprem: операторской настройки здесь нет. Что меняет режим:

  • маршруты биллинга не подключаются вовсе, счетов и подписок в установке нет;
  • квоты и набор возможностей берутся из подписанной лицензии, а не из тарифного каталога;
  • служебный кабинет вендора не подключается;
  • первый владелец создаётся из BOOTSTRAP_*, а не регистрацией;
  • становятся доступны послабления для внутренней сети, перечисленные выше, — в облачном режиме их наличие вообще запрещает старт;
  • внешняя телеметрия выключена, пока её не включат явно.
Переменная Обязательна Назначение Если задать неверно
TELEMETRY_OPTIN нет Отправка ping об установке, по умолчанию false При true без корректного идентификатора установки — отказ на старте
TELEMETRY_INSTANCE_ID при включённой телеметрии Псевдоним установки в формате UUID, не идентификатор организации и не имя узла Не UUID — отказ на старте
TELEMETRY_ENDPOINT нет Приёмник, по умолчанию публичный адрес вендора Не HTTPS, с учётными данными, запросом или фрагментом — отказ на старте
TELEMETRY_ALLOW_PRIVATE_ENDPOINT нет Разрешить приёмник во внутренней сети, по умолчанию false
PP_VERSION заполнена в шаблоне Версия релиза, проставляется автоматикой сборки Правка вручную ломает соответствие версии и ссылок на образы; при включённой телеметрии недопустимое значение — отказ на старте

При выключенной телеметрии клиент внешних запросов вообще не создаётся. При включённой уходит фиксированный набор из четырёх полей: версия схемы, название продукта, псевдоним установки и версия. События, пользователи, идентификаторы организаций и проектов, настройки и имя узла не отправляются.

Сроки хранения

В on-prem сроки хранения заданы режимом работы, а не переменной окружения: события — 25 месяцев, записи сессий — 90 дней. Переменной, которая бы это изменила, нет, и увеличить срок настройкой нельзя: те же значения стоят верхними границами на таблицах аналитического хранилища. Ежедневная фоновая задача удаляет просроченное и уступает очередь запросам на удаление данных субъекта.

Остальные сроки настраиваются: логи — LOG_RETENTION_DAYS, метрики — PROMETHEUS_RETENTION, завершённые письма — EMAIL_RETENTION. Очередь событий держит уже принятые события не дольше 48 часов, а записи сессий — не дольше суток; это транспортное окно, а не хранилище. Журнал действий живёт по своим правилам: обычные записи год, обязательные семь лет. Подробности — сроки хранения данных.

Что нельзя менять после первого запуска

Значение Почему
PG_PASSWORD Пароль записывается при инициализации тома базы. Смена в .env не меняет пароль в самой базе — сервисы перестанут аутентифицироваться. Меняйте одновременно в базе и в файле
CH_CONSOLE_PASSWORD Пользователь SQL-консоли создаётся миграцией один раз; повторный прогон миграций пароль не переписывает
Диапазоны сетей Сети создаются при первом запуске. Пересечение с сетью узла или VPN лечится только пересозданием сетей, то есть остановкой всех сервисов
MFA_ENCRYPTION_KEY Им шифруются семена второго фактора. Задайте до первого подключения 2FA: появление или смена значения после делает сохранённые семена нечитаемыми
IMPORT_ENC_KEY Сохранённые настройки импорта зашифрованы им без версии, поэтому постепенная ротация небезопасна
IMPORT_PRIVACY_HMAC_CURRENT_VERSION Версию нельзя переиспользовать с другим секретом: сервисы фиксируют отпечаток и отказываются работать при несовпадении
PP_DOMAIN и публичные адреса Смена домена ломает выданные ссылки в письмах, список разрешённых origin у браузерных токенов и уже включённое требование HTTPS у браузеров
MODE В поставляемом compose зафиксирован. Смена режима на существующих данных не поддерживается
Ссылки на образы Меняются только по маршруту обновления, для очереди и аналитического хранилища — по одному шагу за раз

Что проверить перед первым запуском

  1. ./preflight.sh .env завершается строкой об успешной проверке.
  2. docker compose -f docker-compose.onprem.yml --env-file .env config --quiet ничего не печатает.
  3. Права .env0600, файл не символьная ссылка, ни один ключ не повторяется.
  4. Все семь ссылок на образы — точные, с цифровым отпечатком. Ссылка только по тегу релизом не является.
  5. Каждый секрет — своё независимое значение. Ни одной заглушки из шаблона не осталось.
  6. Заполнены обе переменные подписи манифеста резервной копии: preflight требует их всегда, даже если профиль резервного копирования не запускается.
  7. Файлы списка доступа кэша, конфигурации очереди и закрытых ключей TLS лежат вне репозитория с правами 0600.
  8. BOOTSTRAP_EMAIL и BOOTSTRAP_PASSWORD заполнены только до появления первого владельца, потом очищены оба.

После запуска:

docker compose -f docker-compose.onprem.yml --env-file .env ps
curl -fsS "https://${PP_DOMAIN}/healthz"

Проверка живости отвечает {"status":"ok"} и не требует аутентификации: у кабинета она пингует базу, у приёма событий — соединение с очередью. Ответ 503 приходит с обычным конвертом ошибки и указанием недоступной зависимости.

Что метрики действительно не выведены наружу, видно изнутри контейнера:

docker compose -f docker-compose.onprem.yml --env-file .env exec query-api \
  wget -qO- http://localhost:9090/metrics | grep pp_

Дальше — TLS и секреты: где заканчивается шифрование, что публиковать наружу и как ротировать перечисленные ключи.