SSD Nodes Learn 🎉 VPS desde $5.50/mês
Guias Matt ConnorPor Matt Connor

Como rodar Ollama no Podman rootless em uma VPS

Configure Ollama com Podman rootless, usuario dedicado, lingering, Quadlet, SELinux e API fechada na porta 11434, acessivel apenas por tunel SSH.

Executar o Ollama no Podman rootless numa VPS

Para executar o Ollama no Podman rootless num servidor, é necessário cumprir cinco condições que um tutorial para desktop pode ignorar. Um utilizador não privilegiado dedicado é proprietário do contentor. O lingering está ativado para esse utilizador, para que o contentor continue em execução depois de terminar a sessão. Um ficheiro Quadlet entrega o contentor ao systemd, para que este volte a arrancar depois de um reboot. O diretório dos modelos tem um rótulo SELinux nas distribuições que aplicam esse mecanismo. 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 escala (LLM). Armazena os pesos dos modelos em disco, carrega-os para a memória e responde a pedidos HTTP na porta 11434. Não tem login, chave de API nem contas de utilizador, por isso a rede é o único controlo de acesso disponível. O Podman executa contentores sem daemon e sem root, por isso qualquer processo que escape do contentor começa por ser executado como um utilizador não privilegiado comum. Se quiser consultar primeiro a comparação entre runtimes, leia como o Podman e o Docker diferem numa VPS. Se preferir evitar completamente os contentores, instalar o Ollama diretamente numa VPS é um caminho mais curto.

A SSD Nodes disponibiliza o 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 stack em 5 de agosto de 2026: Executar o Ollama localmente com Podman no Fedora Linux, de Yazan Monshed. É um bom ponto de partida para conhecer as ferramentas. No entanto, o guia também se destina a 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 simples podman run -d. 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.
  • Publica com -p 11434:11434, que faz bind em todas as interfaces. Atrás de um router doméstico, o serviço fica inacessível a partir da internet. Num VPS, isso transforma-o numa API pública de inferência sem palavra-passe.
  • Executa como 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 atinja um 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 rever quando o servidor está acessível a partir de qualquer lugar e não há ninguém sentado à sua frente.

Crie o utilizador sem privilégios e verifique o subuid

O Podman sem root mapeia os IDs de utilizador (UID) internos do contentor para um bloco de IDs não utilizados no host. Esse bloco é declarado em /etc/subuid e /etc/subgid. Sem esse bloco, os contentores sem root 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

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

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

O número inicial será diferente, e isso é normal. Se grep não apresentar nada, useradd não atribuiu um intervalo, e o primeiro comando podman executado por esse utilizador falha desta forma:

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

Atribua um intervalo que nenhum outro utilizador possua e, em seguida, 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 significa que ninguém inicia sessão diretamente como ollama. Para aceder à conta, use o seu utilizador administrador com sudo -iu ollama.

Ative o lingering para que o serviço sobreviva ao logout

A instância systemd de um utilizador normalmente é iniciada no login e parada 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 lingering antes de criar a unidade, porque o diretório necessário para a unidade, /run/user/<uid>, só existe quando o lingering está ativo.

Há mais um passo que normalmente não é esperado. sudo -iu ollama fornece uma shell, mas não um bus 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 bus do utilizador em $XDG_RUNTIME_DIR/bus, e sudo -i não define essa variável. Defina-a manualmente em cada shell administrativa 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 armazenados e quanto espaço em disco reservar

Ollama grava os pesos em /root/.ollama/models dentro do container. Monte um diretório do diretório pessoal do utilizador nesse caminho. Assim, os ficheiros ficam num local que pode medir: /home/ollama/ollama-data/models. Os blobs ficam em models/blobs como ficheiros endereçados pelo 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.

Dimensione o disco antes de transferir qualquer modelo. Os tamanhos de download 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 apresentam valores publicados em ollama.com/library, não tamanhos medidos num disco. A tag mais pequena aqui, gemma3:4b, transfere 3.3 GB. A maior, qwen3:30b, transfere 19 GB. A imagem do container ocupa espaço adicional no armazenamento próprio do Podman. Por isso, verifique ambos os valores em conjunto com podman system df e df -h /home. Um modelo também precisa de aproximadamente o tamanho do seu 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 da 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 publicada, 0.32.9 em agosto de 2026, e não latest. Uma tag fixa garante que um restart às 04:00 use o mesmo binário que foi testado. Assim, 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.

Especifique 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 o pull manualmente primeiro é opcional, mas útil. Isso retira o download de vários gigabytes do timeout de arranque da unidade.

A unidade Quadlet que sobrevive a um reboot

Quadlet é o gerador de systemd do Podman. Escreva um ficheiro .container, o systemd transforma-o num serviço no arranque 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 tarefa. O Quadlet cria a ligação para arrancar no boot durante daemon-reload, razão pela qual esse comando não é opcional. TimeoutStartSec=900 abrange um primeiro arranque que ainda tem de descarregar 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 vantagens e desvantagens estão explicadas em manter um modelo Ollama carregado na memória. Se algum dos termos do systemd aqui usados for novo para si, como funcionam os serviços e temporizadores do systemd num VPS explica as próprias unidades.

Por que o diretório de modelos devolve "permission denied" com 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 no diretório pessoal de um utilizador tem o rótulo user_home_t. A política não permite que um interaja com o outro. Por isso, o Ollama não consegue criar a árvore de modelos e o contentor termina. getenforce imprime Enforcing nestes sistemas, e a recusa fica registada:

sudo ausearch -m avc -ts recent

Verá uma linha que identifica o domínio e o rótulo 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. Este comando altera o rótulo do diretório do anfitrião para container_file_t e atribui-lhe uma categoria MCS (multi-category security) privada que apenas este contentor possui. A versão em minúsculas, :z, utiliza um rótulo partilhado. É 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 alteração de rótulos é recursiva. Se apontar o comando para /home/ollama, todos os ficheiros desse diretório pessoal serão alterados, o que impede o acesso às chaves SSH desse utilizador. Atribua sempre a :Z um subdiretório dedicado que não contenha outros ficheiros. Os volumes nomeados não precisam deste comando, porque o Podman atribui-lhes os rótulos corretos quando os cria. Para obter uma visão mais abrangente, conceitos básicos 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 mantê-lo na unidade não causa problemas.

Fechar a porta 11434 e aceder à API por SSH

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

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 a Ollama is running.

Seja preciso sobre o lado ao qual está a associar. O endereço em PublishPort é o endereço do host. Dentro do contentor, o Ollama deve continuar a escutar em todas as interfaces, que é a predefinição da imagem. Definir Environment=OLLAMA_HOST=127.0.0.1 associa o 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é mesmo os provenientes do host.

Manter a porta 11434 aberta cria dois problemas. O 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 seu 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, permitindo que todas as máquinas no caminho os leiam. Ambos os problemas desaparecem se a porta nunca sair do servidor.

Na sua estação de trabalho, encaminhe a porta por SSH:

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

Agora, http://127.0.0.1:11434 no seu computador portátil é o Ollama do servidor, dentro da encriptação da sessão SSH. Se o seu computador portátil já executar o 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 browser precisar da API, coloque antes dele um reverse proxy com palavra-passe. Um bloco de site do Caddy tem quatro linhas, e caddy hash-password mostra 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 autonomamente um certificado através de TLS (transport layer security), pelo que o tráfego é encriptado. Teste primeiro o seu cliente: muitas ferramentas que comunicam com o Ollama não têm um campo para um cabeçalho Authorization e falham com a autenticação básica quando recebem um 401 Unauthorized sem conteúdo. O túnel SSH não tem esse problema, razão pela qual é a recomendação predefinida neste caso.

Obtenha 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 devolve um JSON que lista gemma3:4b. /api/generate devolve um objeto JSON com um campo response, depois de 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 continua ativo; a secção [Install] e daemon-reload funcionaram corretamente. inactive significa que falta uma das três partes.

Modos de falha e 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, o container não arranca automaticamente.

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. 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 a obtenção da imagem ainda estava em curso. 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 são recusados a partir do host. 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 no CPU e um modelo grande é naturalmente lento. Um container que termine a meio de um pedido e apresente signal: killed nos logs foi terminado pelo mecanismo de falta de memória do kernel. 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 deliberada, não algo que acontece sem controlo. 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 é alterada. AutoUpdate=registry na secção [Container] existe para quem utiliza 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 contêiner Podman sem root para quando termino a sessão?

A instância systemd de um utilizador e o respetivo diretório /run/user/<uid> são desmontados quando termina a última sessão desse utilizador, e todos os contêineres sem root 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 depois de o lingering estar ativo.

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

No Fedora, RHEL, Rocky e AlmaLinux, sim, se fizer bind mount de um diretório do host. O contêiner é 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 nova etiquetagem é recursiva e apontar :Z para uma pasta pessoal inteira impede o acesso às chaves SSH desse utilizador. Os volumes nomeados recebem as etiquetas corretas pelo Podman e não precisam de mais nada.

De quanto espaço em disco precisa um modelo Ollama?

Comece pelo tamanho de download publicado em ollama.com/library, que varia entre 3.3 GB para gemma3:4b e 19 GB para qwen3:30b. Acrescente 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 é distribuído sem qualquer tipo de autenticação, por isso qualquer pessoa que alcance a porta pode listar os seus modelos, eliminá-los, fazer pull de novos modelos para o seu disco e executar inferência usando o seu CPU e o seu limite de largura de banda. HTTP simples através da Internet também envia cada prompt e cada resposta em texto sem cifragem. 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 uma palavra-passe.