SSD Nodes Learn Hosting plans →
Guias Matt ConnorPor Matt Connor · Atualizado 2026-09-04

Como executar o Ollama no Podman rootless numa VPS

Aprenda a executar o Ollama no Podman rootless com utilizador dedicado, Quadlet, lingering, SELinux e API fechada na porta 11434, acessível por túnel SSH.

Execute Ollama no Podman rootless numa VPS

Para executar o Ollama no Podman rootless num servidor, têm de estar reunidas cinco condições que um guia para desktop pode ignorar. Um utilizador não privilegiado dedicado é o proprietário do contentor. O lingering está ativado para esse utilizador, para que o contentor continue a executar depois de terminar a sessão. Um ficheiro Quadlet entrega o contentor ao systemd, para que este volte a iniciar depois de um reboot. O diretório dos modelos tem um rótulo SELinux nas distribuições que o aplicam. A API escuta apenas no loopback, e o acesso é feito através de um túnel SSH (secure shell).

O Ollama é um servidor para modelos de linguagem de grande dimensão (LLM). Armazena os pesos dos modelos no disco, carrega-os para a memória e responde a pedidos HTTP na porta 11434. Não tem login, API key nem contas de utilizador, pelo que a rede é o único controlo de acesso disponível. O Podman executa contentores sem daemon e sem root, pelo que qualquer processo que escape do contentor começa por ser executado como um utilizador normal não privilegiado. Se quiser consultar primeiro a comparação do runtime, leia como o Podman e o Docker diferem numa VPS. Se preferir ignorar completamente os contentores, instalar o Ollama diretamente numa VPS é um caminho mais curto.

A SSD Nodes disponibiliza Fedora entre as suas imagens, e o Fedora inclui o Podman e o SELinux (security-enhanced Linux) por predefinição. Todos os comandos abaixo funcionam em qualquer distribuição com Podman 5 ou mais recente.

Por que a versão para laptop precisa de alterações num servidor

A Fedora Magazine publicou um guia claro sobre esta pilha em 5 August 2026: Running Ollama Locally with Podman on Fedora Linux, de Yazan Monshed. É um bom primeiro contacto de uma hora com as ferramentas. O guia também tem como alvo um laptop, e quatro das suas escolhas comportam-se de forma diferente numa máquina com um endereço IP público.

  • Inicia o contentor com um `podman run -d` simples. Um contentor iniciado manualmente não volta a arrancar depois de um reboot, porque nunca foi configurado para arrancar.
  • Usa a tag mutável `ollama/ollama`. Num laptop, nota o dia em que o comportamento muda. Num servidor, o primeiro sinal é um script que deixou de funcionar durante a noite.
  • Faz a publicação com `-p 11434:11434`, que associa o serviço a todas as interfaces. Atrás de um router doméstico, o serviço fica inacessível a partir da internet. Numa VPS, transforma-se numa API pública de inferência sem palavra-passe.
  • É executado com o seu próprio utilizador de login. Num servidor, a conta proprietária do contentor não deve ser proprietária de mais nada, para que uma fuga do contentor termine num diretório home vazio.

Nada disso está errado para a máquina a que o guia se destina. Cada item é simplesmente uma decisão que deve ser revista quando o sistema está acessível a partir de qualquer lugar e não há ninguém sentado à sua frente.

Criar o utilizador sem privilégios e verificar subuid

O Podman rootless mapeia os IDs de utilizador internos do contentor (UID) para um bloco de IDs não utilizados no host. Esse bloco é declarado em /etc/subuid e /etc/subgid. Sem ele, os contentores rootless não conseguem iniciar.

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

O comando grep deve apresentar duas linhas, uma de cada ficheiro, cada uma a indicar um intervalo de 65536 IDs:

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

O número inicial será diferente no seu sistema, o que é normal. Se grep não apresentar nada, useradd não atribuiu um intervalo, e o primeiro comando podman executado por esse utilizador falha com esta mensagem:

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

Atribua um intervalo que não esteja atribuído a outro utilizador e informe o Podman de que o mapeamento antigo está obsoleto:

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

Bloquear a palavra-passe impede que alguém inicie sessão diretamente como ollama. Para aceder à conta, utilize sudo -iu ollama a partir do seu utilizador administrativo.

Ative o lingering para o serviço sobreviver ao logout

A instância systemd de um utilizador normalmente inicia no login e para no logout, e /run/user/<uid> é removido com ela. Todos os contentores rootless pertencentes a esse utilizador terminam no mesmo momento. O lingering mantém a instância do utilizador em execução sem uma sessão associada.

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

O comando deve apresentar Linger=yes. Ative-o antes de criar a unidade, porque o diretório necessário para a unidade, /run/user/<uid>, só existe depois de o lingering ser ativado.

Há mais um passo que normalmente não é esperado. sudo -iu ollama fornece uma shell, mas não um barramento de sessão, por isso systemctl --user falha imediatamente:

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

O systemd procura o barramento do utilizador em $XDG_RUNTIME_DIR/bus, e sudo -i não define essa variável. Defina-a manualmente em cada shell de administração onde gerir este serviço:

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

Onde os blobs dos modelos são gravados e quanto espaço em disco reservar

Ollama grava os pesos em /root/.ollama/models dentro do contentor. Associe um diretório do diretório pessoal do utilizador a esse caminho, para que os ficheiros sejam gravados num local que possa medir: /home/ollama/ollama-data/models. Os blobs ficam em models/blobs como ficheiros endereçados por conteúdo, e models/manifests contém o pequeno índice que lhes atribui nomes. Se utilizar um volume nomeado, como no artigo da Fedora Magazine, a mesma árvore fica em /home/ollama/.local/share/containers/storage/volumes/<volume>/_data. Em ambos os casos, ollama pull e ollama run gravam os pesos na mesma árvore. O que distingue os dois comandos é apenas o facto de uma sessão de chat ser iniciada depois de a transferência terminar.

Dimensione o disco antes de transferir qualquer ficheiro. Os tamanhos de transferência publicados indicam o mínimo necessário.

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
  }
]

Todas as 7 linhas são valores publicados em ollama.com/library, não tamanhos medidos num disco. A tag mais pequena desta lista, gemma3:4b, transfere 3.3 GB. A maior, qwen3:30b, transfere 19 GB. A imagem do contentor ocupa espaço adicional no armazenamento próprio do Podman. Por isso, confirme ambos os valores em conjunto com podman system df e df -h /home. Um modelo também precisa de aproximadamente o tamanho do ficheiro em RAM enquanto está carregado, além de espaço para a janela de contexto. Por isso, um modelo de 19 GB não será executado numa VPS com 16 GB.

Fixe a tag de imagem e use o nome completo do registry

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

Use uma tag de versão lançada, 0.32.9 em agosto de 2026, e não latest. Uma tag fixa garante que um reinício às 04:00 use o mesmo binário que testou, por isso qualquer alteração de comportamento resulta de uma alteração feita por si. O Docker Hub também publica as tags -rc e -rocm para as mesmas versões. Escolha a tag simples, exceto se tiver uma GPU AMD.

Escreva também o host do registry. No Fedora, um nome curto numa unidade systemd não tem um terminal onde possa pedir confirmação, e a unidade falha com:

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

Fazer primeiro o pull manualmente é opcional, mas útil, porque retira o download de vários gigabytes do tempo limite de arranque da unidade.

A unidade Quadlet que sobrevive a um reboot

Quadlet é o gerador systemd do Podman. Escreve um ficheiro .container, o systemd transforma-o num serviço no boot e podman generate systemd deixa de ser necessário. Guarde-o como /home/ollama/.config/containers/systemd/ollama.container, pertencente ao utilizador 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

O nome do ficheiro define o nome do serviço, por isso ollama.container torna-se ollama.service.

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

status deve mostrar active (running). Não execute systemctl --user enable ollama.service. A unidade não existe como ficheiro no disco, por isso o systemd recusa:

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

A secção [Install] já executa essa função. O Quadlet cria a ligação de arranque no boot durante daemon-reload, razão pela qual esse comando não é opcional. TimeoutStartSec=900 cobre um primeiro arranque que ainda tem de obter a imagem, porque os 90 segundos predefinidos não são suficientes para uma transferência de dois gigabytes e o systemd termina o arranque como falhado. OLLAMA_KEEP_ALIVE=30m mantém um modelo na memória entre pedidos, em vez de o descarregar após cinco minutos; as contrapartidas estão em manter um modelo Ollama carregado na memória. Se algum dos termos do systemd usados aqui for novo, como funcionam os serviços e temporizadores systemd numa VPS explica as próprias unidades.

Por que o diretório de modelos retorna "permission denied" com o SELinux

No Fedora, RHEL, Rocky e AlmaLinux, o SELinux é aplicado por padrão. Um processo de contentor é executado no domínio container_t, e um diretório na home de um utilizador recebe a etiqueta user_home_t. A política não permite que um interaja com o outro. Por isso, o Ollama não consegue criar a sua árvore de modelos e o contentor termina. Nestes sistemas, getenforce apresenta Enforcing, e a negação é registada:

sudo ausearch -m avc -ts recent

Verá uma linha que identifica o domínio e a etiqueta do destino:

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

O :Z no fim da linha Volume= é a correção. Ele volta a etiquetar o diretório do host como container_file_t e atribui-lhe uma categoria MCS privada (multi-category security) que apenas este contentor possui. O :z em minúsculas usa uma etiqueta partilhada. É essa a opção correta quando dois contentores leem o mesmo diretório.

Tenha atenção ao :Z, porque esta operação é destrutiva e silenciosa. A etiquetagem é recursiva. Se a apontar para /home/ollama, todos os ficheiros desse diretório home serão novamente etiquetados, o que impede o acesso às chaves SSH desse utilizador. Atribua sempre a :Z um subdiretório dedicado que não contenha mais nada. Os volumes nomeados não precisam disso, porque o Podman atribui-lhes as etiquetas corretas quando os cria. Para obter uma visão mais ampla, noções básicas do SELinux para um servidor explica os contextos e os booleanos. No Ubuntu e no Debian, é utilizado o AppArmor. Nesse caso, :Z não faz nada, e deixá-lo na unidade não causa problemas.

Feche a porta 11434 e aceda à API através de SSH

PublishPort=127.0.0.1:11434:11434 associa o lado do host ao loopback. Confirme-o:

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

A saída de ss deve mostrar 127.0.0.1:11434. 0.0.0.0:11434 ou *:11434 significa que a porta está aberta à Internet, e curl deve responder Ollama is running.

Seja preciso sobre o lado ao qual está a associar o endereço. O endereço em PublishPort é o endereço do host. Dentro do contentor, Ollama deve continuar a escutar em todas as interfaces, que é a predefinição da imagem. Definir Environment=OLLAMA_HOST=127.0.0.1 associa Ollama ao loopback do próprio contentor, e o Podman encaminha o tráfego publicado para o endereço de rede do contentor. Por isso, todos os pedidos são recusados, até os enviados a partir do host.

Manter 11434 aberta causa dois problemas. Ollama não tem autenticação, por isso qualquer pessoa que alcance a porta pode listar os seus modelos através de /api/tags, executar inferência no seu CPU e consumir o limite de largura de banda através de /api/generate, transferir novos modelos para o disco e eliminar os modelos existentes. Além disso, HTTP simples para uma porta remota envia os prompts e as respostas em texto simples. Qualquer máquina no caminho pode lê-los. Ambos os problemas desaparecem se a porta nunca sair do servidor.

A partir da sua estação de trabalho, encaminhe a porta através de SSH:

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

Agora http://127.0.0.1:11434 no seu portátil é o Ollama do servidor, dentro da encriptação da sessão SSH. Se o seu portátil já executar Ollama, a associação local falha com bind [127.0.0.1]:11434: Address already in use. Use -L 11435:127.0.0.1:11434 e aponte o seu cliente para 11435.

Quando um cliente de navegador precisar desse acesso, coloque antes dele um reverse proxy com palavra-passe. Um bloco de site do Caddy tem quatro linhas, e caddy hash-password apresenta o hash bcrypt necessário:

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

O Caddy obtém automaticamente um certificado através de TLS (transport layer security), por isso o tráfego é encriptado. Teste primeiro o seu cliente: muitas ferramentas que comunicam com Ollama não têm um campo para um cabeçalho Authorization e falham perante a autenticação básica com um 401 Unauthorized simples. O túnel SSH não tem esse problema, razão pela qual é a recomendação predefinida neste caso.

Puxe um modelo e verifique todo o percurso

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 retorna JSON com a lista de gemma3:4b. /api/generate retorna um objeto JSON com um campo response, após uma pausa enquanto os pesos são carregados do disco. du deve apresentar um número próximo do tamanho de download publicado. Em seguida, confirme a parte a que todo este guia se destina:

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

active significa que o processo permanece em execução, e que a secção [Install] e daemon-reload funcionaram corretamente. inactive significa que falta um dos três componentes.

Modos de falha e as mensagens apresentadas

O container desaparece depois de um reboot. Verifique primeiro loginctl show-user ollama --property=Linger, porque, sem Linger=yes, a instância systemd do utilizador nunca arranca no boot. Se o lingering estiver ativo, a secção [Install] estiver em falta no ficheiro .container ou tiver editado o ficheiro sem executar systemctl --user daemon-reload, este problema pode ocorrer.

Error: statfs /home/ollama/ollama-data: no such file or directory. A origem do bind mount tem de existir antes de o container arrancar. O Podman não cria diretórios no host por si. Execute mkdir -p ~/ollama-data como o utilizador ollama.

O arranque falha aos 90 segundos. journalctl --user -u ollama.service mostra Start operation timed out. Terminating. porque o pull da imagem ainda estava em execução. Faça o pull manualmente ou mantenha TimeoutStartSec=900.

O container arranca e termina. podman logs ollama e sudo ausearch -m avc -ts recent, em conjunto, indicam se o problema está na etiqueta SELinux. Um AVC que mencione container_t e user_home_t significa que falta :Z.

Os pedidos enviados a partir do host são recusados. curl: (7) Failed to connect to 127.0.0.1 port 11434: Connection refused com o serviço active normalmente significa que OLLAMA_HOST foi definido como um endereço de loopback dentro do container. Remova essa linha.

A geração é muito lenta ou o container é terminado. Sem GPU, a inferência é executada na CPU e um modelo grande é naturalmente lento. Um container que termine durante um pedido e tenha signal: killed nos logs foi terminado pelo out-of-memory killer do kernel. Nesse caso, escolha uma tag mais pequena no quadro acima.

Atualizar uma imagem fixada

Fixar uma versão significa que as atualizações são uma ação explícita, não algo que acontece automaticamente. Edite Image= em ollama.container e, em seguida, recarregue e reinicie:

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

Os modelos ficam no bind mount, por isso permanecem intactos quando a imagem muda. AutoUpdate=registry na secção [Container] existe para quem usa uma tag móvel e não tem utilidade junto de uma tag de versão fixa, porque o conteúdo dessa tag nunca muda. Faça uma cópia de segurança de /home/ollama/ollama-data/models/manifests e do ficheiro .container e ignore os blobs: são grandes, e ollama pull volta a obtê-los numa máquina nova.

FAQ

Por que o meu contentor Podman rootless para quando termino a sessão?

A instância systemd de um utilizador e o seu diretório /run/user/<uid> são desmontados quando termina a última sessão desse utilizador, e todos os contentores rootless são terminados com eles. Execute sudo loginctl enable-linger ollama e confirme que loginctl show-user ollama --property=Linger apresenta Linger=yes. Ative o lingering antes de criar a unidade Quadlet, porque o diretório de runtime de que a unidade precisa só existe quando o lingering está ativo.

Preciso de etiquetas SELinux no diretório dos modelos do Ollama?

No Fedora, RHEL, Rocky e AlmaLinux, sim, se montar um diretório do host com bind mount. O contentor é executado no domínio container_t e um diretório dentro de uma pasta pessoal tem a etiqueta user_home_t, por isso a escrita é negada e o Ollama termina. Acrescente :Z à linha Volume= e atribua-lhe um subdiretório dedicado, porque a reetiquetagem é recursiva e apontar :Z para uma pasta pessoal inteira impede o acesso às chaves SSH desse utilizador. Os volumes nomeados recebem as etiquetas corretas do Podman e não precisam de configuração adicional.

Quanto espaço em disco precisa um modelo do Ollama?

Comece pelo tamanho de transferência publicado em ollama.com/library, que varia entre 3.3 GB para gemma3:4b e 19 GB para qwen3:30b. Some a imagem Podman e deixe espaço livre adicional, porque um segundo modelo não substitui o primeiro no disco. Verifique df -h /home antes de fazer o pull e du -sh ~/ollama-data/models depois. Planeie a RAM da mesma forma: enquanto está carregado, um modelo precisa aproximadamente do seu tamanho em ficheiro na memória, além da janela de contexto.

É seguro expor a porta 11434 numa VPS?

Não. O Ollama não inclui qualquer autenticação, por isso qualquer pessoa que alcance a porta pode listar os seus modelos, eliminá-los, fazer pull de novos modelos para o disco e executar inferência utilizando a CPU e o limite de largura de banda. O HTTP simples através da Internet também envia cada prompt e cada resposta em texto simples. Faça o bind do lado do host a 127.0.0.1 com PublishPort=127.0.0.1:11434:11434, confirme com ss -ltnp | grep 11434 e aceda através de um túnel SSH ou de um reverse proxy que exija palavra-passe.