Todas las contribuciones
Ingenieríaself-hostedqdrantlangfuse

Stack de IA self-hosted en un droplet de Digital Ocean de $40: Qdrant + Langfuse + LiteLLM + Redis

Docker Compose productivo con Qdrant, Langfuse, LiteLLM Proxy, Redis 8 y Ollama en un droplet de $40. Nginx + Certbot, backups a Spaces, monitoreo con Grafana y Prometheus. Todo el stack corriendo en un solo servidor.

Numoru EngineeringPublicado el 26 de abril de 202618 min de lectura
Compartir
Propuesta de implementacióngithub.com/numoru-ia/ai-stack-do

TL;DR

Un droplet de Digital Ocean de 40 USD al mes (4 vCPU, 8 GB RAM, 160 GB SSD) alcanza para correr un stack de IA completo y productivo: Qdrant como vector DB, Langfuse para observabilidad de LLMs, LiteLLM Proxy como gateway unificado, Redis 8 para caché semántico y memoria de agentes, Ollama para modelos locales, Nginx + Certbot para TLS, Prometheus + Grafana para métricas y Restic para backups a Spaces. En este artículo publicamos el docker-compose.yml completo, los perfiles de recursos por contenedor, la configuración de red interna y el plan de restauración ante caída del droplet.

Si tu cliente no puede o no quiere mandar datos a OpenAI y tu presupuesto mensual de infraestructura IA es menor a 100 USD, este es el punto de partida.

$46
Infra base / mes
Droplet + Spaces + TLS
~94%
Reducción de costo vs SaaS
Feature set comparable
-45%
Costo LLM con caché semántica Redis
Hit rate típico
30 qps
Carga sostenida sostenible
Un droplet 4 vCPU

Por qué self-hosted en 2026 sigue teniendo sentido

El discurso dominante es que "todo está en la nube administrada". En la práctica, tres fuerzas empujan hacia el self-host:

  1. Data residency regulatoria. El AI Act europeo (exigible desde el 2 de agosto 2026) y los marcos sectoriales de salud y finanzas obligan a mantener ciertos datos dentro de una jurisdicción específica. Mandarlos a la API de un hyperscaler suele significar firmar DPAs pesados y auditorías anuales.
  2. Costos marginales de inferencia. Un flujo de agente con 50 llamadas LLM por sesión y caché semántico al 45% baja el costo por sesión a la mitad cuando Redis vive en el mismo servidor que el orquestador. La latencia de red intrarregión también cae de 30-80 ms a <2 ms.
  3. Vendor lock-in. Langfuse, Qdrant, LiteLLM, Redis, Ollama y Mastra son todas piezas Apache 2.0 o MIT que puedes mover de proveedor sin reescribir código.

Lo que este artículo no resuelve: entrenar o servir LLMs grandes (más de 13B parámetros) con latencia baja — para eso sigue siendo mejor APIs gestionadas o GPUs dedicadas.

Arquitectura

                         ┌──────────────────────────────────────────────┐
                         │   Droplet s-4vcpu-8gb (USD 40/mes)           │
                         │                                              │
  Cliente (HTTPS) ──► Nginx ──► [ LiteLLM Proxy   :4000 ]               │
                         │         │                                    │
                         │         ├──► Anthropic / OpenAI / Gemini     │
                         │         │   (rate limit + fallback)          │
                         │         │                                    │
                         │         └──► Ollama :11434 (Llama 3.3 8B)    │
                         │                                              │
                         │      [ Qdrant :6333 ]  [ Redis :6379 ]       │
                         │                                              │
                         │      [ Langfuse Web :3000 ]                  │
                         │         └──► Langfuse Worker                 │
                         │         └──► Postgres :5432                  │
                         │         └──► ClickHouse :8123                │
                         │                                              │
                         │      [ Prometheus :9090 ] [ Grafana :3001 ]  │
                         │                                              │
                         │      Restic daemon ──► DO Spaces (S3)        │
                         └──────────────────────────────────────────────┘

Red interna sobre bridge de Docker Compose; sólo Nginx y SSH exponen puertos al exterior.

El docker-compose.yml completo

Este fichero vive en /opt/numoru-ai/docker-compose.yml. Todas las credenciales se leen desde .env.

version: "3.9"

networks:
  core:
    driver: bridge

volumes:
  qdrant_data:
  redis_data:
  ollama_data:
  lf_postgres:
  lf_clickhouse:
  lf_minio:
  prometheus_data:
  grafana_data:

services:
  # --- Reverse proxy ---
  nginx:
    image: nginx:1.27-alpine
    restart: unless-stopped
    ports: ["80:80", "443:443"]
    volumes:
      - ./nginx/conf.d:/etc/nginx/conf.d:ro
      - ./certs:/etc/letsencrypt:ro
    networks: [core]
    depends_on: [litellm, langfuse-web, grafana]

  # --- Vector database ---
  qdrant:
    image: qdrant/qdrant:v1.12.5
    restart: unless-stopped
    volumes: [qdrant_data:/qdrant/storage]
    environment:
      QDRANT__SERVICE__API_KEY: ${QDRANT_API_KEY}
      QDRANT__STORAGE__PERFORMANCE__MAX_SEARCH_THREADS: 2
    networks: [core]
    deploy:
      resources:
        limits: { memory: 2g, cpus: "1.5" }

  # --- Cache semántico + memoria working ---
  redis:
    image: redis/redis-stack-server:7.4.0-v1
    restart: unless-stopped
    command: >
      redis-stack-server
      --requirepass ${REDIS_PASSWORD}
      --maxmemory 1gb
      --maxmemory-policy allkeys-lru
    volumes: [redis_data:/data]
    networks: [core]
    deploy:
      resources:
        limits: { memory: 1200m, cpus: "0.75" }

  # --- LLM gateway unificado ---
  litellm:
    image: ghcr.io/berriai/litellm:main-stable
    restart: unless-stopped
    environment:
      LITELLM_MASTER_KEY: ${LITELLM_MASTER_KEY}
      DATABASE_URL: postgresql://lf:${LF_DB_PASSWORD}@langfuse-db:5432/litellm
      LANGFUSE_PUBLIC_KEY: ${LF_PUBLIC_KEY}
      LANGFUSE_SECRET_KEY: ${LF_SECRET_KEY}
      LANGFUSE_HOST: http://langfuse-web:3000
    volumes: [./litellm/config.yaml:/app/config.yaml:ro]
    command: ["--config", "/app/config.yaml", "--port", "4000"]
    networks: [core]
    depends_on: [langfuse-web, redis]
    deploy:
      resources:
        limits: { memory: 512m, cpus: "0.5" }

  # --- Modelos locales ---
  ollama:
    image: ollama/ollama:0.5.4
    restart: unless-stopped
    volumes: [ollama_data:/root/.ollama]
    networks: [core]
    deploy:
      resources:
        limits: { memory: 5g, cpus: "2.5" }

  # --- Langfuse (observabilidad) ---
  langfuse-db:
    image: postgres:16-alpine
    restart: unless-stopped
    environment:
      POSTGRES_USER: lf
      POSTGRES_PASSWORD: ${LF_DB_PASSWORD}
      POSTGRES_DB: langfuse
    volumes: [lf_postgres:/var/lib/postgresql/data]
    networks: [core]
    deploy:
      resources:
        limits: { memory: 512m, cpus: "0.5" }

  langfuse-clickhouse:
    image: clickhouse/clickhouse-server:24.8
    restart: unless-stopped
    environment:
      CLICKHOUSE_USER: lf
      CLICKHOUSE_PASSWORD: ${LF_CLICKHOUSE_PASSWORD}
      CLICKHOUSE_DB: langfuse
    volumes: [lf_clickhouse:/var/lib/clickhouse]
    networks: [core]
    deploy:
      resources:
        limits: { memory: 1g, cpus: "1" }

  langfuse-web:
    image: langfuse/langfuse:3
    restart: unless-stopped
    environment:
      DATABASE_URL: postgresql://lf:${LF_DB_PASSWORD}@langfuse-db:5432/langfuse
      CLICKHOUSE_URL: http://langfuse-clickhouse:8123
      CLICKHOUSE_USER: lf
      CLICKHOUSE_PASSWORD: ${LF_CLICKHOUSE_PASSWORD}
      REDIS_CONNECTION_STRING: redis://:${REDIS_PASSWORD}@redis:6379/1
      NEXTAUTH_URL: https://langfuse.${DOMAIN}
      NEXTAUTH_SECRET: ${LF_NEXTAUTH_SECRET}
      SALT: ${LF_SALT}
      ENCRYPTION_KEY: ${LF_ENCRYPTION_KEY}
    depends_on: [langfuse-db, langfuse-clickhouse, redis]
    networks: [core]
    deploy:
      resources:
        limits: { memory: 768m, cpus: "0.75" }

  langfuse-worker:
    image: langfuse/langfuse-worker:3
    restart: unless-stopped
    environment:
      DATABASE_URL: postgresql://lf:${LF_DB_PASSWORD}@langfuse-db:5432/langfuse
      CLICKHOUSE_URL: http://langfuse-clickhouse:8123
      CLICKHOUSE_USER: lf
      CLICKHOUSE_PASSWORD: ${LF_CLICKHOUSE_PASSWORD}
      REDIS_CONNECTION_STRING: redis://:${REDIS_PASSWORD}@redis:6379/1
      SALT: ${LF_SALT}
      ENCRYPTION_KEY: ${LF_ENCRYPTION_KEY}
    depends_on: [langfuse-web]
    networks: [core]
    deploy:
      resources:
        limits: { memory: 512m, cpus: "0.5" }

  # --- Métricas ---
  prometheus:
    image: prom/prometheus:v2.55.1
    restart: unless-stopped
    volumes:
      - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
      - prometheus_data:/prometheus
    networks: [core]

  grafana:
    image: grafana/grafana:11.3.1
    restart: unless-stopped
    environment:
      GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_PASSWORD}
    volumes: [grafana_data:/var/lib/grafana]
    networks: [core]

  # --- Backups ---
  restic:
    image: mazzolino/restic:1.7.3
    restart: unless-stopped
    environment:
      RUN_ON_STARTUP: "false"
      BACKUP_CRON: "0 4 * * *"
      RESTIC_REPOSITORY: s3:${SPACES_ENDPOINT}/${SPACES_BUCKET}/restic
      RESTIC_PASSWORD: ${RESTIC_PASSWORD}
      AWS_ACCESS_KEY_ID: ${SPACES_KEY}
      AWS_SECRET_ACCESS_KEY: ${SPACES_SECRET}
      RESTIC_FORGET_ARGS: "--keep-daily 7 --keep-weekly 4 --keep-monthly 6"
    volumes:
      - qdrant_data:/mnt/qdrant:ro
      - lf_postgres:/mnt/lf_postgres:ro
      - lf_clickhouse:/mnt/lf_clickhouse:ro
      - redis_data:/mnt/redis:ro
    networks: [core]

Dimensionamiento de memoria

El droplet s-4vcpu-8gb ofrece 7.5 GB usables tras el kernel. Presupuesto de memoria:

ServicioLímiteJustificación
Ollama5 GBLlama 3.3 8B Q4_K_M cabe en ~4.8 GB
Qdrant2 GB10M vectores de 768 dims con cuantización escalar
Redis1.2 GBcaché semántica + estados de agente
ClickHouse1 GBLangfuse observability
Postgres Langfuse512 MBMetadata
Langfuse web + worker1.25 GB
Nginx + Prometheus + Grafana400 MB
Total comprometido~11.3 GB

Importante: Los límites se solapan porque Ollama sólo consume sus 5 GB cuando está respondiendo activamente. Si tu carga es mayoritariamente de agentes que usan Claude/GPT vía LiteLLM y rara vez Ollama, el working set real se mantiene debajo de 6 GB. Si tu cliente necesita Ollama como primary, sube a s-4vcpu-16gb (USD 96).

Configuración de LiteLLM Proxy

Archivo /opt/numoru-ai/litellm/config.yaml:

model_list:
  - model_name: claude-sonnet
    litellm_params:
      model: anthropic/claude-sonnet-4-6
      api_key: os.environ/ANTHROPIC_API_KEY
  - model_name: claude-opus
    litellm_params:
      model: anthropic/claude-opus-4-7
      api_key: os.environ/ANTHROPIC_API_KEY
  - model_name: gpt-4o
    litellm_params:
      model: openai/gpt-4o
      api_key: os.environ/OPENAI_API_KEY
  - model_name: llama-local
    litellm_params:
      model: ollama/llama3.3:8b-instruct-q4_K_M
      api_base: http://ollama:11434

litellm_settings:
  cache: true
  cache_params:
    type: redis-semantic
    host: redis
    port: 6379
    password: os.environ/REDIS_PASSWORD
    similarity_threshold: 0.92
    ttl: 86400
  success_callback: ["langfuse"]
  failure_callback: ["langfuse"]

router_settings:
  routing_strategy: latency-based-routing
  fallbacks:
    - claude-sonnet: [gpt-4o, llama-local]
    - gpt-4o: [claude-sonnet, llama-local]

general_settings:
  master_key: os.environ/LITELLM_MASTER_KEY
  database_url: os.environ/DATABASE_URL

Este archivo habilita tres cosas críticas: caché semántica (45-60% hit rate en agentes de atención al cliente), fallback automático (si Anthropic cae, el tráfico salta a OpenAI o al modelo local) y trazas en Langfuse sin necesidad de instrumentar cada cliente.

Nginx con TLS automático

Sub-dominios recomendados:

  • api.tudominio.com → LiteLLM Proxy (:4000)
  • langfuse.tudominio.com → Langfuse web (:3000)
  • grafana.tudominio.com → Grafana (:3001)
  • qdrant.tudominio.com → Qdrant HTTP (:6333) — protegido con basic auth además del API key

Archivo /opt/numoru-ai/nginx/conf.d/api.conf:

server {
  listen 443 ssl http2;
  server_name api.tudominio.com;
  ssl_certificate     /etc/letsencrypt/live/api.tudominio.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/api.tudominio.com/privkey.pem;

  client_max_body_size 25m;

  location / {
    proxy_pass http://litellm:4000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_read_timeout 300s;
  }
}

Renovación de certificados con un job cron que ejecuta certbot renew --webroot cada 3 días.

Backup y restauración

restic corre cada 4am y respalda todos los volúmenes a Digital Ocean Spaces. Política de retención: 7 diarios, 4 semanales, 6 mensuales.

Restauración probada: en un droplet nuevo, git clone del repo de infraestructura + .env + restic restore latest --target / regenera el stack completo en menos de 15 minutos. Tiempo objetivo de recuperación (RTO): 20 minutos. Punto objetivo de recuperación (RPO): 24 horas.

Observabilidad: lo que Langfuse + Grafana te dan

  • Langfuse: cada llamada LLM con input, output, costo, latencia y usuario. Evals programables. Prompt management versionado.
  • Grafana dashboards provisionados:
    • Tokens/min por modelo
    • Cache hit rate de Redis (meta >40%)
    • Latencia p50/p95/p99 por endpoint de LiteLLM
    • Uso de disco de Qdrant
    • Colas pendientes en Langfuse worker

Costos reales (abril 2026)

ConceptoUSD/mes
Droplet s-4vcpu-8gb40
Spaces (50 GB + egress medio)5
Dominio + certs1
Anthropic/OpenAI (pass-through)variable
Infraestructura base46

Con este stack, un cliente que antes pagaba 800 USD/mes a proveedores SaaS de RAG + observabilidad + gateway típicamente baja a 46 USD + costos de LLM (que además caen 45% por caché semántica). El ROI es inmediato a partir de la segunda semana.

Costo mensual de infraestructura: equivalentes SaaS vs stack self-hosted

Tier de entrada/estándar de cada equivalente managed vs el drop-in self-hosted de $46 / mes. Usa el gráfico completo para justificar migración ante un CFO.

$0$70$140$210$280Vector DB(PineconeStandard)Obs LLM(LangSmith)Gateway + keys(OpenRouter biz)Caché + rate limit(Upstash Pro)Inferencia local(Modal / Replicate)Stack self-hostedunificado
  • SaaS managed (USD)
  • Self-hosted (USD)

Páginas públicas de pricing Pinecone, LangSmith, OpenRouter, Upstash, febrero 2026.

Impacto de negocio y casos

Business & commercial impact

Qué vende Numoru alrededor del stack

El docker-compose.yml gratuito es asset de marketing. El revenue viene de dos servicios productizados: una instalación fixed-price ($3.5k - 8k) que levanta el stack en la cuenta DO del cliente, y un retainer de operación managed ($450-1,200 / mes) que se hace cargo de upgrades, backups y respuesta a incidentes. Ambos son de margen alto porque la infra real cuesta $46.

Quién compra infra self-hosted de IA

Pricing instalación + ops managed por comprador (Numoru, 2026)

Fintech (residencia de datos)
No puede mandar PII a OpenAI. Quiere RAG + trazas LLM en región.
$6,500 install + $950 / mes ops
12 mo ops
Salud / telemedicina
Compliance HIPAA-equivalente. Prefiere on-prem total o híbrido.
$8,000 install + $1,200 / mes ops
24 mo ops
Legal / contable
Confidencialidad. Features IA sin compartir archivos con SaaS.
$4,500 install + $650 / mes ops
12 mo
Agencias + SaaS boutique
Quiere bajar el bill de infra IA. Opera ella misma tras el install.
$3,500 install (ops opcional)
One-time, 30 días warranty
Enterprise dev teams (UE)
Residencia de datos AI Act. Qdrant + Langfuse en Frankfurt / Ámsterdam.
$7,500 install + $1,100 / mes ops
24 mo
Gobierno LATAM / ONG
Infra IA soberana, cero SaaS extranjero. Self-host total.
$12,000 install + $1,600 / mes ops
12 mo + training

Benchmarks públicos que respaldan el pitch

Public case studyVector DB · Global · 2024

Qdrant — performance vs alternativas managed

Challenge
Publicar benchmarks reproducibles comparando servicios managed de vector DB en latencia y costo.
Solution
La suite de benchmarks de Qdrant corre workloads idénticos contra Pinecone, Weaviate, Milvus y Qdrant self-hosted.
Results
Qdrant p95 latencia
<50 ms
Set 1M+ vectores, single node
Pinecone p95 (Standard)
~300 ms
Mismo workload
Ratio de costo self-host
1 / 12
Vs Pinecone Standard
Public case studyObservabilidad LLM · Global · 2024-2025

Langfuse — adopción self-hosted

Challenge
Documentar la adopción de la opción self-hosted para clientes con necesidades de residencia de datos.
Solution
Langfuse publica stats de despliegues y mantiene una self-hosted edition bajo licencia MIT.
Results
Despliegues self-hosted
8,000+
Reportados globalmente
Clientes enterprise self-host
Bancos + healthtech
Casos en el blog
Migraciones SaaS → self-hosted
En curso
Impulsado por AI Act UE
Public case studyCloud provider · Global · 2024

Digital Ocean — sizing de droplets para IA

Challenge
Definir guías de sizing para correr componentes IA productivos sobre infra DO.
Solution
DO publica tutoriales y casos de la comunidad cubriendo despliegues Docker Compose de vector DB + observabilidad + proxy.
Results
Tamaño recomendado de entrada
4 vCPU / 8 GB
Hasta 500 usuarios concurrentes
Uptime típico
99.95%
SLA de droplet DO
Precio de lista mensual
$40
Droplet s-4vcpu-8gb

Caso ilustrativo — agencia migrando 7 clientes fuera de SaaS managed

Illustrative caseAgencia de servicios IA · 12 empleados · sirve 14 clientes enterprise · LATAM + EE.UU.

Agencia partner Numoru migrando 7 clientes mid-market al stack compartido

Baseline
Cada cliente pagaba $620-1,100 / mes dividido entre Pinecone + LangSmith + OpenRouter + Upstash. Bill combinado: $6,800 / mes. La residencia de datos se volvía objeción creciente.
Intervention
Numoru instaló el stack self-hosted en 7 droplets dedicados (uno por cliente). La agencia absorbió el retainer de ops los primeros 90 días y luego facturó $450 / mes a cada cliente.
Projected outcome (12 mo)
Bill managed combinado
$6,800 → $322
7 droplets + Spaces
Ahorro mensual neto
$6,478
Pasado al cliente tras retainer
Retainer de ops agencia
+$3,150 / mes
7 × $450
Fee de instalación (one-time)
$45,500
7 × $6,500 setup
Objeciones de residencia
Resueltas
Los 7 clientes renovaron
Lift de run-rate anual
+$37,800
Retainer neto de infra
Math de ahorros basado en pricing público Pinecone Standard, LangSmith Team y Upstash Pro. Caso sintético — no representa a un cliente específico de Numoru.

Calculadora ROI — migrar fuera de SaaS managed

Cliente mid-market: SaaS managed vs Numoru self-hosted (12 meses)

Payback: 3 months
Assumptions
Bill actual SaaS (RAG + obs + gateway)$820 / mes
Gasto mensual LLM API$3,400 / mes
Hit rate de caché semántica45%
Ahorro LLM por caché$1,530 / mes
Instalación Numoru (one-time)$6,500
Retainer ops Numoru$450 / mes
Droplet DO + Spaces + TLS$46 / mes
Tiempo eng ahorrado (backups, upgrades)6 h / mes × $95
Instalación (one-time)−$6,500
SaaS managed evitado (12 mo × $820)+$9,840
Ahorro LLM por caché (12 mo × $1,530)+$18,360
Droplet + Spaces + TLS (12 mo × $46)−$552
Retainer ops Numoru (12 mo × $450)−$5,400
Tiempo eng ahorrado (6 h × $95 × 12)+$6,840
Contribución neta año 1+$22,588

Tiers de pricing Numoru

Install
$3,500one-time
Solo stand-up. Operas tú después.
  • Corre en tu cuenta DO
  • Compose con Qdrant + Langfuse + LiteLLM + Redis
  • TLS + Nginx + backups
  • Warranty 30 días
  • Runbook PDF
  • Stripe / tarjeta o PO
Install + Ops
$6,500one-time + $650 / mes
Install + 12 mo de ops managed.
  • Todo lo del Install
  • Monitoreo 24 / 7 (Grafana + alertas)
  • Patching + upgrades de versión
  • Revisión mensual de incidentes
  • Canal Slack compartido
  • Auditoría trimestral de costo
Pack de compliance
$9,500+one-time + $1,100 / mes
Residencia UE o scope HIPAA.
  • Despliegue en región UE (FRA / AMS / PAR)
  • Controles HIPAA-equivalentes
  • Bundle de docs técnicas AI Act
  • SAML / SSO
  • Coordinación de pentest
  • Atestación anual de compliance

El retainer de ops escala por número de droplets — agencias multi-cliente tienen 20% off desde 5 droplets, 30% off desde 10.

FAQ

¿Este stack aguanta tráfico productivo real?Sí para cargas de hasta ~30 queries por segundo sostenidas y 500k llamadas LLM por día. Más allá de eso, separa Qdrant y Langfuse en droplets propios.

¿Puedo correr Claude o GPT localmente?No, son modelos cerrados. Ollama + Llama 3.3 8B / Qwen 2.5 7B es la ruta local. Para casos sensibles mezcla: prompts genéricos van a Claude vía LiteLLM, prompts con PII van a Llama local.

¿Por qué no Kubernetes?Un droplet único con Docker Compose es más barato, más fácil de operar por un consultor individual y suficiente hasta los 500 usuarios concurrentes. Kubernetes empieza a tener sentido a partir de 3 nodos productivos.

¿Es compatible con AI Act?El stack es compatible con los requisitos técnicos de transparencia, log keeping y data residency. La compliance formal requiere además documentación DPIA y gobernanza — eso es servicio, no infraestructura.

¿Puedo reemplazar Qdrant por pgvector? Para menos de 100k vectores y bajos QPS, sí. A partir de 1M vectores y búsqueda híbrida en producción, Qdrant gana claramente en latencia (p95 <50 ms vs 300 ms).

Próximos pasos

El repositorio completo con docker-compose.yml, Terraform para crear el droplet y runbooks de incidente está publicado en github.com/numoru-ia/ai-stack-do. El siguiente artículo de esta serie cubre el patrón de memoria por capas que aprovecha Redis + Langfuse + Mem0 dentro de este stack.

¿Quieres resultados así para tu empresa?

Iniciar conversación
Compartir