SSD Nodes Learn 🎉 VPS от $5.50/мес
Руководства Matt ConnorАвтор: Matt Connor

Запуск Ollama в rootless Podman на VPS

Настройте Ollama в rootless Podman с использованием Quadlet для автозапуска. Руководство охватывает настройку пользователя, режим lingering, метки SELinux и защиту API через SSH.

Запуск Ollama в rootless Podman на VPS

Для запуска Ollama в rootless Podman на сервере должны быть соблюдены пять условий, которые обычно опускаются в руководствах для настольных систем. Контейнер принадлежит выделенному непривилегированному пользователю. Для этого пользователя включен режим lingering, чтобы контейнер продолжал работу после вашего выхода из системы. Файл Quadlet передает управление контейнером systemd, что обеспечивает его автоматический запуск после перезагрузки. Директория с моделями имеет метку SELinux в дистрибутивах, где он включен. API прослушивает только loopback, а доступ к нему осуществляется через SSH-туннель.

Ollama — это сервер для больших языковых моделей (LLM). Он хранит веса моделей на диске, загружает их в память и отвечает на HTTP-запросы через порт 11434. В нем нет системы входа, API-ключей или учетных записей пользователей, поэтому сеть является единственным средством контроля доступа. Podman запускает контейнеры без демона и без прав root, поэтому любой процесс, вышедший за пределы контейнера, будет выполняться от имени обычного непривилегированного пользователя. Если вы хотите сначала сравнить среды выполнения, прочитайте чем отличаются Podman и Docker на VPS. Если вы предпочитаете не использовать контейнеры, установка Ollama напрямую на VPS будет более коротким путем.

SSD Nodes предоставляет Fedora в качестве одного из образов, а Fedora по умолчанию поставляется с Podman и SELinux (security-enhanced Linux). Каждая приведенная ниже команда работает в любом дистрибутиве с Podman версии 5 или новее.

Почему версия для ноутбука требует изменений при переносе на сервер

5 августа 2026 года в Fedora Magazine было опубликовано понятное руководство по этому стеку: Запуск Ollama локально с помощью Podman в Fedora Linux, автор Yazan Monshed. Это отличный материал для первого знакомства с инструментами. Однако он ориентирован на ноутбук, и четыре предложенных в нем решения ведут себя иначе на машине с публичным IP-адресом.

  • Контейнер запускается с помощью простой команды podman run -d. Контейнер, запущенный вручную, не перезапустится после перезагрузки системы, так как не было настроено автоматическое управление его состоянием.
  • Используется динамический тег ollama/ollama. На ноутбуке вы сразу заметите день, когда поведение изменится. На сервере первым признаком станет скрипт, который перестал работать за ночь.
  • Публикация портов выполняется через -p 11434:11434, что привязывает сервис ко всем сетевым интерфейсам. За домашним роутером это безопасно, так как сервис недоступен из Интернета. На VPS это превращается в публичный API для инференса без какой-либо защиты паролем.
  • Сервис запускается от имени вашего текущего пользователя. На сервере учетная запись, владеющая контейнером, не должна иметь доступа к другим ресурсам, чтобы в случае взлома злоумышленник оказался в пустой домашней директории.

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

Создание непривилегированного пользователя и проверка subuid

Rootless Podman отображает внутренние идентификаторы пользователей (UID) контейнера на блок неиспользуемых идентификаторов хоста. Этот блок описывается в /etc/subuid и /etc/subgid. Без него rootless-контейнеры не смогут запуститься.

sudo dnf install -y podman        # or: sudo apt install -y podman
sudo useradd --create-home --shell /bin/bash --comment "Ollama container owner" ollama
sudo passwd --lock ollama
grep ollama /etc/subuid /etc/subgid

Команда grep должна вывести две строки, по одной из каждого файла, где указан диапазон из 65536 идентификаторов:

/etc/subuid:ollama:100000:65536
/etc/subgid:ollama:100000:65536

Ваши начальные числа будут отличаться, это нормально. Если grep ничего не выводит, значит useradd не выделил диапазон, и первая команда podman от имени этого пользователя завершится ошибкой:

Error: cannot find UID/GID for user ollama: no subuid ranges found for user "ollama" in /etc/subuid

Назначьте диапазон, который не занят другими пользователями, а затем сообщите Podman, что старые настройки отображения устарели:

sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 ollama
sudo -iu ollama podman system migrate

Блокировка пароля означает, что никто не сможет войти в систему напрямую под учетной записью ollama. Вы можете переключиться на эту учетную запись из своей административной учетной записи с помощью sudo -iu ollama.

Включение режима lingering для работы сервиса после выхода из системы

Экземпляр systemd пользователя обычно запускается при входе в систему и завершается при выходе, при этом /run/user/<uid> удаляется. Все rootless-контейнеры, принадлежащие этому пользователю, завершают работу в тот же момент. Режим lingering позволяет экземпляру пользователя продолжать работу без активной сессии.

sudo loginctl enable-linger ollama
loginctl show-user ollama --property=Linger

Эта команда должна вывести Linger=yes. Включите этот режим до создания юнита, так как каталог, необходимый юниту, /run/user/<uid>, создается только после активации lingering.

Существует еще один неочевидный шаг. sudo -iu ollama предоставляет оболочку (shell), но не сессионную шину, поэтому systemctl --user завершается с ошибкой:

Failed to connect to bus: $DBUS_SESSION_BUS_ADDRESS and $XDG_RUNTIME_DIR not defined

systemd ищет пользовательскую шину по адресу $XDG_RUNTIME_DIR/bus, а sudo -i не устанавливает эту переменную. Установите её вручную в каждой административной оболочке, где вы управляете этим сервисом:

sudo -iu ollama
export XDG_RUNTIME_DIR=/run/user/$(id -u)
systemctl --user status

Где хранятся блобы моделей и сколько места на диске планировать

Ollama записывает веса в /root/.ollama/models внутри контейнера. Примонтируйте каталог из домашней директории пользователя по этому пути, и файлы окажутся там, где их можно измерить: /home/ollama/ollama-data/models. Блобы попадают в models/blobs как файлы с адресацией по содержимому, а в models/manifests хранится небольшой индекс с их именами. Если вы используете именованный том, как в статье Fedora Magazine, то же дерево каталогов будет находиться в /home/ollama/.local/share/containers/storage/volumes/<volume>/_data.

Рассчитайте объем диска до того, как начнете загрузку. Опубликованные размеры загружаемых файлов — это нижний предел.

ChartPublished download size per Ollama model tag, ollama.com/library, checked 13 August 2026
The data behind this chart
[
  {
    "label": "gemma3:4b",
    "download_gb": 3.3
  },
  {
    "label": "mistral:7b",
    "download_gb": 4.4
  },
  {
    "label": "qwen3:8b",
    "download_gb": 5.2
  },
  {
    "label": "gemma3:12b",
    "download_gb": 8.1
  },
  {
    "label": "qwen3:14b",
    "download_gb": 9.3
  },
  {
    "label": "gemma3:27b",
    "download_gb": 17
  },
  {
    "label": "qwen3:30b",
    "download_gb": 19
  }
]

Все 7 строк — это данные, опубликованные на ollama.com/library, а не размеры, измеренные на диске. Самый маленький тег здесь, gemma3:4b, требует загрузки 3.3 ГБ. Самый большой, qwen3:30b, требует 19 ГБ. Образ контейнера занимает дополнительное место в собственном хранилище Podman, поэтому проверяйте оба показателя вместе с помощью podman system df и df -h /home. Модели также требуется объем оперативной памяти, примерно равный размеру файла, плюс место для контекстного окна, поэтому модель размером 19 ГБ не запустится на VPS с 16 ГБ ОЗУ.

Закрепляйте тег образа и используйте полное имя реестра

sudo -iu ollama
export XDG_RUNTIME_DIR=/run/user/$(id -u)
mkdir -p ~/ollama-data ~/.config/containers/systemd
podman pull docker.io/ollama/ollama:0.32.9

Используйте тег выпущенной версии, 0.32.9 по состоянию на август 2026 года, а не latest. Закрепленный тег означает, что при перезапуске в 04:00 вы получите тот же самый бинарный файл, который тестировали, поэтому любое изменение в поведении будет результатом ваших действий. Docker Hub также публикует теги -rc и -rocm для одних и тех же версий; выбирайте обычный вариант, если у вас нет GPU AMD.

Указывайте также хост реестра. В Fedora короткое имя в юните systemd не имеет терминала для ввода данных, поэтому юнит завершается с ошибкой:

Error: short-name "ollama/ollama" did not resolve to an alias and no unqualified-search registries are defined

Предварительная загрузка образа вручную не является обязательной, но полезна, так как позволяет вынести многогигабайтную загрузку за пределы тайм-аута запуска юнита.

Quadlet-юнит, сохраняющий работоспособность после перезагрузки

Quadlet — это генератор systemd для Podman. Вы создаете файл .container, systemd превращает его в сервис при загрузке, и podman generate systemd больше не требуется. Сохраните этот файл как /home/ollama/.config/containers/systemd/ollama.container, владельцем которого является пользователь ollama.

[Unit]
Description=Ollama API (rootless)
After=network-online.target
Wants=network-online.target

[Container]
Image=docker.io/ollama/ollama:0.32.9
ContainerName=ollama
PublishPort=127.0.0.1:11434:11434
Volume=/home/ollama/ollama-data:/root/.ollama:Z
Environment=OLLAMA_KEEP_ALIVE=30m
Environment=OLLAMA_MAX_LOADED_MODELS=1

[Service]
Restart=always
TimeoutStartSec=900

[Install]
WantedBy=default.target

Имя файла определяет имя сервиса, поэтому ollama.container превращается в ollama.service.

systemctl --user daemon-reload
systemctl --user start ollama.service
systemctl --user status ollama.service

status должен показать active (running). Не запускайте systemctl --user enable ollama.service. Юнит не существует как файл на диске, поэтому systemd выдаст отказ:

Failed to enable unit: Unit file /run/user/1001/systemd/generator/ollama.service is transient or generated.

Секция [Install] уже выполняет эту задачу. Quadlet сам создает ссылку для автозапуска во время daemon-reload, поэтому данная команда обязательна. TimeoutStartSec=900 учитывает первый запуск, при котором требуется скачивание образа, так как стандартных 90 секунд недостаточно для загрузки двух гигабайт, и systemd принудительно завершит процесс как неудачный. OLLAMA_KEEP_ALIVE=30m удерживает модель в оперативной памяти между запросами вместо выгрузки через пять минут; о компромиссах можно прочитать в удержании модели Ollama в памяти. Если какая-то часть терминологии systemd здесь в новинку, в принципах работы сервисов и таймеров systemd на VPS описаны сами юниты.

Почему каталог моделей возвращает ошибку permission denied под SELinux

В Fedora, RHEL, Rocky и AlmaLinux SELinux включен в режиме принудительного контроля (enforcing) по умолчанию. Процесс контейнера выполняется в домене container_t, а каталог в домашней директории пользователя имеет метку user_home_t. Политика безопасности запрещает взаимодействие между ними, поэтому Ollama не может создать дерево моделей, и контейнер завершает работу. getenforce выводит Enforcing в этих системах, а факт отказа записывается в журнал:

sudo ausearch -m avc -ts recent

Вы увидите строку с указанием домена и целевой метки:

avc:  denied  { write } for  pid=1842 comm="ollama" name="models" dev="vda1" ino=131077 scontext=system_u:system_r:container_t:s0:c214,c827 tcontext=unconfined_u:object_r:user_home_t:s0 tclass=dir permlisted=0

Флаг :Z в конце строки Volume= является решением. Он меняет метку хостового каталога на container_file_t и присваивает ему частную категорию MCS (multi-category security), которой обладает только этот контейнер. Строчный флаг :z использует общую метку, что необходимо, если два контейнера должны читать один и тот же каталог.

Предупреждение относительно :Z: этот флаг действует деструктивно и без вывода подтверждений. Перемаркировка выполняется рекурсивно. Если указать /home/ollama, то каждый файл в этой домашней директории будет перемаркирован, что нарушит доступ по SSH-ключам для этого пользователя. Всегда выделяйте для :Z отдельный подкаталог, в котором нет других данных. Именованные тома не нуждаются в этом, так как Podman корректно маркирует их при создании. Если вам нужны подробности, основы SELinux для сервера объясняют контексты и булевы значения. В Ubuntu и Debian вместо этого используется AppArmor, поэтому :Z там не выполняет никаких действий, и его наличие в юните безопасно.

Закрытие порта 11434 и доступ к API через SSH

PublishPort=127.0.0.1:11434:11434 привязывает порт на стороне хоста к интерфейсу loopback. Проверьте это:

ss -ltnp | grep 11434
curl http://127.0.0.1:11434

Вывод ss должен содержать 127.0.0.1:11434. Значения 0.0.0.0:11434 или *:11434 означают, что порт открыт для доступа из Интернета, и curl должен отвечать Ollama is running.

Будьте внимательны при выборе стороны привязки. Адрес в PublishPort — это адрес хоста. Внутри контейнера Ollama должна продолжать прослушивать все интерфейсы, что является настройкой образа по умолчанию. Установка Environment=OLLAMA_HOST=127.0.0.1 привязывает Ollama к loopback-интерфейсу самого контейнера, а Podman перенаправляет опубликованный трафик на сетевой адрес контейнера, поэтому каждый запрос будет отклоняться даже с хоста.

Открытый порт 11434 несет два риска. В Ollama отсутствует аутентификация, поэтому любой, кто получит доступ к порту, сможет просмотреть список ваших моделей через /api/tags, выполнять вычисления на вашем CPU и расходовать ваш лимит трафика через /api/generate, загружать новые модели на ваш диск и удалять существующие. Во-вторых, обычный HTTP к удаленному порту передает запросы и ответы в открытом виде, поэтому любая машина на пути следования трафика может их прочитать. Обе проблемы исчезают, если порт не выходит за пределы сервера.

С вашей рабочей станции пробросьте порт через SSH:

ssh -N -L 11434:127.0.0.1:11434 you@vps.example.com

Теперь http://127.0.0.1:11434 на вашем ноутбуке — это Ollama на сервере, работающая внутри зашифрованного SSH-туннеля. Если на вашем ноутбуке уже запущена Ollama, локальная привязка завершится ошибкой bind [127.0.0.1]:11434: Address already in use; используйте -L 11435:127.0.0.1:11434 и укажите вашему клиенту порт 11435.

Если доступ нужен браузерному клиенту, установите перед сервисом reverse proxy с парольной защитой. Блок конфигурации Caddy состоит из четырех строк, а caddy hash-password выведет bcrypt-хеш, который ему необходим:

ollama.example.com {
  basic_auth {
    you $2a$14$replace_with_the_generated_hash
  }
  reverse_proxy 127.0.0.1:11434
}

Caddy автоматически получает сертификат по TLS (transport layer security), поэтому трафик будет зашифрован. Сначала протестируйте ваш клиент: многие инструменты для работы с Ollama не имеют поля для заголовка Authorization, и они будут выдавать ошибку 401 Unauthorized при попытке авторизации через basic auth. SSH-туннель лишен этой проблемы, поэтому он является рекомендуемым решением по умолчанию.

Загрузка модели и проверка полного пути

podman exec -it ollama ollama pull gemma3:4b
curl -s http://127.0.0.1:11434/api/tags
curl -s http://127.0.0.1:11434/api/generate -d '{"model":"gemma3:4b","prompt":"Reply with the single word: ready","stream":false}'
du -sh ~/ollama-data/models

/api/tags возвращает JSON-список gemma3:4b. /api/generate возвращает JSON-объект с полем response после паузы, необходимой для загрузки весов с диска. du должен показать число, близкое к опубликованному размеру загрузки. Затем подтвердите часть, которой посвящено всё это руководство:

sudo reboot
# reconnect, then:
sudo -iu ollama
export XDG_RUNTIME_DIR=/run/user/$(id -u)
systemctl --user is-active ollama.service

active означает, что процесс остался в памяти, а секция [Install] и daemon-reload выполнили свои задачи. inactive означает, что один из этих трёх компонентов отсутствует.

Типичные сбои и сообщения об ошибках

Контейнер исчезает после перезагрузки. Сначала проверьте loginctl show-user ollama --property=Linger, так как без Linger=yes пользовательский экземпляр systemd не запускается при загрузке системы. Если режим ожидания (lingering) включен, возможно, отсутствует секция [Install] в файле .container, либо вы изменили файл, но не выполнили systemctl --user daemon-reload.

Error: statfs /home/ollama/ollama-data: no such file or directory. Исходная директория для bind mount должна существовать до запуска контейнера. Podman не создает директории на хосте автоматически. Выполните mkdir -p ~/ollama-data от имени пользователя ollama.

Ошибка запуска через 90 секунд. journalctl --user -u ollama.service показывает Start operation timed out. Terminating., так как процесс загрузки образа еще не завершился. Выполните загрузку вручную или увеличьте TimeoutStartSec=900.

Контейнер запускается и сразу завершается. podman logs ollama и sudo ausearch -m avc -ts recent вместе позволяют определить, связана ли проблема с меткой SELinux. Сообщение AVC с упоминанием container_t и user_home_t означает, что отсутствует :Z.

Запросы с хоста отклоняются. curl: (7) Failed to connect to 127.0.0.1 port 11434: Connection refused для сервиса active обычно означает, что OLLAMA_HOST был привязан к адресу loopback внутри контейнера. Удалите эту строку.

Генерация происходит очень медленно или контейнер завершается принудительно. Без GPU инференс выполняется на CPU, поэтому работа с большой моделью по определению идет медленно. Если контейнер завершается во время обработки запроса, а в логах присутствует signal: killed, значит, сработал механизм OOM Killer (Out-of-Memory) ядра Linux. Выберите более легкий тег модели из таблицы выше.

Обновление зафиксированного образа

Фиксация версии означает, что обновления выполняются вручную, а не автоматически. Отредактируйте Image= в ollama.container, затем выполните перезагрузку конфигурации и перезапуск службы:

systemctl --user daemon-reload
systemctl --user restart ollama.service
podman exec ollama ollama --version

Модели хранятся в bind mount, поэтому они остаются неизменными при смене образа. Параметр AutoUpdate=registry в секции [Container] предназначен для тех, кто использует динамические теги. Он бесполезен при работе с фиксированным тегом версии, так как содержимое такого тега не меняется. Создайте резервную копию /home/ollama/ollama-data/models/manifests и файла .container. Пропускайте копирование blobs: они занимают много места, а ollama pull загрузит их повторно на новом узле.

FAQ

Почему мой rootless Podman контейнер останавливается после выхода из системы?

Экземпляр systemd пользователя и его каталог /run/user/<uid> завершают работу, когда заканчивается последняя сессия этого пользователя, и все rootless контейнеры завершаются вместе с ними. Выполните sudo loginctl enable-linger ollama и убедитесь, что loginctl show-user ollama --property=Linger выводит Linger=yes. Включите режим lingering перед созданием Quadlet-юнита, так как каталог среды выполнения, необходимый юниту, существует только при активном lingering.

Нужны ли метки SELinux для каталога моделей Ollama?

В Fedora, RHEL, Rocky и AlmaLinux — да, если вы используете bind mount для каталога хоста. Контейнер работает в домене container_t, а каталог в домашней папке имеет метку user_home_t, поэтому запись запрещается и Ollama завершает работу. Добавьте :Z в строку Volume= и выделите отдельный подкаталог, так как изменение меток происходит рекурсивно, а применение :Z ко всей домашней директории нарушит доступ пользователя по SSH-ключам. Именованные тома получают правильные метки от Podman автоматически и не требуют дополнительных действий.

Сколько дискового пространства нужно для модели Ollama?

Ориентируйтесь на опубликованный размер загрузки на ollama.com/library, который варьируется от 3.3 ГБ для gemma3:4b до 19 ГБ для qwen3:30b. Добавьте к этому размер образа Podman и оставьте запас, так как вторая модель не заменяет первую на диске. Проверяйте df -h /home перед загрузкой и du -sh ~/ollama-data/models после. Планируйте объем оперативной памяти аналогично: модели требуется примерно столько же памяти, сколько весит файл модели, плюс размер контекстного окна.

Безопасно ли открывать порт 11434 на VPS?

Нет. Ollama поставляется без какой-либо аутентификации, поэтому любой, кто получит доступ к порту, сможет просматривать ваши модели, удалять их, загружать новые на ваш диск и выполнять инференс, используя ваш процессор и лимит трафика. Обычный HTTP через интернет также передает все запросы и ответы в открытом виде. Привяжите хост-порт к 127.0.0.1 с помощью PublishPort=127.0.0.1:11434:11434, проверьте результат командой ss -ltnp | grep 11434 и обращайтесь к сервису через SSH-туннель или обратный прокси-сервер с обязательной авторизацией.