SSD Nodes Learn 🎉 VPS desde $5.50/mes
Guías Matt ConnorPor Matt Connor · Actualizado 2026-08-21

Instalar llama.cpp con llama-server en un VPS

Compile llama-server desde una etiqueta fijada, sirva modelos GGUF con la API compatible con OpenAI, use localhost y controle la memoria con systemd.

Qué va a crear

Ejecutará el servidor de llama.cpp en un VPS mediante un único binario, llama-server, que carga un solo archivo de modelo GGUF y responde a solicitudes HTTP mediante una API compatible con OpenAI. Apunte cualquier cliente de OpenAI a http://127.0.0.1:8080/v1 y funcionará. La instalación es la parte sencilla.

El resto del trabajo corresponde a las operaciones: fijar una versión, mantener el puerto en localhost, escribir una unidad de systemd y decidir qué ocurre cuando el servidor se queda sin memoria. Esto es lo que cubre esta guía. Si todavía no ha elegido entre las dos opciones más evidentes, lea primero las diferencias entre Ollama y llama.cpp, porque esta es la guía práctica que esa comparación omite deliberadamente.

Elija una etiqueta de versión y anótela

llama.cpp crea una etiqueta de versión para casi cada merge, por lo que las etiquetas son números de compilación. b10488 es la más reciente a fecha de 18 August 2026. No existe una rama estable de larga duración. Por tanto, "latest" cambia constantemente y la versión que probó es la única que puede admitir. Elija una etiqueta, regístrela y use esa misma cadena en el clon, en el nombre del binario y en sus notas.

Cada etiqueta también incluye archivos precompilados. Para una VPS x86 sólo con CPU, esa etiqueta es llama-b10488-bin-ubuntu-x64.tar.gz. Si usa una VPS ARM en lugar de x86, encontrará un archivo arm64 junto a ella.

curl -LO https://github.com/ggml-org/llama.cpp/releases/download/b10488/llama-b10488-bin-ubuntu-x64.tar.gz
tar tf llama-b10488-bin-ubuntu-x64.tar.gz | head

Enumere el archivo antes de extraerlo para saber dónde se instalarán los archivos. Esos binarios están enlazados con la biblioteca C de la imagen que los compiló. En una distribución más antigua, fallan al iniciarse y muestran un error que menciona una versión de GLIBC_ que no está instalada. Compilar desde el código fuente tarda unos minutos en una VPS pequeña y elimina por completo esta clase de problemas. Por eso, ese es el procedimiento que se explica a continuación.

Compile llama-server desde una etiqueta fijada

sudo apt update
sudo apt install -y build-essential cmake git libssl-dev
git clone --depth 1 --branch b10488 https://github.com/ggml-org/llama.cpp
cd llama.cpp
cmake -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF -DLLAMA_BUILD_TESTS=OFF -DLLAMA_BUILD_EXAMPLES=OFF
cmake --build build --config Release -t llama-server -j 2

--branch b10488 en un clon --depth 1 extrae esa etiqueta y nada más, por lo que la compilación no puede desviarse mientras trabaja.

libssl-dev es importante porque la opción LLAMA_OPENSSL está activada de forma predeterminada. Esta opción permite que el binario descargue modelos mediante HTTPS más adelante. Sin las cabeceras, el paso de configuración falla.

-DBUILD_SHARED_LIBS=OFF genera un único binario autónomo. La compilación predeterminada coloca las bibliotecas compartidas junto al ejecutable. Por eso, copiar sólo el ejecutable a /usr/local/bin provoca después el error error while loading shared libraries: libllama.so.

-t llama-server compila sólo el destino del servidor. La compilación predeterminada también compila las demás herramientas y las pruebas. En una VPS con dos núcleos, esto añade varios minutos para archivos que nunca ejecutará.

-j 2 es intencionado. Cada trabajo de compilación en paralelo mantiene su propio conjunto de trabajo. Por eso, -j $(nproc) en un plan pequeño termina con c++: fatal error: Killed signal terminated program cc1plus, cuando el asesino de procesos por falta de memoria del kernel detiene el compilador. Reduzca el número de trabajos o añada swap para la compilación.

Hay una opción que quizá quiera cambiar: GGML_NATIVE está activada de forma predeterminada, por lo que el compilador genera código para la CPU exacta que realiza la compilación. Esto es lo que necesita cuando compila en la máquina donde ejecutará el binario. Si compila una vez y copia el binario a otro host, añada -DGGML_NATIVE=OFF. De lo contrario, un binario que use instrucciones ausentes en la otra CPU termina con Illegal instruction (core dumped) durante la primera inferencia.

Instálelo con un nombre que incluya la etiqueta.

./build/bin/llama-server --version
sudo install -m 755 build/bin/llama-server /usr/local/bin/llama-server-b10488
sudo ln -sfn /usr/local/bin/llama-server-b10488 /usr/local/bin/llama-server

--version muestra el número de compilación y el commit. Debe coincidir con la etiqueta que extrajo. Si no coincide, compiló otra cosa. Mantener el número en el nombre del archivo y apuntar un enlace simbólico a ese archivo permite actualizar con un ln -sfn y un reinicio. Para revertir la actualización, use el mismo comando con el número anterior.

Obtenga un modelo GGUF y compruebe primero el espacio en disco

GGUF es el formato de archivo único que carga llama.cpp. Un archivo contiene los pesos, el tokenizador y los metadatos, por lo que no es necesario instalar nada más. El sufijo del nombre de archivo indica la cuantización, es decir, la precisión con la que se almacenan los pesos: Q4_K_M es una combinación de 4 bits, Q8_0 usa 8 bits y f16 es el archivo de media precisión sin cuantizar.

Cree una cuenta de servicio y un directorio para el modelo antes de descargar cualquier archivo.

sudo useradd --system --home /srv/llama --create-home --shell /usr/sbin/nologin llama
sudo install -d -o llama -g llama /srv/models
df -h /srv

El servidor puede obtener un modelo directamente con -hf. Es la forma más rápida de comprobar que la compilación funciona.

sudo -u llama env LLAMA_CACHE=/srv/models /usr/local/bin/llama-server \
  -hf ggml-org/gemma-3-1b-it-GGUF:Q4_K_M --host 127.0.0.1 --port 8080

LLAMA_CACHE establece el directorio de descarga. Sin esta opción, el archivo se guarda en ~/.cache/llama.cpp para la cuenta que ejecutó el comando, lo que no es adecuado para un servicio cuyo directorio de inicio pronto tendrá permisos de lectura restringidos. Ejecute ls -lh /srv/models después, porque el nombre del archivo en la caché se basa en el nombre del repositorio y no en el nombre de archivo simple.

Para un servicio, descargue el modelo en una ruta elegida por usted. Así, el archivo de unidad puede apuntar a una ubicación estable.

sudo -u llama curl -L --output-dir /srv/models -O \
  https://huggingface.co/ggml-org/gemma-3-1b-it-GGUF/resolve/main/gemma-3-1b-it-Q4_K_M.gguf

El espacio en disco es el primer límite que encuentran muchos usuarios. Estos son los tamaños de archivo publicados para dos modelos, comprobados el 18 de agosto de 2026.

ChartGGUF file size on disk, published figures, 18 August 2026
The data behind this chart
[
  {
    "label": "gemma-3-1b-it Q4_K_M",
    "size_gb": 0.81
  },
  {
    "label": "gemma-3-1b-it Q8_0",
    "size_gb": 1.07
  },
  {
    "label": "gemma-3-1b-it f16",
    "size_gb": 2.01
  },
  {
    "label": "gpt-oss-20b MXFP4",
    "size_gb": 12.11
  }
]

El archivo de 4 bits del modelo 1B ocupa 0.81 GB. El mismo modelo sin cuantización ocupa 2.01 GB, por lo que la elección del formato cambia el tamaño en más del doble. Un modelo 20B con MXFP4 ocupa 12.11 GB. No cabe en el disco de muchos planes básicos y, además, después debe cargarse en memoria.

Compruebe df -h antes de cada descarga. Si el sistema de archivos raíz se llena durante una transferencia de 12 GB, todos los demás procesos que necesiten escribir fallarán, incluido el journal.

Ejecutelo una vez manualmente y compruebelo

sudo -u llama /usr/local/bin/llama-server \
  --model /srv/models/gemma-3-1b-it-Q4_K_M.gguf \
  --host 127.0.0.1 --port 8080 \
  --ctx-size 4096 --parallel 1 --threads 2 --no-webui

En una segunda sesión, pregunte al servidor si está listo.

curl -s http://127.0.0.1:8080/health

Mientras se carga el archivo, recibirá HTTP 503 y este cuerpo:

{"error":{"code":503,"message":"Loading model","type":"unavailable_error"}}

Cuando esté listo, el cuerpo será {"status": "ok" }. Después, envíe una solicitud real.

curl -s http://127.0.0.1:8080/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"model":"local","messages":[{"role":"user","content":"Say hello in five words."}]}'

Un objeto JSON con un arreglo choices indica que el servidor funciona. El campo model existe porque los clientes de OpenAI siempre lo envían. Este servidor tiene un único modelo cargado, por lo que el valor no se utiliza para seleccionar nada.

La API compatible con OpenAI y qué más hay en el puerto

POST /v1/chat/completions, POST /v1/completions y POST /v1/embeddings son las rutas compatibles con OpenAI, y GET /v1/models informa del modelo cargado. GET /health es la comprobación de disponibilidad anterior, GET /props devuelve la configuración actual del servidor y GET /metrics expone contadores de Prometheus cuando se inicia con --metrics.

Cualquier SDK de OpenAI funciona si se establece la URL base en http://127.0.0.1:8080/v1 y se pasa una cadena de clave API no vacía. Esa clave no se comprueba hasta que se configura --api-key manualmente.

No tomes como referencia para tu propio plan las cifras de rendimiento de otras personas. La velocidad de inferencia en la CPU depende del número de núcleos, del ancho de banda de memoria y de los vecinos con los que compartas el host, así que mide los tokens por segundo en tu propio equipo y considera ese resultado como válido. El tiempo robado por un vecino ruidoso se manifiesta aquí como una velocidad de generación que cambia de una hora a otra.

Manténgalo en 127.0.0.1 y coloque un proxy delante

--host ya tiene 127.0.0.1 como valor predeterminado, por lo que el servidor no es accesible desde el exterior hasta que lo cambie. No lo modifique. llama-server no tiene modelo de usuarios, límite de tasa ni un registro de auditoría útil, y el único control integrado es --api-key, que compara una cadena. Un puerto de inferencia abierto proporciona capacidad de cómputo gratuita a cualquiera que lo encuentre, y el mismo error con Ollama tiene la misma estructura: proteger una API de modelos autoalojada se aplica aquí exactamente igual.

Termine TLS (seguridad de la capa de transporte) en nginx y haga proxy al puerto de loopback.

server {
    listen 443 ssl;
    server_name llm.example.com;

    location /v1/ {
        proxy_pass http://127.0.0.1:8080;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_buffering off;
        proxy_read_timeout 600s;
    }
}

proxy_buffering off es necesario para la transmisión en tiempo real. Con el almacenamiento en búfer activado, nginx retiene los eventos enviados por el servidor (SSE) hasta que termina la respuesta. El cliente permanece esperando en silencio y después recibe toda la respuesta de una vez. proxy_read_timeout 600s cubre las generaciones largas, porque el valor predeterminado de 60 segundos convierte una respuesta lenta en 504 Gateway Time-out. Obtenga el certificado con Certbot y Let's Encrypt en nginx.

La unidad de systemd

Escriba /etc/systemd/system/llama-server.service.

[Unit]
Description=llama.cpp server
After=network-online.target
Wants=network-online.target

[Service]
User=llama
Group=llama
Environment=LLAMA_ARG_MODEL=/srv/models/gemma-3-1b-it-Q4_K_M.gguf
Environment=LLAMA_ARG_HOST=127.0.0.1
Environment=LLAMA_ARG_PORT=8080
Environment=LLAMA_ARG_CTX_SIZE=4096
Environment=LLAMA_ARG_N_PARALLEL=1
Environment=LLAMA_ARG_THREADS=2
ExecStart=/usr/local/bin/llama-server --no-webui
Restart=on-failure
RestartSec=5
TimeoutStopSec=30
MemoryHigh=3G
MemoryMax=3500M
OOMPolicy=stop
NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=strict
ProtectHome=yes

[Install]
WantedBy=multi-user.target

La configuración se encuentra en las líneas de Environment= porque llama-server lee las variables LLAMA_ARG_* para la mayoría de las opciones, y un argumento de la línea de comandos reemplaza la variable correspondiente. Así, puede cambiar el tamaño del contexto en un solo lugar y mantener ExecStart lo bastante corto para leerlo de un vistazo.

ProtectSystem=strict hace que todo el sistema de archivos sea de solo lectura para esta unidad. Esto es correcto porque el servidor solo lee el modelo. Añada ReadWritePaths=/srv/models si quiere que el propio servicio descargue modelos con -hf. ProtectHome=yes oculta /home y /root. Este es el segundo motivo para mantener los modelos en /srv: con ProtectHome activado, la ruta predeterminada ~/.cache/llama.cpp no es visible para el proceso.

sudo systemctl daemon-reload
sudo systemctl enable --now llama-server
systemctl status llama-server
curl -s http://127.0.0.1:8080/health
journalctl -u llama-server -n 50 --no-pager

enable --now es la mitad que muchas personas omiten. Sin enable, el servidor desaparece después del siguiente reinicio. Si quiere programar tareas relacionadas con el servicio, como comprobar cada noche si existe una nueva versión, un servicio de systemd más un temporizador es el mecanismo adecuado.

Decida qué ocurre ante un OOM antes de que ocurra

El uso de memoria tiene dos componentes y se comportan de forma distinta bajo un límite. De forma predeterminada, el archivo del modelo se asigna en memoria, por lo que sus páginas están respaldadas por el archivo: el kernel puede descartarlas y volver a leerlas del disco. La caché KV, que es el estado por token que el servidor conserva para cada conversación activa, es memoria anónima. No se puede descartar, por lo que provoca la terminación del proceso.

Por eso, los dos límites de la unidad cumplen funciones distintas. MemoryHigh=3G es un límite flexible: cuando se supera, el kernel somete el cgroup a presión de recuperación, por lo que las páginas asignadas del modelo se expulsan y se vuelven a leer del disco con el siguiente token. El servicio sigue funcionando, pero se vuelve más lento. MemoryMax=3500M es un límite estricto: cuando se supera, el proceso termina y el journal lo indica claramente.

llama-server.service: A process of this unit has been killed by the OOM killer.

Establezca --ctx-size manualmente. El valor predeterminado es 0, que corresponde al contexto con el que se entrenó el modelo y, en un modelo moderno de contexto largo, asigna una caché KV muy grande durante el arranque. El servicio termina antes de atender una sola petición. --parallel multiplica el mismo coste, porque cada slot conserva su propio estado de conversación, así que déjelo en 1 hasta que necesite concurrencia.

Con Restart=on-failure, un servicio terminado vuelve a iniciarse. Si termina en cada arranque, systemd deja de intentarlo y systemctl status muestra start request repeated too quickly. Es el comportamiento correcto: un bucle de reinicios que vuelve a leer un archivo de 12 GB cada cinco segundos es peor que una interrupción del servicio. Corrija el límite o el tamaño del contexto y, después, borre el estado con sudo systemctl reset-failed llama-server.

Supervise el valor real con systemctl show llama-server -p MemoryCurrent mientras se ejecuta una petición. Limitar la memoria y la CPU de un proceso con systemd explica estas directivas con más detalle.

Evite usar swap con esta carga de trabajo. Un modelo expulsado a swap convierte cada token en lecturas de disco con desplazamientos aleatorios. La asignación en memoria del archivo del modelo consigue el mismo efecto con menos impacto, porque el kernel lee directamente del archivo las páginas que necesita.

Dónde Ollama es la mejor opción

Este es un punto de decisión. Elija llama-server cuando quiera un solo proceso con las opciones que haya definido, una compilación fijada y un archivo elegido por usted. Nada cambia por debajo porque no se ejecuta nada más.

Elija Ollama cuando necesite gestionar modelos: descargar modelos por nombre, mantener varios en el disco, descargar de la memoria uno que esté inactivo y actualizar con un solo comando en lugar de recompilar. Es trabajo real que, de otro modo, tendría que automatizar con scripts. Ejecutar Ollama en un VPS realiza el mismo trabajo, pero con la decisión tomada en el sentido contrario. Ambos exponen una API compatible con OpenAI, por lo que el código cliente sigue funcionando al cambiar de una opción a la otra.

Actualizar una compilación fijada

Sustituya bNNNNN por la etiqueta a la que va a cambiar.

cd llama.cpp
git fetch --tags
git checkout bNNNNN
cmake -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF -DLLAMA_BUILD_TESTS=OFF -DLLAMA_BUILD_EXAMPLES=OFF
cmake --build build --config Release -t llama-server -j 2
sudo install -m 755 build/bin/llama-server /usr/local/bin/llama-server-bNNNNN
sudo ln -sfn /usr/local/bin/llama-server-bNNNNN /usr/local/bin/llama-server
sudo systemctl restart llama-server

El binario anterior permanece en el disco, por lo que una reversión consiste en hacer un ln -sfn a llama-server-b10488 y reiniciar. Lea las notas de la versión antes de cambiar. Los archivos GGUF tienen versiones y los anteriores siguen cargándose, pero los flags cambian de nombre: --mlock y --no-mmap ya están obsoletos en favor de --load-mode, y una unidad que pase un flag eliminado falla al iniciar con un mensaje de argumento no reconocido.

Modos de fallo y mensajes que verá

error while loading shared libraries: libllama.so después de copiar el binario a otra ubicación. La compilación predeterminada genera bibliotecas compartidas junto al binario. Vuelva a compilar con -DBUILD_SHARED_LIBS=OFF o copie todo el directorio build/bin.

Illegal instruction (core dumped) al iniciar o durante la primera petición. El binario se compiló con GGML_NATIVE activado para una CPU distinta de la que lo ejecuta. Vuelva a compilar en este equipo o configure con -DGGML_NATIVE=OFF.

c++: fatal error: Killed signal terminated program cc1plus durante la compilación. El compilador terminó porque utilizó demasiada memoria. Reduzca -j o añada swap para la compilación y elimínela después.

curl: (7) Failed to connect ... Connection refused desde su portátil. Es correcto: el servidor escucha en la dirección de loopback del VPS. Haga la prueba en el propio VPS o abra un túnel con ssh -L 8080:127.0.0.1:8080 user@your-vps y use http://127.0.0.1:8080 localmente.

HTTP 503 con "message":"Loading model" durante los primeros segundos o minutos después de un reinicio. Leer un archivo de varios gigabytes requiere tiempo, y systemd informa de que la unidad está activa en cuanto se inicia el proceso, mucho antes de que el modelo esté en memoria.

Las peticiones se bloquean y después devuelven 504 Gateway Time-out. El proxy agotó el tiempo de espera antes de que terminara la carga del modelo. Aumente proxy_read_timeout y desactive proxy_buffering para que los tokens lleguen al cliente a medida que se generan.

La unidad entra en un ciclo de reinicios y después se detiene con start request repeated too quickly. Algo la termina en cada inicio. Compruebe journalctl -u llama-server para localizar la línea del OOM killer y, después, reduzca --ctx-size, reduzca --parallel o aumente MemoryMax.

FAQ

¿Debo ejecutar el servidor de llama.cpp o Ollama en mi VPS?

Ejecute llama-server cuando necesite fijar una compilación exacta, pasar flags exactos y mantener un modelo en un único archivo que nada actualice sin su conocimiento. Use Ollama cuando necesite gestionar modelos y actualizarlos con un solo comando, porque descargar modelos por nombre, conservar varios en disco y descargar de memoria los que están inactivos son tareas que, de otro modo, tendría que automatizar con scripts. Ambos ofrecen una API compatible con OpenAI, por lo que el código del cliente no cambia si cambia de uno a otro más adelante.

¿Qué versión de llama.cpp debo fijar?

Cualquier tag que haya compilado y probado realmente. llama.cpp crea tags para casi cada merge y los nombres son números de compilación, como b10488, que era el más reciente el 18 August 2026. No existe una rama estable independiente, por lo que la versión "current" cambia varias veces al día. Clone con --branch <tag>, instale el binario con un nombre que contenga ese tag y apunte un enlace simbólico a él, de modo que la actualización y la reversión requieran un solo comando cada una.

¿Cuánta RAM necesita llama-server?

Parta del tamaño del archivo GGUF y añada la caché KV, que crece con --ctx-size y con el número de slots --parallel. Las cifras publicadas no sustituyen las mediciones de su propia configuración, porque el total depende del modelo, la cuantización y el contexto permitido. Ejecute systemctl show llama-server -p MemoryCurrent mientras haya una petición en curso y utilice el número que aparezca.

¿Por qué /health devuelve 503 con "Loading model"?

El proceso se ha iniciado, pero el archivo del modelo todavía no está en memoria, por lo que el servidor responde {"error":{"code":503,"message":"Loading model","type":"unavailable_error"}}. Esto es normal después de cada reinicio y dura tanto como tarde en leerse el archivo. Sólo se convierte en un problema cuando un cliente o un proxy trata ese primer 503 como un fallo definitivo. Consulte /health periódicamente hasta que devuelva {"status": "ok" }.

¿Puedo exponer llama-server directamente a Internet?

No lo vincule a 0.0.0.0 ni abra el puerto. No tiene cuentas, limitación de tasa ni un registro de peticiones adecuado para auditoría, y la única comprobación integrada es --api-key, que compara una sola cadena. Mantenga el bind predeterminado 127.0.0.1, coloque nginx delante con TLS y establezca también --api-key, para que un error en la configuración del proxy no deje el modelo abierto a cualquiera.