Guía9 min

Desplegar un agente IA en Cloudflare Workers: guía práctica

Resumen

Tutorial paso a paso para correr un agente de IA en Cloudflare Workers: Wrangler, handler fetch para webhooks, Cron Triggers para jobs periódicos, secretos cifrados, tabla de límites Free vs Paid y cuándo Workers gana frente a Vercel, Railway o un VPS.

Cloudflare
Red de nodos en el borde conectada a un worker que recibe un webhook y dispara un cron

Qué resuelve

Esta pieza se queda en la decisión práctica: qué instalar, qué riesgo agrega y cómo aplicarlo sin romper operación.

La comparativa Vercel vs Workers vs VPS te dice dónde. Esta guía es el cómo cuando eliges Cloudflare Workers: un proceso en el borde, cobrado por CPU (no por reloj), con HTTPS incluido y un cron nativo. Para un agente de webhooks o un job periódico que llama a un LLM, ese modelo es el que más se parece a “gratis hasta que escala”. Las fuentes oficiales fueron consultadas el 3 de septiembre de 2026.

Si tu agente necesita un proceso vivo con sqlite y Docker, Railway o el self-hosting en VPS encajan mejor. Aquí el foco es el Worker.

Cuándo sí y cuándo no

Sí, si el agente:

  • Responde webhooks (Telegram, Slack, GitHub) y sale en milisegundos más la espera al LLM.
  • Corre un cron: digest diario, reindex de RAG, alerta de costos.
  • Tiene poco estado: el estado vive en KV, D1 o un API externo, no en RAM del proceso.

No, si el agente:

  • Ejecuta código de usuario o un sandbox largo: 128 MB de RAM y 3–10 MB de Worker no dan para un runtime de coding.
  • Mantiene un socket o un long-poll abierto horas: el runtime se actualiza varias veces por semana y corta inflight a 30 s de gracia.
  • Necesita GPU o binarios nativos.

La regla: Workers es un handler, no un servidor. Si tu arquitectura asume while (true), no es Workers.

Límites que sí importan (Free vs Paid)

LímiteFreePaid (USD 5/mes)
Requests100,000/día (reset 00:00 UTC; Error 1027 al pasarte)sin límite diario de requests
CPU por request HTTP10 msdefault 30 s, máximo 5 min
Wall clock HTTPsin límite duro mientras el cliente esté conectadoigual
Cron wall clock15 min15 min
Memoria128 MB128 MB
Subrequests50/request10,000/request
Tamaño del Worker3 MB10 MB
Variables de entorno64 × 5 KB128 × 5 KB
Cron Triggers por cuenta5250

Dos lecturas prácticas:

  1. El cobro es por CPU, no por espera. Llamar a OpenAI y esperar 8 s no consume el cupo de 10 ms / 30 s. El loop que parsea JSON sí.
  2. waitUntil() solo alarga 30 s después de responder o de que el cliente se desconecte. Trabajo post-respuesta largo → Queues o Workflows, no un for en el handler.

Paso 1 — Proyecto con Wrangler

Wrangler pide Node ≥ 16.17. Con pnpm:

pnpm create cloudflare@latest mi-agente
cd mi-agente
pnpm wrangler login

El scaffold deja un Worker con fetch. Eso cubre el webhook. El cron va en el mismo archivo.

Paso 2 — fetch + scheduled en un Worker

Flujo de un agente en Workers: webhook HTTP y cron periódico

export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext) {
    if (request.method !== "POST") {
      return new Response("ok", { status: 200 });
    }
    const event = await request.json();
    ctx.waitUntil(runAgent(event, env));
    return new Response("accepted", { status: 202 });
  },

  async scheduled(controller: ScheduledController, env: Env, ctx: ExecutionContext) {
    ctx.waitUntil(runDigest(env, controller.cron));
  },
};

El 202 vuelve al webhook en milisegundos; el LLM corre en waitUntil. Si el digest dura más de 30 s tras responder, no uses waitUntil para eso: el scheduled ya tiene 15 min de wall clock.

Cron en Wrangler (UTC):

[triggers]
crons = ["0 14 * * *"]

Prueba local del cron, según la docs de scheduled:

curl "http://localhost:8787/cdn-cgi/handler/scheduled?format=json"

Paso 3 — Secretos, no variables

La API key del modelo es un secreto, no una env var del wrangler.toml. Cloudflare cifra el binding y lo inyecta en env:

pnpm wrangler secret put OPENAI_API_KEY

En el handler: env.OPENAI_API_KEY. El resto (rotación, no loguear, no NEXT_PUBLIC_) está en secretos y variables de entorno.

Paso 4 — Deploy y verificar

pnpm wrangler deploy
curl -s https://mi-agente.<subdominio>.workers.dev

Criterio de éxito: el GET responde ok, un POST de prueba entra a logs (pnpm wrangler tail) y el cron aparece en Workers → Triggers. Custom domain: Workers → Settings → Domains; el certificado lo emite Cloudflare.

Relación con el resto del stack de deploy

Workers cubre el borde. No cubre un proceso con sqlite, un sandbox de código ni un reverse proxy que tú parcheas. Esa frontera es la que evita canibalizar las otras guías:

Si el webhook en Workers es la puerta y el trabajo pesado vive en otro servicio, el Worker solo valida, encola y responde 2xx. Esa es la forma correcta de combinarlos, no clonar el agente en los dos sitios.

Errores comunes

Errores típicos al operar un agente en Cloudflare Workers

SíntomaCausa típicaFix
Error 1027Te pasaste de 100k requests/día en FreeSube a Paid o recorta healthchecks cada 10 s
CPU exceeded (10 ms)Parseo pesado o loop en el handler, no la espera al LLMMueve CPU al scheduled o a una Queue; perfila con DevTools
Cron no disparaFalta [triggers].crons o el handler scheduledAñade ambos; prueba con /cdn-cgi/handler/scheduled
Secret undefinedLo pusiste en [vars] del toml (queda en el repo)wrangler secret put; borra el valor del toml
Work post-202 se cortawaitUntil tope 30 sCron de 15 min, Queue o Workflow

Checklist de producción

  • Free vs Paid decidido con la tabla de requests/CPU, no a ojo
  • API keys por wrangler secret put, no en el toml
  • Webhook responde 2xx rápido; el LLM va en waitUntil o en cron
  • Cron en UTC, testeado en local
  • wrangler tail abierto el primer día de tráfico real
  • Deployment previo identificable en el dashboard para volver atrás a mano

Siguiente paso: si el Worker es solo la puerta y el agente pesado vive en otro lado, combina esta guía con Railway o el VPS con Docker. Si todavía no tienes runtime local, el curso gratuito deja un agente corriendo para portarlo al fetch.