Запуск через Podman и управление службой

Поможем запустить Open WebUI через Podman, сохранить данные и управлять службой Quadlet. Разберём автозапуск, rootless и альтернативу с kube play.

После урока вы сможете

  • Понимать, когда выбирать Podman и где выполняются команды
  • Запускать отдельный Open WebUI через rootless Quadlet
  • Различать перезапуск, автозапуск и работу после выхода пользователя
  • Понимать роль kube play и постоянного хранилища

Проверяемый результат: Понятно, какой способ запуска выбран, где сохраняются данные Open WebUI и как проверить и остановить свой экземпляр.

Локальный интерфейс Open WebUI удобно настроить так, чтобы после перезапуска приложения вы возвращались к тем же чатам и своей учётной записи без повторного ввода длинных команд. Если на компьютере с Linux используется Podman, сам движок запускает контейнер и подключает хранилище. Для постоянной пользовательской службы управление запуском и перезапуском в этом примере поручим менеджеру служб systemd.

В этом уроке мы сразу опишем постоянную службу через Quadlet. Так называется встроенный в Podman генератор, который превращает лаконичное описание контейнера в понятную для systemd службу. Варианты с одиночной командой podman run и локальным манифестом podman kube play мы разберём в конце как самостоятельные альтернативы для других задач. Конфигурации и команды ниже составлены по документации и исходному коду проекта. При подготовке текста мы не запускали контейнеры, не выходили из системы и не перезагружали хост.

Где будем запускать Open WebUI

Примеры опираются на документацию для Debian 13 (trixie) на архитектуре amd64. В этой среде уже должны быть настроены пользовательский режим Podman (пакет podman 5.4.2 есть в репозитории дистрибутива), сетевой стек pasta, иерархия cgroup v2, пользовательский менеджер systemd и утилита curl для проверки HTTP-ответа. Пользовательский менеджер должен быть активен. Версия 5.4.2 задаёт базовую среду примера по документации; мы не утверждаем, что это минимальная подходящая версия. Браузер будем открывать на этом же компьютере с Linux.

Мы используем rootless-режим. Это означает, что Podman запускает контейнеры от имени вашей обычной учётной записи на хосте, без прав суперпользователя root и без обязательного центрального фонового демона. По умолчанию данные контейнеров, образы и тома находятся в хранилище внутри домашнего каталога пользователя. Поэтому все команды podman и systemctl --user в уроке выполняются от одной учётной записи. Добавлять перед ними sudo не нужно: вызов sudo podman переключит вас в отдельное хранилище пользователя root, где не будет видно ни ваших контейнеров, ни созданных томов. Rootless-режим определяет владельца процессов и файлов на хосте. Он не заменяет настройку сетевого доступа и не означает, что процессы внутри контейнера работают под тем же числовым идентификатором (UID).

Понятия образа, проброса портов и постоянного тома уже знакомы вам по уроку про запуск через Docker Compose, но установленный Docker не заменяет подготовку Podman. Если Podman ещё не установлен или для вашей учётной записи не подготовлено пользовательское окружение (включая диапазоны подчинённых идентификаторов subuid и subgid), перед началом сверьтесь со страницей пакета в своём дистрибутиве и официальным руководством по настройке rootless Podman.

Проверим версию Podman, версию cgroups и состояние пользовательского менеджера служб:

podman --version
podman info --format '{{.Host.CgroupsVersion}}'
systemctl --user status

Во второй строке ожидаем увидеть v2, так как работа пользовательских служб Podman с systemd опирается на cgroup v2. Команда systemctl --user status показывает состояние пользовательского менеджера. Даже если она возвращает ненулевой код завершения из-за другой проблемной службы в системе, сам менеджер при этом работает.

Перед созданием файлов убедитесь, что выбранный локальный порт свободен, а в выводе podman ps -a, podman pod ps и podman volume ls нет чужих ресурсов с именами из нашего примера. Если имя или порт уже заняты прежней установкой, остановитесь и не удаляйте старые контейнеры или тома наугад. Именованные тома из Docker или данные из прежней Python-установки в Podman автоматически не переносятся даже при полном совпадении имён.

Для основного маршрута проверим и имя пользовательской службы open-webui.service: команда systemctl --user status open-webui.service не должна указывать на уже существующую установку. Файл ~/.config/containers/systemd/open-webui.container тоже не должен принадлежать другому экземпляру. Этот пример рассчитан на новую службу; имеющийся файл или службу мы им не заменяем.

Ниже приведены три независимых способа запуска. Выберите только один:

ВариантИмя контейнера или podТомЛокальный порт
Основной Quadletopen-webuiopen-webui-data3000
Необязательный runopen-webui-onceopen-webui-once-data3001
Необязательный kube playopen-webui-kubeopen-webui-kube-data3002

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

Описываем контейнер в Quadlet

Чтобы не запускать контейнер вручную перед настройкой службы, сразу опишем его для генератора Quadlet. В пользовательском режиме Podman ищет такие файлы в каталоге ~/.config/containers/systemd/.

Создадим этот каталог, если его ещё нет:

mkdir -p ~/.config/containers/systemd

Сохраним в файл ~/.config/containers/systemd/open-webui.container следующее описание:

[Unit]
Description=Open WebUI

[Container]
Image=ghcr.io/open-webui/open-webui:v0.12.3
ContainerName=open-webui
PublishPort=127.0.0.1:3000:8080
Volume=open-webui-data:/app/backend/data
Environment=WEBUI_SECRET_KEY_FILE=/app/backend/data/.webui_secret_key

[Service]
Restart=always
TimeoutStartSec=900

[Install]
WantedBy=default.target

Разберём строки этого файла:

  • Image=ghcr.io/open-webui/open-webui:v0.12.3 выбирает конкретный выпуск Open WebUI v0.12.3 вместо плавающих тегов вроде :main или :latest. Как отмечено в руководстве по запуску контейнера Open WebUI, точный тег версии позволяет сохранить предсказуемое поведение при следующих перезапусках.
  • ContainerName=open-webui задаёт постоянное имя создаваемого контейнера в Podman.
  • PublishPort=127.0.0.1:3000:8080 связывает внутренний порт 8080, который слушает приложение в штатном образе, с портом 3000 на локальном интерфейсе 127.0.0.1 хоста. Если опустить IP-адрес и написать просто 3000:8080, порт будет опубликован на всех сетевых интерфейсах машины. Явная привязка к 127.0.0.1 публикует порт на loopback-интерфейсе хоста. В этом уроке мы обращаемся к нему браузером и curl на том же Linux-компьютере.
  • Volume=open-webui-data:/app/backend/data подключает именованный том Podman (named volume) к каталогу /app/backend/data внутри контейнера, где хранятся база данных и файлы приложения. В отличие от временного записываемого слоя самого контейнера, именованный том сохраняется при пересоздании контейнера. Podman создаст том open-webui-data автоматически при первом старте службы, поэтому отдельный файл .volume для этой задачи не нужен.
  • Environment=WEBUI_SECRET_KEY_FILE=/app/backend/data/.webui_secret_key указывает путь к файлу секретного ключа внутри только что подключённого постоянного тома. Это настройка для чистой установки, основанная на логике стартового скрипта start.sh версии v0.12.3: если переменные WEBUI_SECRET_KEY и WEBUI_JWT_SECRET_KEY не заданы отдельно, штатный скрипт при первом запуске сам создаёт файл по пути из WEBUI_SECRET_KEY_FILE и при следующих стартах читает ключ из него. Поскольку путь ведёт в том open-webui-data, ключ остаётся на месте после пересоздания контейнера. Генерировать значение ключа вручную, выводить этот файл на экран или применять такой приём к старой базе с другим ключом не нужно.
  • Restart=always и TimeoutStartSec=900 в секции [Service] передаются напрямую менеджеру systemd. Первая директива поручает systemd перезапускать службу после завершения процесса, кроме явной остановки через менеджер служб; сам образ она при этом не обновляет. Вторая увеличивает предел ожидания старта до 900 секунд, как показано в примере документации Podman по Quadlet: при первом запуске Podman должен скачать образ из реестра, и стандартного тайм-аута может не хватить. Лимит в 900 секунд даёт запуску больше времени, чем стандартный тайм-аут, но при его превышении попытка всё равно может завершиться ошибкой.
  • WantedBy=default.target в секции [Install] сообщает генератору Quadlet, что службу нужно связать со стандартной целью запуска пользовательского менеджера.

Здесь есть отличие от обычных unit-файлов, которые мы создавали вручную в уроке про запуск Python-версии как службы. Службы, сгенерированные через Quadlet, нельзя включать или выключать командами systemctl --user enable или disable. Как прямо объясняет документация генератора podman-systemd.unit, генератор сам считывает секцию [Install] во время перезагрузки конфигурации и создаёт нужные связи в памяти systemd. Появление сгенерированной службы ещё не запускает контейнер в текущем сеансе.

Попросим пользовательский менеджер перечитать конфигурацию, чтобы генератор создал службу open-webui.service, и сразу запустим её:

systemctl --user daemon-reload
systemctl --user start open-webui.service

После этих команд проверим, как прошёл запуск службы и самого веб-приложения.

Проверяем службу, интерфейс и подключение модели

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

Проверим текущее состояние службы и посмотрим последние 50 строк её журнала:

systemctl --user status open-webui.service
journalctl --user -u open-webui.service -n 50 --no-pager

При самом первом запуске служба может некоторое время находиться в состоянии активации (activating), пока скачивается образ v0.12.3 и распаковываются его слои. При успешном запуске контейнера статус сменится на active (running), а в журнале journalctl появятся сообщения о подготовке приложения. Если служба вообще не находится после daemon-reload, проверьте точность пути ~/.config/containers/systemd/open-webui.container и отсутствие синтаксических опечаток в секциях файла.

Когда процесс контейнера работает, проверим HTTP-готовность backend-сервера через маршрут /ready:

curl --fail --silent --show-error http://127.0.0.1:3000/ready

Согласно коду маршрутов main.py в выпуске v0.12.3, маршрут /ready проверяет три условия: завершилась ли стартовая инициализация приложения, проходит ли проверочный запрос к базе данных и отвечает ли Redis (только если Redis специально настроен в окружении). Пока приложение ещё загружается или база не готова, сервер возвращает код 503, а когда всё готово к работе, отвечает JSON-объектом со статусом true. Более простой маршрут /health, который используется и во встроенной проверке HEALTHCHECK самого образа, возвращает положительный ответ без проверки завершения старта и базы данных. Если curl сообщает об ошибке соединения или коде 503, подождите немного и повторно посмотрите вывод journalctl.

Получив успешный ответ от /ready, откройте в браузере на этом же компьютере адрес http://127.0.0.1:3000. Так как мы используем новый пустой том open-webui-data, откроется страница создания первой учётной записи администратора. Порядок регистрации и базовых настроек подробно разобран в уроке про первый вход в Open WebUI. Аккаунты из ваших прежних установок в Docker или Python здесь не появятся.

Доступность веб-интерфейса и успешный ответ /ready не проверяют связь с сервером языковых моделей. Если вы подключаете к Open WebUI локальный сервер моделей (например, Ollama, запущенную на том же Linux-хосте), учитывайте устройство контейнерной сети. Адрес localhost или 127.0.0.1 внутри контейнера указывает на сам контейнер open-webui, а не на компьютер с Linux, где он запущен.

Для обращения из rootless-контейнера к сервису на хосте в руководстве Open WebUI по Podman используется специальное имя host.containers.internal (например, адрес подключения может выглядеть как OLLAMA_BASE_URL=http://host.containers.internal:11434). В Podman 5.4.2 со стеком pasta внутренние имена хоста добавляются автоматически, если сетевой шлюз определён. Но само разрешение имени host.containers.internal ещё не гарантирует, что сервер моделей запущен, принимает запросы или уже загрузил нужную модель. Если чат не видит модель, проверяйте настройки подключения в интерфейсе и работу самого модельного сервера на хосте; переводить Ollama на прослушивание всех сетевых интерфейсов (0.0.0.0) просто ради локальной проверки не следует.

Управляем перезапуском и автозапуском

Когда интерфейс работает, можно перейти к управлению службой. Сначала разберём остановку и перезапуск контейнера, затем условия, при которых работает пользовательский менеджер systemd.

Останавливаем и запускаем свой экземпляр

Для управления работающей службой используются команды systemctl с флагом --user. Выполнять их подряд не нужно; выбирайте команду под текущую задачу.

Чтобы штатно остановить службу и контейнер:

systemctl --user stop open-webui.service

При явной остановке через stop политика Restart=always не запускает контейнер обратно: менеджер systemd отличает действие пользователя от сбоя.

Чтобы снова запустить остановленную службу:

systemctl --user start open-webui.service

Если нужно перезапустить работающий экземпляр:

systemctl --user restart open-webui.service

Если вы изменили параметры в файле ~/.config/containers/systemd/open-webui.container, сначала выполните systemctl --user daemon-reload, чтобы генератор обновил определение службы, и только затем перезапустите её через systemctl --user restart open-webui.service. После любого перезапуска дождитесь готовности приложения по /ready, прежде чем обновлять страницу в браузере.

Остановка и перезапуск службы не удаляют именованный том open-webui-data. Поскольку база данных и файл .webui_secret_key лежат внутри этого тома, после следующего старта вы сможете войти в ту же учётную запись. Сохранение тома на диске не заменяет отдельного резервного копирования перед обновлениями и не позволяет безопасно откатить базу на более старую версию образа.

Если служба нужна без входа пользователя

Закрытие одного окна терминала при продолжающемся графическом или SSH-сеансе не останавливает службу open-webui.service, так как пользовательский менеджер systemd продолжает работать. Без linger время жизни пользовательского менеджера связано с сеансами и настройками logind: после полного выхода из всех сеансов (logout) менеджер может быть остановлен. Директива WantedBy=default.target в нашем файле указывает, что службу нужно запустить вместе с пользовательским менеджером, но она не заставляет систему включать сам этот менеджер при загрузке компьютера до вашего входа.

Если вам нужно, чтобы пользовательский менеджер запускался сразу при включении компьютера и продолжал держать службы после полного выхода из учётной записи, в systemd используется режим linger (буквально «задерживаться»), управляемый через loginctl.

Проверить текущее состояние этой настройки для своего пользователя можно командой:

loginctl show-user "$USER" -p Linger

Если в выводе указано Linger=no, а вам действительно нужна работа службы до входа в систему и после выхода из всех сессий, этот режим включается командой:

loginctl enable-linger "$USER"

В зависимости от политики безопасности конкретной системы вызов enable-linger может потребовать прав администратора (проверки через Polkit). Режим linger действует на всю вашу учётную запись, а не на один контейнер Open WebUI: он меняет время жизни пользовательского менеджера целиком. Вместе с ним смогут запускаться службы, включённые в его цели, а ошибки их настройки linger не исправляет. Если вы работаете с Open WebUI только во время своего обычного сеанса за компьютером, включать linger не обязательно.

Когда нужен другой способ запуска Podman

Если постоянная служба systemd вам не нужна или вы хотите описывать локальную нагрузку в формате манифестов Kubernetes, Podman предлагает два других инструмента. Оба варианта ниже описывают самостоятельные чистые установки со своими портами и томами. Они не продолжают настройку Quadlet.

Разовый запуск без службы

Чтобы запустить отдельный экземпляр Open WebUI без регистрации службы в systemd, используем команду podman run. Для этого независимого маршрута мы берём имя контейнера open-webui-once, том open-webui-once-data и локальный порт 3001:

podman run -d \
  --name open-webui-once \
  --publish 127.0.0.1:3001:8080 \
  --volume open-webui-once-data:/app/backend/data \
  --env WEBUI_SECRET_KEY_FILE=/app/backend/data/.webui_secret_key \
  ghcr.io/open-webui/open-webui:v0.12.3

Флаг -d переводит контейнер в фоновый режим и освобождает терминал, но не создаёт службу systemd. Такой контейнер не встроен в систему управления службами пользователя и не запустится сам после перезагрузки компьютера.

После завершения стартовой инициализации этот экземпляр открывается в локальном браузере по адресу http://127.0.0.1:3001. Готовность backend можно проверить запросом к http://127.0.0.1:3001/ready. Внутри тома open-webui-once-data создаются собственные база данных, секретный ключ и учётные записи, никак не связанные с томом из раздела про Quadlet.

Этим контейнером управляют прямые команды Podman. Чтобы остановить контейнер, сохранив его самого и том с данными, выполните:

podman stop open-webui-once

Чтобы снова запустить этот же остановленный контейнер без пересоздания:

podman start open-webui-once

Kubernetes YAML через kube play без кластера

Команда podman kube play позволяет прочитать поддерживаемое подмножество манифеста Kubernetes YAML и развернуть описанные в нём объекты прямо в локальном Podman. Для этого не требуются ни кластер Kubernetes, ни kubectl, ни Helm. В то же время это именно локальная интерпретация файла силами Podman, а не полноценный контроллер кластера.

В Kubernetes минимальную единицу развёртывания называют Pod. Это группа из одного или нескольких контейнеров с общим сетевым пространством и возможностью подключать общие тома. Запрос на подключение постоянного тома описывается через PersistentVolumeClaim (PVC). Когда Podman читает секцию persistentVolumeClaim в локальном YAML, он сопоставляет указанное имя claimName со своим обычным именованным томом (named volume). Писать для этого отдельный манифест PVC с размером хранилища вроде 5Gi не нужно: Podman создаст локальный том по одному лишь имени claimName, а декларативные квоты размера из Kubernetes на обычный локальный том всё равно автоматически не накладываются.

Если вы выбрали этот независимый маршрут, создадим отдельный рабочий каталог и перейдём в него:

mkdir -p ~/open-webui-podman && cd ~/open-webui-podman

Сохраним в этом каталоге файл open-webui-kube.yaml (для этой ветки используются имя open-webui-kube, том open-webui-kube-data и локальный порт 3002):

apiVersion: v1
kind: Pod
metadata:
  name: open-webui-kube
spec:
  containers:
    - name: open-webui-kube
      image: ghcr.io/open-webui/open-webui:v0.12.3
      env:
        - name: WEBUI_SECRET_KEY_FILE
          value: /app/backend/data/.webui_secret_key
      ports:
        - containerPort: 8080
      volumeMounts:
        - name: data
          mountPath: /app/backend/data
  volumes:
    - name: data
      persistentVolumeClaim:
        claimName: open-webui-kube-data

Внутри манифеста указан только внутренний порт контейнера containerPort: 8080, а привязку к локальному адресу и порту хоста мы передадим через флаг --publish в командной строке. Запустим pod из каталога ~/open-webui-podman:

podman kube play --publish 127.0.0.1:3002:8080 ./open-webui-kube.yaml

Podman создаст том open-webui-kube-data, поднимет pod open-webui-kube и опубликует интерфейс по адресу http://127.0.0.1:3002. Как и podman run, самостоятельный вызов podman kube play запускает контейнеры локально, но не создаёт службу systemd для автозапуска. В Quadlet существуют отдельные .kube-единицы. Мы не смешиваем их с этим примером, чтобы не создавать вторую службу в системе.

Если вы изменили файл open-webui-kube.yaml и хотите применить новые параметры, простой повтор kube play не работает как фоновая сверка состояния в кластере. Для пересоздания pod по обновлённому файлу используйте флаг --replace, обязательно повторив флаг публикации порта (так как внешний порт 3002 не записан внутри YAML):

podman kube play --replace --publish 127.0.0.1:3002:8080 ./open-webui-kube.yaml

Флаг --replace сначала полностью удаляет старый pod и его контейнеры и только затем создаёт новые, поэтому работа интерфейса на время пересоздания прерывается.

Когда нужно остановить и удалить pod и контейнеры, созданные по этому манифесту, выполните из того же каталога команду podman kube down:

podman kube down ./open-webui-kube.yaml

Вызов podman kube down без флага --force удаляет сам pod и его контейнеры, но сохраняет именованный том open-webui-kube-data со всеми данными и ключом. Если выполнить down повторно, когда pod уже удалён, команда может сообщить об отсутствии объекта. В отличие от контроллеров кластера, она не гарантирует одинаковый результат при повторных вызовах.

Для своей установки придерживайтесь одного выбранного маршрута. В Quadlet процессом контейнера управляет пользовательский systemd, а в run и kube play вы управляете им прямыми командами. Именованный том хранит базу и файл ключа, поэтому остановка процесса не означает потерю данных. Проверяйте состояние процесса, HTTP-ответ /ready и подключение модели отдельно: они отвечают на разные вопросы.

Материалы урока

Отметить прохождение

Отметьте урок после того, как получили проверяемый результат из практической части.

/body>