Запуск Ollama в rootless Podman на VPS
Настройте Ollama в rootless Podman на VPS с помощью Quadlet. В статье описаны параметры lingering для systemd, настройка SELinux и безопасный доступ через 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), но не сессионную шину (session bus), поэтому systemctl --user завершается с ошибкой:
Failed to connect to bus: $DBUS_SESSION_BUS_ADDRESS and $XDG_RUNTIME_DIR not definedsystemd ищет пользовательскую шину по пути $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. В любом случае ollama pull и ollama run записывают веса в одно и то же дерево, а разница между этими двумя командами заключается лишь в том, откроется ли сессия чата после завершения загрузки.
Рассчитайте объем диска перед тем, как что-либо скачивать. Опубликованные размеры загружаемых файлов — это минимальный порог.
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.servicestatus должен показать 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 описаны сами юниты.
Почему при работе с SELinux возникает ошибка permission denied для каталога моделей
В дистрибутивах 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 отдельный подкаталог, в котором нет других данных. Именованные тома (named volumes) не требуют этого флага, так как Podman автоматически присваивает им корректные метки при создании. Если вам нужны дополнительные сведения, в статье Основы SELinux для сервера подробно описаны контексты и булевы значения. В Ubuntu и Debian вместо SELinux используется 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, выполнять вычисления на вашем процессоре и расходовать ваш лимит трафика через /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.
Если доступ нужен браузерному клиенту, установите перед API обратный прокси с паролем. Блок конфигурации сайта в 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. 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.serviceactive означает, что процесс остался в памяти, а секция [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 ядра. Выберите более легкий тег из таблицы выше.
Обновление зафиксированного образа
Фиксация версии означает, что обновления выполняются вручную, а не автоматически. Отредактируйте 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-ключам для этого пользователя. Именованные тома (named volumes) получают корректные метки от 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-туннель или обратный прокси-сервер с обязательной авторизацией.