SSD Nodes Learn 🎉 VPS desde $5.50/mes
Guías Matt ConnorPor Matt Connor

Ollama en Podman rootless en un VPS

Configura Ollama en Podman rootless con usuario dedicado, lingering, Quadlet, SELinux y la API cerrada en el puerto 11434 mediante un túnel SSH.

Ejecutar Ollama en Podman rootless en un VPS

Para ejecutar Ollama en Podman rootless en un servidor, deben cumplirse cinco condiciones que una guía para equipos de escritorio puede omitir. Un usuario sin privilegios dedicado debe ser el propietario del contenedor. El lingering debe estar habilitado para ese usuario, de modo que el contenedor siga ejecutándose después de cerrar la sesión. Un archivo Quadlet debe entregar el contenedor a systemd para que vuelva a iniciarse después de un reinicio. El directorio de modelos debe tener una etiqueta SELinux en las distribuciones que aplican SELinux. La API debe escuchar sólo en loopback y se debe acceder a ella mediante un túnel SSH (secure shell).

Ollama es un servidor para modelos de lenguaje de gran tamaño (LLM). Almacena los pesos de los modelos en disco, los carga en memoria y responde a solicitudes HTTP en el puerto 11434. No tiene inicio de sesión, API key ni cuentas de usuario, por lo que la red es el único control de acceso disponible. Podman ejecuta contenedores sin daemon y sin root, de modo que cualquier proceso que escape del contenedor comienza como un usuario normal sin privilegios. Si quiere consultar primero la comparación del entorno de ejecución, lea cómo se diferencian Podman y Docker en un VPS. Si prefiere omitir los contenedores por completo, instalar Ollama directamente en un VPS es una opción más corta.

SSD Nodes ofrece Fedora entre sus imágenes, y Fedora incluye Podman y SELinux (security-enhanced Linux) de forma predeterminada. Todos los comandos siguientes se ejecutan en cualquier distribución que incluya Podman 5 o una versión posterior.

Por qué la versión para un portátil necesita cambios en un servidor

Fedora Magazine publicó una guía clara sobre esta pila el 5 de agosto de 2026: Ejecutar Ollama localmente con Podman en Fedora Linux, escrita por Yazan Monshed. Es un buen punto de partida para familiarizarse con las herramientas. También está orientada a un portátil, y cuatro de sus decisiones se comportan de otra forma en una máquina con una dirección IP pública.

  • Inicia el contenedor con un `podman run -d` simple. Un contenedor iniciado manualmente no vuelve a iniciarse después de un reinicio porque nunca se configuró su inicio automático.
  • Usa la etiqueta mutable `ollama/ollama`. En un portátil se nota el día en que cambia el comportamiento. En un servidor, la primera señal puede ser un script que dejó de funcionar durante la noche.
  • Publica con `-p 11434:11434`, que enlaza todas las interfaces. Detrás de un router doméstico, no es accesible desde Internet. En un VPS, es una API pública de inferencia sin contraseña.
  • Se ejecuta con su propio usuario de inicio de sesión. En un servidor, la cuenta propietaria del contenedor no debería ser propietaria de ningún otro recurso, para que una salida del contenedor termine en un directorio personal vacío.

Nada de esto es incorrecto para la máquina a la que se destinó la guía. Cada punto es simplemente una decisión que debe revisarse cuando el equipo es accesible desde cualquier lugar y no hay nadie delante de él.

Cree el usuario sin privilegios y compruebe subuid

Rootless Podman asigna los ID de usuario (UID) internos del contenedor a un bloque de ID no utilizados en el host. Ese bloque se declara en /etc/subuid y /etc/subgid. Sin este bloque, los contenedores rootless no pueden iniciarse.

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 debe mostrar dos líneas, una de cada archivo. Cada línea debe indicar un rango de 65536 ID:

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

El número inicial será diferente, y eso es correcto. Si grep no muestra nada, useradd no asignó ningún rango y el primer comando podman ejecutado como ese usuario falla de esta forma:

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

Asigne un rango que no esté asignado a ningún otro usuario y, después, indique a Podman que su asignación anterior está obsoleta:

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

Bloquear la contraseña impide que alguien inicie sesión directamente como ollama. Acceda a esa cuenta desde su usuario administrador con sudo -iu ollama.

Activar el modo persistente para que el servicio sobreviva al cierre de sesión

La instancia de systemd de un usuario normalmente se inicia al entrar en el sistema y se detiene al cerrar la sesión; /run/user/<uid> también se elimina. Todos los contenedores rootless propiedad de ese usuario se detienen en ese momento. El modo persistente mantiene la instancia del usuario en ejecución sin una sesión asociada.

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

Debe mostrar Linger=yes. Actívelo antes de crear la unidad, porque el directorio que necesita la unidad, /run/user/<uid>, sólo existe cuando el modo persistente está activado.

Hay un paso más que suele pasar desapercibido. sudo -iu ollama proporciona un shell, pero no un bus de sesión, por lo que systemctl --user falla inmediatamente:

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

systemd busca el bus de usuario en $XDG_RUNTIME_DIR/bus, y sudo -i no establece esa variable. Defínala manualmente en cada shell de administración desde el que gestione este servicio:

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

Dónde se almacenan los blobs de los modelos y cuánto espacio de disco reservar

Ollama escribe los pesos en /root/.ollama/models dentro del contenedor. Asigne un directorio del directorio personal del usuario a esa ruta mediante un bind mount. Así, los archivos se almacenan en una ubicación que puede medir: /home/ollama/ollama-data/models. Los blobs se guardan en models/blobs como archivos direccionados por contenido, y models/manifests contiene el índice pequeño que les asigna nombres. Si usa un volumen con nombre, como en el artículo de Fedora Magazine, el mismo árbol se encuentra en /home/ollama/.local/share/containers/storage/volumes/<volume>/_data.

Calcule el espacio de disco antes de descargar nada. Los tamaños de descarga publicados proporcionan el mínimo necesario.

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

Las 7 filas son cifras publicadas en ollama.com/library, no tamaños medidos en disco. La etiqueta más pequeña de esta lista, gemma3:4b, descarga 3.3 GB. La más grande, qwen3:30b, descarga 19 GB. La imagen del contenedor ocupa espacio adicional en el almacenamiento propio de Podman, por lo que debe comprobar ambos valores junto con podman system df y df -h /home. Un modelo también necesita aproximadamente en RAM el mismo espacio que ocupa su archivo mientras está cargado, además del espacio para la ventana de contexto. Por tanto, un modelo de 19 GB no se ejecutará en un VPS de 16 GB.

Fije la etiqueta de la imagen y use el nombre completo del registro

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 una etiqueta de versión publicada, 0.32.9 en agosto de 2026, y no latest. Una etiqueta fijada garantiza que un reinicio a las 04:00 use el mismo binario que probó, por lo que cualquier cambio de comportamiento se debe a un cambio que usted hizo. Docker Hub también publica las etiquetas -rc y -rocm para las mismas versiones; elija la etiqueta sin sufijo salvo que tenga una GPU AMD.

Especifique también el host del registro. En Fedora, un nombre corto en una unidad de systemd no tiene un terminal donde solicitar confirmación, y la unidad falla con:

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

Extraer la imagen manualmente antes es opcional, pero resulta útil porque saca la descarga de varios gigabytes del tiempo de espera de inicio de la unidad.

La unidad Quadlet que sobrevive a un reinicio

Quadlet es el generador de systemd de Podman. Se escribe un archivo .container, systemd lo convierte en un servicio durante el arranque y ya no se necesita podman generate systemd. Guarde esto como /home/ollama/.config/containers/systemd/ollama.container, con el usuario ollama como propietario.

[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

El nombre del archivo determina el nombre del servicio, por lo que ollama.container se convierte en ollama.service.

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

status debería mostrar active (running). No ejecute systemctl --user enable ollama.service. La unidad no existe como archivo en el disco, por lo que systemd la rechaza:

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

La sección [Install] ya realiza esa tarea. Quadlet crea por sí mismo el enlace para iniciar el servicio durante daemon-reload, por lo que ese comando es obligatorio. TimeoutStartSec=900 cubre un primer arranque que todavía debe descargar la imagen, porque los 90 segundos predeterminados no bastan para una descarga de dos gigabytes y systemd finalizará el arranque como fallido. OLLAMA_KEEP_ALIVE=30m mantiene un modelo en memoria entre solicitudes en lugar de descargarlo después de cinco minutos; las ventajas y desventajas se explican en mantener un modelo de Ollama cargado en memoria. Si algún término de systemd usado aquí es nuevo para usted, cómo funcionan los servicios y temporizadores de systemd en un VPS explica las propias unidades.

Por qué el directorio de modelos devuelve «permiso denegado» con SELinux

En Fedora, RHEL, Rocky y AlmaLinux, SELinux está en modo enforcing de forma predeterminada. Un proceso de contenedor se ejecuta en el dominio container_t y un directorio del directorio personal de un usuario tiene la etiqueta user_home_t. La política no permite que uno acceda al otro, por lo que Ollama no puede crear su árbol de modelos y el contenedor termina. getenforce muestra Enforcing en estos sistemas y el rechazo queda registrado:

sudo ausearch -m avc -ts recent

Verá una línea que indica el dominio y la etiqueta del 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

El :Z al final de la línea Volume= es la solución. Vuelve a etiquetar el directorio del host como container_file_t y le asigna una categoría MCS (seguridad mult categoría) privada que sólo lleva este contenedor. La opción en minúscula :z usa una etiqueta compartida, que es la adecuada cuando dos contenedores leen el mismo directorio.

Una advertencia sobre :Z, porque es destructivo y silencioso. El cambio de etiquetas se aplica de forma recursiva. Si apunta a /home/ollama, vuelve a etiquetar todos los archivos de ese directorio personal, lo que impide al usuario acceder a sus claves SSH. Asigne siempre a :Z un subdirectorio dedicado que no contenga nada más. Los volúmenes con nombre no lo necesitan, porque Podman les asigna las etiquetas correctas al crearlos. Si necesita una explicación más amplia, Conceptos básicos de SELinux para un servidor explica los contextos y los booleanos. En Ubuntu y Debian se usa AppArmor; allí :Z no hace nada y dejarlo en la unidad no causa problemas.

Cierre el puerto 11434 y acceda a la API mediante SSH

PublishPort=127.0.0.1:11434:11434 enlaza el lado del host con la interfaz de loopback. Confírmelo:

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

La salida de ss debe mostrar 127.0.0.1:11434. 0.0.0.0:11434 o *:11434 significa que el puerto está abierto a Internet, y curl debe responder a Ollama is running.

Sea preciso sobre el lado que está enlazando. La dirección de PublishPort es la dirección del host. Dentro del contenedor, Ollama debe seguir escuchando en todas las interfaces, que es el valor predeterminado de la imagen. Establecer Environment=OLLAMA_HOST=127.0.0.1 enlaza Ollama con el loopback propio del contenedor. Podman reenvía el tráfico publicado a la dirección de red del contenedor, por lo que todas las peticiones se rechazan incluso desde el host.

Tener 11434 abierto le perjudica de dos formas. Ollama no tiene autenticación, por lo que cualquiera que acceda al puerto puede enumerar sus modelos mediante /api/tags, ejecutar inferencias usando la CPU y la cuota de ancho de banda mediante /api/generate, descargar modelos nuevos en el disco y eliminar los modelos existentes. Además, HTTP sin cifrado hacia un puerto remoto envía las solicitudes y las respuestas en texto claro. Todas las máquinas que atraviesen la conexión pueden leerlas. Ambos problemas desaparecen si el puerto nunca sale del equipo.

Desde su estación de trabajo, reenvíe el puerto mediante SSH:

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

Ahora http://127.0.0.1:11434 en su portátil es el Ollama del servidor, dentro del cifrado de la sesión SSH. Si su portátil ya ejecuta Ollama, el enlace local falla con bind [127.0.0.1]:11434: Address already in use. Use -L 11435:127.0.0.1:11434 y dirija el cliente a 11435.

Cuando lo necesite un cliente de navegador, coloque delante un reverse proxy con contraseña. Un bloque de sitio de Caddy tiene cuatro líneas, y caddy hash-password imprime el hash bcrypt que necesita:

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

Caddy obtiene por su cuenta un certificado mediante TLS (seguridad de la capa de transporte), por lo que el tráfico está cifrado. Pruebe primero el cliente: muchas herramientas que se comunican con Ollama no tienen un campo para una cabecera Authorization y fallarán frente a la autenticación básica con un 401 Unauthorized sin contenido. El túnel SSH no presenta ese problema, por lo que es la recomendación predeterminada aquí.

Descargue un modelo y compruebe toda la ruta

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 devuelve un JSON que contiene la lista de gemma3:4b. /api/generate devuelve un objeto JSON con un campo response, después de una pausa mientras los pesos se cargan desde el disco. du debería mostrar un número cercano al tamaño de descarga publicado. A continuación, compruebe la parte central de esta guía:

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

active significa que la configuración persiste; la sección [Install] y daemon-reload han funcionado correctamente. inactive significa que falta uno de los tres elementos.

Modos de fallo y mensajes que verá

El contenedor desaparece después de reiniciar. Compruebe primero loginctl show-user ollama --property=Linger, porque sin Linger=yes la instancia de systemd del usuario nunca se inicia durante el arranque. Si el mecanismo de persistencia está activado, falta la sección [Install] en el archivo .container o editó el archivo y no ejecutó systemctl --user daemon-reload.

Error: statfs /home/ollama/ollama-data: no such file or directory. El origen del bind mount debe existir antes de iniciar el contenedor. Podman no crea directorios en el host automáticamente. Ejecute mkdir -p ~/ollama-data como el usuario ollama.

El inicio falla a los 90 segundos. journalctl --user -u ollama.service muestra Start operation timed out. Terminating. porque la descarga de la imagen todavía estaba en curso. Descárguela manualmente o mantenga TimeoutStartSec=900.

El contenedor se inicia y se cierra. podman logs ollama y sudo ausearch -m avc -ts recent juntos indican si el problema está relacionado con la etiqueta de SELinux. Un AVC que menciona container_t y user_home_t significa que falta :Z.

El host rechaza las solicitudes. curl: (7) Failed to connect to 127.0.0.1 port 11434: Connection refused con el servicio active suele indicar que OLLAMA_HOST se configuró con una dirección de loopback dentro del contenedor. Elimine esa línea.

La generación es muy lenta o el contenedor termina. Sin GPU, la inferencia se ejecuta en la CPU y un modelo grande es lento por naturaleza. Si un contenedor termina a mitad de una solicitud y los registros contienen signal: killed, intervino el terminador de procesos por falta de memoria del kernel. Elija una etiqueta más pequeña de la tabla anterior.

Actualizar una imagen fijada

Fijar una versión significa que las actualizaciones son una tarea planificada. Edite Image= en ollama.container y, después, vuelva a cargar y reinicie:

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

Los modelos están en el montaje de enlace, por lo que permanecen intactos aunque cambie la imagen. AutoUpdate=registry, en la sección [Container], existe para quienes ejecutan una etiqueta móvil. No resulta útil junto a una etiqueta de versión fija, porque el contenido de esa etiqueta nunca cambia. Haga una copia de seguridad de /home/ollama/ollama-data/models/manifests y del archivo .container, y omita los blobs: son grandes y ollama pull los descarga de nuevo en un equipo nuevo.

FAQ

¿Por qué se detiene mi contenedor rootless de Podman cuando cierro la sesión?

La instancia de systemd de un usuario y su directorio /run/user/<uid> se desmontan cuando termina la última sesión de ese usuario, y todos los contenedores rootless se detienen con ellos. Ejecute sudo loginctl enable-linger ollama y confirme que loginctl show-user ollama --property=Linger muestre Linger=yes. Habilite el modo lingering antes de crear la unidad de Quadlet, porque el directorio de tiempo de ejecución que necesita la unidad sólo existe cuando el modo lingering está habilitado.

¿Necesito etiquetas de SELinux en el directorio de modelos de Ollama?

En Fedora, RHEL, Rocky y AlmaLinux, sí, si monta un directorio del host mediante bind mount. El contenedor se ejecuta en el dominio container_t y un directorio situado en una carpeta personal tiene la etiqueta user_home_t, por lo que la escritura se deniega y Ollama se cierra. Añada :Z a la línea Volume= y asígnele un subdirectorio dedicado, porque el cambio recursivo de etiquetas afecta a todo el contenido y apuntar :Z a una carpeta personal completa impide el acceso del usuario a sus claves SSH. Podman etiqueta correctamente los volúmenes con nombre y no requieren nada más.

¿Cuánto espacio en disco necesita un modelo de Ollama?

Tome como referencia el tamaño de descarga publicado en ollama.com/library, que va desde 3.3 GB para gemma3:4b hasta 19 GB para qwen3:30b. Añada la imagen de Podman y deje espacio adicional, porque un segundo modelo no sustituye al primero en el disco. Compruebe df -h /home antes de descargarlo y du -sh ~/ollama-data/models después. Planifique la RAM del mismo modo: un modelo necesita aproximadamente el tamaño de su archivo en memoria mientras está cargado, además de la ventana de contexto.

¿Es seguro exponer el puerto 11434 en un VPS?

No. Ollama se distribuye sin ningún tipo de autenticación, por lo que cualquiera que alcance el puerto puede enumerar los modelos, eliminarlos, descargar otros en el disco y ejecutar inferencias usando la CPU y la cuota de ancho de banda. El tráfico HTTP sin cifrar por Internet también envía cada consulta y cada respuesta en texto claro. Enlace el lado del host a 127.0.0.1 con PublishPort=127.0.0.1:11434:11434, confirme el resultado con ss -ltnp | grep 11434 y acceda mediante un túnel SSH o un reverse proxy que requiera una contraseña.