Если вам понравилась эта статья, вы можете стать спонсором на boosty.
Введение#
В первой части , когда мы подняли общую базу данных PostgreSQL и pgAdmin в Docker мы договорились, что в дальнейшем переведём на неё все сервисы, которым для работы нужна база данных Postgres, вместо того чтобы у каждого приложения был свой собственный контейнер с БД. В той же статье я оставил дисклеймер, что подключение сторонних приложений к единой базе - тема отдельной статьи. Не прошло и трех лет как я выполнил свое обещание. Вот эта статья.
В ней я разберу три вещи, которые логически идут одна за другой:
- Как правильно создать пользователя и базу данных под конкретное приложение - но не абы как, а по принципу наименьших привилегий.
- Как на практике подключить к нашей общей PostgreSQL реальный сервис - примером послужит Authentik, благо про него в блоге уже есть большая серия статей.
- Что вообще значит «обслуживание базы данных» и когда это действительно нужно делать руками, а когда PostgreSQL справляется сам.
Почему не стоит запускать все приложения от одного суперпользователя#
Самый простой (и самый плохой, даже ужасный) вариант - выдать всем приложениям логин и пароль от суперпользователя postgres, которого мы создали в первой части, и не заморачиваться. Так действительно можно сделать и работать это будет. Как бы. Но у этого подхода есть накопительный негативный эффект, который проявится не сразу, но обязательно проявится не сразу:
- любое приложение с доступом уровня суперпользователя технически имеет доступ в “чужие” базы данных на этом же сервере;
- если у одного из приложений будет скомпрометировано (SQL-инъекцию, RCE, да что угодно), скомпрометированным окажется не только его собственная база, а весь инстанс PostgreSQL сразу;
- бэкапить, чистить логи доступа и разбираться, кто и что сделал с базой, значительно проще, когда у каждого приложения свой пользователь и своя база данных.
Поэтому единственная правильная схема - у каждого приложения своя база данных и свой пользователь, который имеет права только и только на неё.
Дальше показываю, как это сделать в pgAdmin, на примере Authentik - у него, кстати, именно такая схема доступа рекомендована официально (кто бы сомневался, да?).
Создаём пользователя и базу данных для приложения#
Заходим в pgAdmin (как подключиться к серверу и завести в нём подключение описано в первой части) и повторяем шаги ниже под каждое новое приложение. Пример - для Authentik, у вас может быть любое другое имя пользователя и базы.
Шаг 1. Создаём роль (пользователя)#
В дереве слева: Servers → ваш Postgres-сервер → Login/Group Roles, ПКМ → Create → Login/Group Role…
- Вкладка General → Role name:
authentik - Вкладка Definition → Password: придумываем пароль, он понадобится в
docker-compose.ymlприложения - Вкладка Privileges → включаем только
Can login, всё остальное (superuser, create role, create db и т.д.) оставляем выключенным
Сохраняем. На этом этапе у пользователя authentik нет прав вообще ни на что, кроме права подключиться к серверу - это не просто так, права мы выдадим точечно на следующих шагах.
Шаг 2. Создаём базу данных#
Databases → ПКМ → Create → Database…
- Database:
authentik - Owner:
authentik(тот самый пользователь из шага 1)
Сохраняем - база сразу принадлежит нужному пользователю, дополнительно никаких прав выдавать не нужно.
Шаг 3. Даём права на схему public#
Это обязательный шаг для большинства приложений (включая Authentik) - они создают свои таблицы сами при первом запуске, а для этого им нужно право создавать объекты в схеме. ПКМ по базе authentik → Query Tool, выполняем:
GRANT USAGE, CREATE ON SCHEMA public TO authentik;Шаг 4. Права на существующие таблицы (если база не пустая)#
Если вы создаёте базу с нуля - можно пропустить, приложение создаст таблицы уже с правильным владельцем. Но если пересоздаёте базу под уже существующее приложение (например, переезжаете с его собственного контейнера БД на общий, как в этой статье), то на уже перенесённых таблицах права нужно выдать отдельно:
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO authentik;Шаг 5. Проверяем, что всё работает#
В том же Query Tool базы authentik:
CREATE TABLE authentik_test (
id SERIAL PRIMARY KEY,
name TEXT
);
INSERT INTO authentik_test (name) VALUES ('ok');
SELECT * FROM authentik_test;
DROP TABLE authentik_test;Если все четыре команды выполнились без ошибок доступа или еще чего-нибудь , то пользователь и база данных настроены правильно, можно переходить к подключению самого приложения.
Кстати, вот что у нас получилось, если нарисовать таблицу.
| Компонент | Значение |
|---|---|
| Пользователь | authentik |
| База данных | authentik |
| Права | CONNECT + CREATE + полный доступ на свою схему, и ничего за её пределами |
Подключаем Authentik к общей PostgreSQL#
Если вы разворачиваете Authentik с нуля - следуйте статье про первую настройку, но с одним отличием: не создавайте в docker-compose.yml собственный контейнер postgresql для Authentik - у нас уже есть общий, из первой части этой серии.
Убираем сервис postgresql из compose-файла Authentik и добавляем сервис Authentik в ту же docker-сеть database, где живёт наш общий Postgres:
services:
server:
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2026.5.1} # проверьте актуальный тег на странице релизов перед установкой
command: server
environment:
AUTHENTIK_SECRET_KEY: ваш_секретный_ключ
AUTHENTIK_POSTGRESQL__HOST: postgres # имя контейнера с общей БД из первой части
AUTHENTIK_POSTGRESQL__NAME: authentik
AUTHENTIK_POSTGRESQL__USER: authentik
AUTHENTIK_POSTGRESQL__PASSWORD: пароль_из_шага_1
AUTHENTIK_POSTGRESQL__PORT: 5432
networks:
- proxy
- database # та же сеть, где сидит общий postgres
# остальные переменные и redis - без изменений, смотрите статью про первую настройку
networks:
proxy:
external: true
database:
external: trueПравильно все чувствительные данные вынести в отдельный .env файл
Если раньше Authentik стучался в свою базу по внутреннему docker-имени authentik-postgresql, то теперь он идёт в тот же контейнер postgres, что и все остальные подключённые сервисы - просто под своим собственным логином, паролем и именем базы, которые мы завели в предыдущем разделе. Со стороны PostgreSQL это два полностью изолированных друг от друга «мира», физически работающих на одном инстансе.
Если вы переносите уже работающий Authentik со своей базы на общую, а не разворачиваете с нуля - обязательно снимите дамп через pg_dump со старого контейнера и восстановите его в новую базу через pg_restore или psql, прежде чем менять переменные окружения и удалять старый контейнер. Порядок действий для этого сценария - тема отдельного разговора, здесь не буду его упрощать до пары строк, чтобы не создавать ощущение, что это просто и тривиально.
Тот же принцип - отдельная роль, отдельная база, GRANT только на свою схему - работает для любого другого приложения: Nextcloud, Linkwarden, Vaultwarden и всех остальных сервисов и контейнеров, которые умеют работать с внешней PostgreSQL.
Базовое обслуживание PostgreSQL#
Здесь важно сразу развенчать один миф: PostgreSQL не нужно вручную обслуживать в режиме «раз в неделю захожу и жму VACUUM». В PostgreSQL по умолчанию включён процесс autovacuum - фоновый демон, который сам следит за таблицами и запускает VACUUM/ANALYZE, когда накопилось достаточно «мёртвых» строк (удалённых или изменённых, но ещё физически не освобождённых).
Для подавляющего большинства self-hosted сценариев домашнего масштаба autovacuum справляется полностью сам, и ничего руками делать не нужно. Я бы даже сказал, что никогда ничего не делайте с базой данных, если не понимаете всех рисков.
Ручное обслуживание имеет смысл только в конкретных ситуациях:
VACUUM и VACUUM ANALYZE#
VACUUM;
VACUUM ANALYZE;VACUUM помечает место, занятое удалёнными/устаревшими строками, как свободное для повторного использования - но не возвращает его операционной системе, диск физически не уменьшается.
VACUUM ANALYZE дополнительно обновляет статистику по таблицам, которую использует планировщик запросов - полезно запустить руками после массовой загрузки или удаления большого объёма данных одной операцией, когда не хочется ждать, пока до этого дойдёт autovacuum.
VACUUM FULL#
VACUUM FULL;Это единственная команда, которая реально уменьшает файл базы на диске - она физически пересобирает таблицу. Но у этой операции есть своя цена: VACUUM FULL берёт эксклюзивную блокировку на таблицу на всё время операции - никто не сможет ни читать, ни писать в неё, пока команда не закончится.
На большой таблице сервиса в проде это может означать заметный простой. Использовать стоит точечно - например, после разового удаления огромного количества строк (почистили старые логи, снесли половину базы) - а не как рутинную операцию по расписанию. Но в принципе в homelab необходимость этой операции - что-то из области фантастики.
REINDEX#
REINDEX DATABASE authentik;Пересобирает индексы базы данных с нуля. Индексы в PostgreSQL тоже приводят со временем к «раздутию» (bloat), особенно на таблицах с частыми обновлениями - REINDEX полезен, если вы заметили, что запросы стали заметно медленнее, чем раньше, хотя объём данных не сильно вырос.
Для доступа к консоли конкретной базы выполните docker exec -it postgres psql -U authentik -d authentik (замените имена на свои) - или выполняйте те же команды через Query Tool в pgAdmin, как в разделе про создание базы выше. Не забывайте ; в конце каждой команды.
Если у вас пользователь Authentik и периодически отваливается worker#
Отдельный практический случай, который стоит держать в голове именно владельцам Authentik. Если время от времени вылетает ошибка про недоступный/отвалившийся worker - на моей практике чаще всего помогает прогнать полный набор команд обслуживания сразу, а не по одной:
- Подключаемся к консоли контейнера с базой (у меня это внешний контейнер с общей БД, как в этой статье, только не в Docker - у вас может быть иначе, если Authentik ещё не перевезён на общий Postgres):
docker exec -it postgres psql -U authentik -d authentik- Выполняем весь набор команд обслуживания подряд:
VACUUM;
VACUUM ANALYZE;
VACUUM FULL;
REINDEX DATABASE authentik;У меня это сняло проблему и больше она не появлялась. Но, как я уже писал выше, VACUUM FULL блокирует таблицу целиком на время выполнения - на боевом сервере с реальной нагрузкой стоит выполнять в определенный период времени.
Официального issue на GitHub, который бы прямо связывал именно эту ошибку worker’а с необходимостью VACUUM, нет - в трекере Authentik есть несколько соседних тем про проблемы worker’а с PostgreSQL после отказа от Redis в версии 2025.10.0 (#19302, #20644), но в них решение было не через VACUUM, а через код в новых версиях и тюнинг подключений. Единственный issue, где VACUUM ANALYZE явно и с цифрами помог (#24179), - про деградацию скорости из-за JIT-компилятора Postgres на почве устаревшей статистики, а не про сам worker. Так что описанный выше способ - это моя личная рабочая практика, а не задокументированное решение от разработчиков. Делаете на свой страх и риск.
Подробнее про обслуживание PostgreSQL - в официальной документации.
Итог#
Теперь у нас есть не просто «база данных в докере», а рабочая схема: общий инстанс PostgreSQL, у каждого приложения - изолированный пользователь и своя база с правами только на неё, и понимание, когда обслуживание нужно делать руками, а когда PostgreSQL прекрасно справляется сам через autovacuum. Дальше можно постепенно переводить остальные self-hosted сервисы с их собственных контейнеров БД на эту общую схему - принцип для всех одинаковый, отличаются только имена пользователя и базы.





