LocalAI en un servidor CPU: LLM sin tarjeta gráfica
TL;DR
LocalAI permite ejecutar un modelo de lenguaje local con una API compatible con OpenAI en un VPS o servidor dedicado sin GPU. En esta guía se configurará un LocalAI protegido en Ubuntu 24.04 LTS con Docker, el backend CPU llama.cpp, un modelo en formato GGUF, HTTPS mediante Caddy, una clave API y copias de seguridad.
- Para la primera puesta en marcha funcional, bastan 4 vCPU, 8 GB de RAM y 40–60 GB de SSD.
- Para trabajar cómodamente con modelos 7B–8B en cuantización Q4, es mejor usar 8 vCPU y 16–32 GB de RAM.
- LocalAI proporciona una interfaz compatible con la API de OpenAI:
/v1/chat/completions,/v1/modelsy otros endpoints. - En CPU, la velocidad de un núcleo y la capacidad de RAM son más importantes que el número nominal de vCPU débiles.
- Los modelos GGUF pueden almacenarse localmente: las solicitudes y los documentos no se envían a servicios externos de IA.
- Para el acceso público son obligatorios TLS, firewall, una clave API y la restricción de acceso por IP o reverse proxy.
Qué configuramos y por qué
LocalAI es un servicio de IA self-hosted que ejecuta modelos de lenguaje, modelos para generar embeddings, speech-to-text y algunas otras tareas en su propio servidor. Para LLM en CPU normalmente se utiliza el backend llama.cpp y modelos en formato GGUF. Están cuantizados: ocupan menos memoria que los pesos originales en FP16 o BF16 y pueden funcionar sin una GPU de NVIDIA.
El resultado práctico de esta guía es un servicio API disponible mediante HTTPS en su propio dominio. Podrán conectarse a él aplicaciones que sean compatibles con la API de OpenAI: un chat interno para el equipo, un bot en Telegram o Mattermost, un asistente de IDE, n8n, Dify, LibreChat, Open WebUI, su propio SaaS o backend en Python, Node.js y Go.
En el servidor funcionarán cuatro capas: Docker Compose administra el contenedor, LocalAI proporciona la API, el modelo GGUF responde a las solicitudes y Caddy recibe el tráfico HTTPS y lo proxifica internamente. LocalAI no se publicará directamente en Internet: el puerto 8080 permanecerá disponible solo en localhost.
Cuándo se justifica LocalAI en CPU
- Se necesita una API autónoma para un equipo pequeño, un bot o herramientas internas.
- Los datos no pueden o no conviene enviarlos a proveedores de LLM en la nube.
- La carga es moderada: desde varias decenas hasta cientos de solicitudes al día.
- Es aceptable una latencia de respuesta de varios segundos, en lugar de la velocidad de streaming de un servicio GPU.
- Se necesita un presupuesto mensual predecible sin pago por tokens.
- Desea experimentar con modelos, prompts de sistema y parámetros de inference.
Lo que un servidor CPU no resuelve
La inferencia en CPU no sustituye a una GPU para alta concurrencia. En un servidor con 8 vCPU modernas, un modelo 8B en Q4 suele generar aproximadamente 4–12 tokens por segundo, pero la cifra real depende de la generación de CPU, la frecuencia, AVX2/AVX-512, el tamaño del contexto y la carga de procesos vecinos. Para un único usuario interactivo, esto suele ser suficiente. Para decenas de diálogos simultáneos, ya no.
No debe esperarse un buen resultado de modelos grandes de 30B, 70B o superiores en un VPS convencional. Incluso si el modelo cabe en memoria, el tiempo hasta el primer token y la latencia total harán que el servicio sea incómodo. Para estos modelos se necesita un servidor con gran cantidad de RAM, CPU potentes o infraestructura GPU.
API en la nube y LocalAI self-hosted
| Criterio | API managed en la nube | LocalAI en su propio servidor |
|---|---|---|
| Puesta en marcha | Prácticamente instantánea | Se necesitan servidor, Docker, modelo y protección de la API |
| Calidad de los modelos más grandes | Normalmente superior | Depende del modelo local seleccionado |
| Confidencialidad | Los datos se envían a un proveedor externo | Los datos permanecen en su infraestructura |
| Coste | Pago por tokens o suscripción | Coste fijo del servidor y almacenamiento |
| Velocidad con carga elevada | Alta; la infraestructura se escala automáticamente | Limitada por CPU, RAM y la configuración de la cola |
| Control sobre el modelo | Limitado a los modelos disponibles | Control total sobre el modelo, las plantillas y las versiones |
Un esquema racional para un producto pequeño es utilizar LocalAI para tareas donde importan la privacidad, el bajo coste y la previsibilidad: clasificación, extracción de datos, resumen de textos internos, RAG sobre documentación y asistencia a desarrolladores. La API externa en la nube puede mantenerse como respaldo para solicitudes complejas o picos de carga.
Qué configuración de VPS se necesita para esta tarea
El principal recurso para LocalAI en CPU es la memoria RAM. La segunda característica más importante es el rendimiento del núcleo del procesador. El disco es importante para almacenar modelos: un modelo GGUF 3B en Q4 suele ocupar 2–3 GB, un modelo 8B en Q4 aproximadamente 4,5–6 GB, y varias variantes de modelos y copias de seguridad llenan rápidamente un SSD pequeño.
| Escenario | CPU | RAM | SSD/NVMe | Modelos recomendados |
|---|---|---|---|---|
| Pruebas, un usuario | 4 vCPU | 8 GB | 50 GB | 1B–4B, Q4 |
| Chat, bot, RAG para un equipo | 8 vCPU | 16 GB | 100 GB NVMe | 7B–8B, Q4_K_M |
| Varios usuarios y modelos | 12–16 vCPU | 32–64 GB | 200 GB NVMe | 8B–14B, embeddings, reranker |
| Modelos grandes en CPU | 16+ núcleos dedicados | 64–128 GB | 500 GB NVMe | 14B–32B con un compromiso de velocidad |
Configuración inicial práctica
Para la primera instalación de producción, elija 8 vCPU, 16 GB de RAM, 100 GB NVMe y una conexión desde 1 Gbit/s. Este servidor permitirá ejecutar un modelo principal del nivel de Llama 3.1/3.2 8B, Qwen2.5 7B o Mistral 7B en cuantización GGUF Q4, dejar margen para el sistema operativo y Caddy, y almacenar varios archivos de modelos. Como una de las opciones neutrales, puede elegir un VPS con las características indicadas.
Al elegir un plan, confirme si las vCPU están garantizadas, qué generación de CPU se utiliza y si el procesador cuenta con AVX2. Para llama.cpp, AVX2 influye notablemente en el rendimiento. A veces, 8 núcleos dedicados rápidos dan un resultado más útil que 16 núcleos virtuales sobrecargados.
Estimación de memoria para GGUF
No se debe calcular la RAM solo por el tamaño del archivo GGUF. Además de los pesos del modelo, se necesita memoria para la KV cache, los búferes de inference, el contenedor, el kernel de Linux y la caché de archivos. Una regla práctica segura: para un modelo 8B Q4 de 5 GB, asigne un servidor con al menos 12–16 GB de RAM. Para un 14B Q4 de 9–11 GB se necesitan al menos 24–32 GB de RAM.
El contexto también consume memoria. Si aumenta context_size de 4096 a 16384 tokens, el consumo de RAM puede crecer varios gigabytes. No establezca el contexto máximo «por si acaso»: para un chat habitual y RAG, en la mayoría de los casos bastan 4096–8192 tokens.
Cuándo se necesita un dedicado en lugar de un VPS
Un servidor dedicado es necesario cuando importan una velocidad de generación alta y estable, la garantía de ausencia de noisy neighbors, 32–128 GB de RAM, modelos grandes o solicitudes paralelas constantes. También es la elección correcta si LocalAI atiende un producto comercial donde la latencia influye directamente en la conversión.
Un VPS sigue siendo una buena opción para un prototipo, un servicio interno, un asistente personal y un equipo pequeño. Comience con un VPS, mida la velocidad real mediante la API y pase a un dedicado solo cuando aparezca un cuello de botella confirmado: falta de RAM, saturación de CPU o crecimiento de la cola de solicitudes.
Cómo influye la ubicación del servidor
La ubicación no acelera el cálculo de tokens, pero influye en la latencia de red hasta los usuarios. Para un chat, es recomendable alojar el servidor en una región cercana a la mayoría de los clientes. Una diferencia de 50–100 ms se nota especialmente en las respuestas en streaming, aunque la latencia principal en CPU seguirá estando relacionada con la generación del modelo.
Si LocalAI procesa datos personales, documentos de la empresa o información médica, tenga también en cuenta los requisitos de jurisdicción, almacenamiento de datos y transferencia transfronteriza. Aloje las copias de seguridad separadas del servidor principal, preferiblemente en otro centro de datos.
Preparación del servidor
A continuación se utiliza Ubuntu Server 24.04 LTS. En el momento de la configuración, es una base LTS estable con un largo período de soporte. Inicie sesión en el servidor como usuario root solo para la preparación inicial; después cree un administrador independiente y desactive el inicio de sesión de root por SSH.
Actualización del sistema y paquetes básicos
El comando instala las actualizaciones de seguridad actuales, herramientas de diagnóstico, firewall y fail2ban.
apt update && apt full-upgrade -y
apt install -y ca-certificates curl gnupg jq nano vim \
ufw fail2ban unattended-upgrades \
htop iotop ncdu rsync
Reinicie el servidor si se actualizó el núcleo. Esto aplicará el nuevo kernel y evitará que un núcleo vulnerable siga ejecutándose en memoria.
if [ -f /var/run/reboot-required ]; then reboot; fi
Creación del administrador
Sustituya deploy por su propio nombre de usuario. La cuenta se utilizará para Docker Compose y el mantenimiento posterior de LocalAI.
adduser deploy
usermod -aG sudo deploy
Copie su clave pública SSH al servidor desde el equipo local. Ejecute este comando en su equipo, no en el VPS.
ssh-copy-id deploy@SERVER_IP
Compruebe el inicio de sesión en una terminal independiente. No cierre la sesión actual de root hasta asegurarse de que la autenticación mediante clave funciona.
ssh deploy@SERVER_IP
Protección de SSH
Abra la configuración de SSH y prohíba el inicio de sesión como root y la autenticación por contraseña. Antes de ello, asegúrese de que el usuario deploy dispone de una clave funcional.
sudo nano /etc/ssh/sshd_config.d/99-hardening.conf
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
X11Forwarding no
MaxAuthTries 3
LoginGraceTime 30
Compruebe la sintaxis y reinicie SSH. Si el comando sshd -t no muestra errores, la configuración es correcta.
sudo sshd -t && sudo systemctl restart ssh
Firewall y fail2ban
Abra únicamente SSH, HTTP y HTTPS. No abra el puerto de LocalAI 8080: después de la configuración, solo escuchará en 127.0.0.1.
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status verbose
Active fail2ban. Ubuntu proporciona un filtro listo para OpenSSH; el servicio bloqueará temporalmente una IP tras una serie de intentos de inicio de sesión fallidos.
sudo systemctl enable --now fail2ban
sudo fail2ban-client status sshd
Comprobación de recursos antes de la instalación
Antes de descargar el modelo, compruebe la RAM disponible, el tamaño del disco y las características del procesador. Esto ayudará a elegir de antemano la cuantización y el número de hilos adecuados.
free -h
df -h /
lscpu | egrep 'Model name|CPU\(s\)|Thread|AVX|Flags'
nproc
Si la salida de lscpu no incluye AVX2, LocalAI aún puede funcionar, pero el rendimiento en CPU puede ser notablemente inferior. En ese caso, es más razonable utilizar un modelo compacto de 3B–4B y no intentar atender varios chats simultáneos.
Instalación del software — paso a paso
Esta configuración utiliza Docker Engine 28+ y Docker Compose v2, Ubuntu 24.04 LTS, Caddy 2.8+ y la rama estable actual de LocalAI 3.x. Para producción, no deje la etiqueta latest de forma permanente: tras realizar pruebas satisfactorias, fije una etiqueta específica o el digest de la imagen en compose.yaml.
Instalación de Docker Engine
Elimine los paquetes antiguos de Docker si están presentes. Esto evita conflictos entre los paquetes de Ubuntu y el Docker Engine oficial.
sudo apt remove -y docker.io docker-compose docker-compose-v2 \
docker-doc podman-docker containerd runc 2>/dev/null || true
Añada la clave oficial del repositorio de Docker y conecte el repositorio para Ubuntu 24.04.
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
Instale Docker Engine, containerd y el plugin de Compose. Compose iniciará LocalAI y ejecutará el healthcheck.
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io \
docker-buildx-plugin docker-compose-plugin
Permita al usuario deploy administrar Docker sin sudo. Tras ejecutar el comando, cierre la sesión SSH y vuelva a iniciar sesión para que se aplique el grupo.
sudo usermod -aG docker deploy
exit
Tras volver a iniciar sesión, asegúrese de que Docker funciona. El contenedor hello-world debe finalizar sin errores.
docker version
docker compose version
docker run --rm hello-world
Creación de la estructura del proyecto
Todos los archivos de LocalAI estarán en /opt/localai. El directorio models almacena archivos GGUF y configuraciones YAML de modelos, mientras que data conserva los datos de LocalAI entre reinicios.
sudo mkdir -p /opt/localai/{models,data,backups}
sudo chown -R deploy:deploy /opt/localai
cd /opt/localai
Descarga del primer modelo
Para la primera ejecución en CPU, utilice el modelo instructivo Qwen2.5-3B-Instruct con cuantización Q4_K_M. Es más compacto que los modelos 7B–8B, funciona correctamente con 8 GB de RAM y es adecuado para comprobar la API. Antes de usarlo en producción, revise la licencia del modelo concreto y las condiciones de su distribución.
El siguiente comando descarga el GGUF en el directorio de modelos. La URL se indica como ejemplo de repositorio público; antes de descargar, compruebe el nombre actual del archivo y el hash en la página de lanzamiento del modelo.
cd /opt/localai/models
curl -L --fail --retry 3 \
-o Qwen2.5-3B-Instruct-Q4_K_M.gguf \
"https://huggingface.co/bartowski/Qwen2.5-3B-Instruct-GGUF/resolve/main/Qwen2.5-3B-Instruct-Q4_K_M.gguf"
Compruebe que el archivo se descargó realmente y no tiene tamaño cero. Para un modelo 3B Q4, espere un tamaño de unos 2–3 GB.
ls -lh /opt/localai/models
sha256sum /opt/localai/models/Qwen2.5-3B-Instruct-Q4_K_M.gguf
Creación de secretos
No almacene la clave API en el archivo compose ni en el código fuente de la aplicación. Genere un valor largo y guárdelo en el archivo .env, accesible solo para el usuario deploy.
cd /opt/localai
umask 077
printf 'LOCALAI_API_KEY=%s\n' "$(openssl rand -hex 32)" > .env
chmod 600 .env
cat .env
Guarde esta clave en un gestor de secretos. Las aplicaciones cliente la necesitarán en la cabecera Authorization: Bearer.
Configuración de LocalAI, modelos y HTTPS
Descripción del modelo
Cree un archivo de configuración YAML. El parámetro threads establece el número de hilos de CPU para inference. En un servidor con 8 vCPU, comience con 6: quedarán recursos para el SO, el reverse proxy y el procesamiento de red. El valor context_size 4096 es un punto de partida seguro para un servidor con CPU.
nano /opt/localai/models/qwen-cpu.yaml
name: qwen2.5-3b-cpu
backend: llama-cpp
parameters:
model: Qwen2.5-3B-Instruct-Q4_K_M.gguf
context_size: 4096
threads: 6
temperature: 0.7
top_p: 0.9
max_tokens: 512
template:
chat: |
{{if .System}}<|im_start|>system
{{.System}}<|im_end|>
{{end}}{{range .Messages}}<|im_start|>{{.Role}}
{{.Content}}<|im_end|>
{{end}}<|im_start|>assistant
{{.Assistant}}
La plantilla de chat es importante: transforma los mensajes de la API de OpenAI al formato con el que se entrenó el modelo Qwen. Si las respuestas de repente contienen tokens de servicio, repiten el prompt o ignoran los roles, compruebe primero la chat template.
Docker Compose para inferencia con CPU
Cree compose.yaml. La imagen localai/localai:latest-aio-cpu está diseñada para CPU e incluye los componentes necesarios para una ejecución típica. Tras confirmar una versión funcional, reemplace latest-aio-cpu por una version tag específica de las release notes oficiales.
nano /opt/localai/compose.yaml
services:
localai:
image: localai/localai:latest-aio-cpu
container_name: localai
restart: unless-stopped
env_file:
- .env
environment:
- DEBUG=false
- MODELS_PATH=/models
- THREADS=6
- CONTEXT_SIZE=4096
volumes:
- ./models:/models
- ./data:/data
ports:
- "127.0.0.1:8080:8080"
healthcheck:
test: ["CMD-SHELL", "curl -fsS http://localhost:8080/readyz || curl -fsS http://localhost:8080/v1/models"]
interval: 30s
timeout: 10s
retries: 5
start_period: 120s
deploy:
resources:
limits:
memory: 12G
Un límite de memoria de 12 GB es adecuado para un servidor con 16 GB de RAM y un modelo 3B. En un VPS con 8 GB de RAM, establezca memory: 6G y utilice un modelo más compacto. En un servidor con 32 GB de RAM, para un modelo 8B puede aumentar el límite a 20–24 GB.
Inicie el contenedor en segundo plano y consulte los registros. La primera carga puede tardar más, ya que LocalAI inicializa el backend y lee el archivo del modelo desde el disco.
cd /opt/localai
docker compose pull
docker compose up -d
docker compose ps
docker compose logs -f --tail=100
No publique el puerto local 8080 a través del firewall. La comprobación siguiente se realiza directamente en el servidor y muestra que la API detecta el modelo configurado.
curl -s http://127.0.0.1:8080/v1/models | jq .
Comprobación local de chat completion
Sustituya el valor de la clave mediante un comando que la lea desde .env. El parámetro stream está desactivado para un diagnóstico sencillo; después de iniciar la aplicación, puede activarlo para entregar tokens gradualmente.
cd /opt/localai
source .env
curl -sS http://127.0.0.1:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${LOCALAI_API_KEY}" \
-d '{
"model": "qwen2.5-3b-cpu",
"messages": [
{
"role": "system",
"content": "Ты краткий технический помощник."
},
{
"role": "user",
"content": "Объясни в одном предложении, что такое reverse proxy."
}
],
"temperature": 0.2,
"max_tokens": 100,
"stream": false
}' | jq .
Si la API responde con un objeto JSON que contiene choices, la configuración básica está completada. Si LocalAI devuelve 401, compruebe el nombre de la variable y la compatibilidad con la clave API de la versión de imagen utilizada. En algunas compilaciones de LocalAI, la clave se establece mediante una variable de entorno independiente o resulta más conveniente implementar la autenticación a nivel de Caddy.
Configuración de Caddy y TLS
Para HTTPS se necesita un dominio cuyo registro A apunte a la IP del servidor. Antes de iniciar Caddy, compruebe el DNS: el dominio debe devolver la dirección IPv4 pública del VPS. Los puertos 80 y 443 deben ser accesibles desde el exterior.
Instale Caddy desde el repositorio oficial. Obtiene y renueva automáticamente los certificados de Let’s Encrypt si el DNS y el firewall están configurados correctamente.
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | \
sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | \
sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install -y caddy
Cree una contraseña adicional para basic authentication. Es una segunda barrera ante la API: el cliente necesitará tanto Basic Auth como un Bearer token. Inserte en el Caddyfile el hash que devuelva el comando.
caddy hash-password --plaintext 'СЮДА_ДЛИННЫЙ_ОТДЕЛЬНЫЙ_ПАРОЛЬ'
Abra la configuración de Caddy. Sustituya el dominio, el nombre de usuario y el hash. Si es necesario, en lugar de basic auth puede limitar el acceso mediante una red VPN WireGuard o direcciones IP de la oficina.
sudo nano /etc/caddy/Caddyfile
llm.example.com {
encode zstd gzip
@not_api not path /v1/ /readyz /healthz
respond @not_api "Not found" 404
basicauth {
apiadmin $2a$14$REPLACE_WITH_CADDY_PASSWORD_HASH
}
reverse_proxy 127.0.0.1:8080 {
header_up Host {host}
header_up X-Real-IP {remote_host}
header_up X-Forwarded-For {remote_host}
transport http {
read_timeout 300s
write_timeout 300s
}
}
log {
output file /var/log/caddy/localai-access.log
format json
}
}
Compruebe el archivo antes de aplicarlo. A continuación, recargue Caddy y asegúrese de que el certificado se haya emitido.
sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy
sudo systemctl status caddy --no-pager
sudo journalctl -u caddy -n 50 --no-pager
Comprobación externa de la API HTTPS
Ejecute el comando desde el equipo de trabajo. Comprueba TLS, basic authentication, el Bearer token y la disponibilidad de la lista de modelos a través del dominio público.
curl -sS https://llm.example.com/v1/models \
-u 'apiadmin:ВАШ_ОТДЕЛЬНЫЙ_ПАРОЛЬ' \
-H 'Authorization: Bearer ВАШ_LOCALAI_API_KEY' | jq .
Para el cliente Python, utilice el SDK habitual de OpenAI indicando su propio base_url. Es mejor pasar la contraseña de basic auth mediante un reverse proxy protegido o una VPN; para aplicaciones API resulta más conveniente dejar solo la autenticación Bearer y restringir las direcciones IP en Caddy.
from openai import OpenAI
client = OpenAI(
base_url="https://llm.example.com/v1",
api_key="ВАШ_LOCALAI_API_KEY",
)
response = client.chat.completions.create(
model="qwen2.5-3b-cpu",
messages=[
{"role": "user", "content": "Напиши короткое приветствие."}
],
temperature=0.4,
)
print(response.choices[0].message.content)
Control del rendimiento
Durante una solicitud de prueba, ejecute htop en una ventana SSH independiente. Los procesos de inference deben utilizar aproximadamente el número de núcleos establecido por el parámetro threads. Si el servidor empieza a utilizar swap, reduzca el contexto, elija una cuantización menor o aumente la RAM.
htop
free -h
docker stats localai
docker compose -f /opt/localai/compose.yaml logs --tail=100 localai
Copias de seguridad y mantenimiento
LocalAI normalmente no contiene una base de datos crítica si se utiliza solo como API de inference. Sin embargo, debe conservar la configuración, los archivos de modelos, las variables de entorno, el Caddyfile, los datos de las aplicaciones cliente y, si aparecen, la base de datos vectorial RAG. No considere la Docker image como una copia de seguridad: la imagen se puede descargar de nuevo, pero las configuraciones y los datos no.
Qué incluir en la copia de seguridad
/opt/localai/compose.yaml— versión del servicio y configuración del contenedor./opt/localai/models/.yaml— parámetros y chat templates de los modelos./opt/localai/.env— claves API; almacenar solo en un backup cifrado./opt/localai/data— datos persistentes de LocalAI, si se utilizan./etc/caddy/Caddyfile— reverse proxy y reglas de acceso.- Directorio de modelos GGUF — según el caso: los archivos son grandes, pero volver a descargarlos puede llevar mucho tiempo.
- Datos de la pila RAG: PostgreSQL, Qdrant, pgvector, Chroma u otro vector store.
Realice una copia diaria de la configuración. Puede hacer backup de los modelos semanalmente o no hacer copia alguna si conserva una lista verificada de URL, versiones y SHA256. Para un servicio de production, es más prudente contar con al menos una copia remota de GGUF: el repositorio público puede cambiar su estructura, eliminar el archivo o restringir el acceso.
Copia de seguridad mediante restic
Restic cifra el archivo antes de enviarlo a un almacenamiento compatible con S3. Instale el paquete y cree un archivo de secretos independiente. No añada este archivo a Git ni lo envíe por mensajería.
sudo apt install -y restic
sudo install -d -m 700 /root/.config/restic
sudo nano /root/.config/restic/localai.env
export RESTIC_REPOSITORY="s3:https://s3.example.net/localai-backups"
export RESTIC_PASSWORD="ДЛИННЫЙ_СЛУЧАЙНЫЙ_ПАРОЛЬ_РЕПОЗИТОРИЯ"
export AWS_ACCESS_KEY_ID="S3_ACCESS_KEY"
export AWS_SECRET_ACCESS_KEY="S3_SECRET_KEY"
Restrinja el acceso al archivo e inicialice el repositorio remoto. Esta operación se realiza una sola vez.
sudo chmod 600 /root/.config/restic/localai.env
sudo bash -c 'source /root/.config/restic/localai.env && restic init'
Cree un script. En el ejemplo, los modelos se excluyen del backup diario; añada el directorio /opt/localai/models si también desea copiar los archivos GGUF. El script también elimina snapshots demasiado antiguos según la política de retención.
sudo nano /usr/local/sbin/backup-localai.sh
#!/usr/bin/env bash
set -euo pipefail
source /root/.config/restic/localai.env
restic backup \
/opt/localai/compose.yaml \
/opt/localai/.env \
/opt/localai/data \
/opt/localai/models \
/etc/caddy/Caddyfile \
--exclude='.gguf' \
--tag localai
restic forget \
--keep-daily 7 \
--keep-weekly 4 \
--keep-monthly 6 \
--prune
Haga ejecutable el script, ejecútelo manualmente y compruebe la lista de snapshots. El primer backup confirmará que el acceso a S3, el cifrado y los permisos están configurados correctamente.
sudo chmod 700 /usr/local/sbin/backup-localai.sh
sudo /usr/local/sbin/backup-localai.sh
sudo bash -c 'source /root/.config/restic/localai.env && restic snapshots'
Añada una ejecución diaria mediante cron a las 03:30. Los registros se guardan en un archivo independiente, que conviene revisar al menos una vez por semana.
sudo crontab -e
30 3 /usr/local/sbin/backup-localai.sh >> /var/log/localai-backup.log 2>&1
Comprobación de restauración
Un backup sin una prueba de restauración es solo una suposición. Una vez al mes, restaure un snapshot en un directorio temporal de otro servidor o de una máquina local, compruebe las configuraciones YAML y asegúrese de que .env esté presente.
sudo mkdir -p /tmp/localai-restore
sudo bash -c 'source /root/.config/restic/localai.env && \
restic restore latest --target /tmp/localai-restore'
sudo find /tmp/localai-restore -maxdepth 4 -type f | sort
Actualizaciones seguras
No actualice LocalAI, Docker, Caddy y el modelo al mismo tiempo. De lo contrario, si ocurre un error será difícil determinar su causa. Para un servicio pequeño, utilice una maintenance window: avise a los usuarios, haga un backup, actualice un componente, realice una smoke test y solo entonces continúe.
La actualización de LocalAI es así: primero guarde el image digest actual, después descargue la nueva imagen, vuelva a crear el contenedor y compruebe la API. Si el modelo o la API dejan de funcionar, restaure la etiqueta de imagen anterior en compose.yaml y vuelva a ejecutar up -d.
cd /opt/localai
docker inspect localai --format='{{.Image}}'
docker compose pull
docker compose up -d
docker compose ps
curl -fsS http://127.0.0.1:8080/v1/models | jq .
Para una actualización rolling sin tiempo de inactividad, necesitará al menos dos instancias de backend, un balanceador y suficiente margen de RAM. En un único VPS con CPU suele ser más seguro programar una breve ventana de mantenimiento: la carga de un solo modelo ya consume una parte considerable de la memoria.
Resolución de problemas y FAQ
¿Por qué LocalAI no ve el modelo en /v1/models?
Primero compruebe la ruta de montaje: el directorio del host /opt/localai/models debe estar conectado en el contenedor como /models. Luego revise los logs: docker compose logs --tail=200 localai. Una causa frecuente es un YAML incorrecto, que el nombre del archivo GGUF no coincida con el parámetro model o permisos incorrectos en el directorio. Asegúrese también de que el archivo de configuración tenga la extensión .yaml y esté junto al modelo.
¿Por qué la respuesta se genera muy lentamente?
Compruebe la carga mediante htop y docker stats localai. En CPU, una velocidad de varios tokens por segundo es normal, especialmente para modelos 7B–8B. Reduzca context_size a 4096, establezca threads aproximadamente en el número de núcleos físicos o virtuales disponibles menos uno o dos, use Q4_K_M en lugar de Q5/Q6 y elija un modelo 3B–4B. Si la CPU está constantemente al 100% y aumenta el número de usuarios, necesitará un servidor más potente o una GPU.
El contenedor se reinicia y los logs muestran out of memory. ¿Qué hacer?
Esto significa que no hubo suficiente RAM para los modelos, la KV cache y los procesos del sistema. Ejecute free -h y compruebe si se utilizó swap. Reduzca el tamaño del contexto, el número de modelos cargados simultáneamente y la cuantización. Por ejemplo, sustituya 8B Q5 por 8B Q4 o 3B Q4. No intente resolver el problema solo con más swap: la inferencia será extremadamente lenta. Para un modelo 8B Q4, el mínimo práctico es 16 GB de RAM.
¿Por qué no se emite el certificado HTTPS de Caddy?
Compruebe el registro A del dominio con el comando dig +short llm.example.com: debe apuntar a la IP del servidor. Asegúrese de que los puertos 80 y 443 estén abiertos en UFW y en el panel del proveedor, si dispone de un firewall independiente. Consulte sudo journalctl -u caddy -n 100. Las causas frecuentes son: DNS aún no se ha actualizado, el dominio se proxifica mediante una CDN externa con un modo TLS inadecuado o el puerto 80 está ocupado por otro servidor web.
La API devuelve 401 Unauthorized. ¿Dónde buscar el error?
Divida la comprobación por niveles. Primero realice una solicitud a 127.0.0.1:8080 sin Caddy. Luego compruebe Basic Auth mediante curl -u y después el Bearer token. Asegúrese de que el valor en .env no contenga espacios adicionales y de que el contenedor se haya reiniciado tras modificar el archivo: docker compose up -d --force-recreate. No pase la clave en la URL ni la guarde en el shell history.
¿Qué configuración mínima de VPS es adecuada?
Para familiarizarse con LocalAI, bastará como mínimo un VPS con 4 vCPU, 8 GB de RAM y 50 GB de SSD. En él debe ejecutar un modelo pequeño 1B–4B con cuantización Q4, un contexto de 2048–4096 y un diálogo activo. Para un modelo 7B–8B, esta configuración ya es límite: el servicio puede funcionar, pero será lento o tendrá falta de memoria. Para un funcionamiento estable con 8B, elija 8 vCPU y 16 GB de RAM.
¿Qué elegir para esta tarea: VPS o dedicated?
Un VPS es adecuado para un asistente personal, un prototipo, RAG sobre documentos internos y un equipo con varios usuarios. Conviene elegir dedicated con carga constante de CPU, requisitos de tiempos de respuesta estables, uso de modelos 14B o mayores, 32–64 GB de RAM o paralelismo considerable. Un enfoque práctico es comenzar con un VPS, recopilar métricas de velocidad, memoria y colas, y después migrar a un servidor dedicado solo cuando exista una necesidad confirmada.
¿Cómo añadir un segundo modelo sin detener el primero?
Copie el segundo archivo GGUF en /opt/localai/models, cree un YAML independiente para él con un name único y reinicie el contenedor. Tenga en cuenta que varios modelos cargados simultáneamente suman su consumo de RAM. En un servidor de 16 GB es razonable mantener un modelo 8B o varios modelos compactos. Si el modelo se necesita rara vez, considere una instancia independiente de LocalAI o un mecanismo para descargar modelos no utilizados, si su versión lo admite.
¿Por qué el modelo responde de forma incoherente, repite texto o ignora el prompt del sistema?
Normalmente el problema está en un chat template incompatible, no en la CPU. Cada modelo instruct tiene su propio formato de roles y tokens especiales. Consulte la model card oficial y compare la plantilla recomendada con el archivo YAML de LocalAI. Reduzca también temperature a 0.2–0.5, asegúrese de haber elegido la versión instruct del modelo y no mezcle la plantilla de Qwen con Llama o Mistral. Para el diagnóstico, envíe una solicitud breve sin un historial largo.
Conclusiones y próximos pasos
Ahora el servidor ejecuta LocalAI con inferencia en CPU, un modelo GGUF local, una API compatible con OpenAI, HTTPS y protección básica. Esta pila es adecuada para herramientas de AI privadas y carga moderada sin comprar ni alquilar una GPU.
- Mida la velocidad de generación, el uso de RAM y las latencias reales en solicitudes típicas de su tarea.
- Añada RAG: un modelo de embeddings, vector store y control de acceso a los documentos.
- Cuando aumente la carga, migre a CPU más rápidas, un servidor dedicated, varias instancias detrás de un reverse proxy o inferencia con GPU.
Antes de implementar en production, fije las versiones de la Docker image y los modelos, documente los SHA256 de los archivos GGUF, compruebe periódicamente la restauración de backups y no publique la API sin autenticación.