Así publiqué mi sitio en Cloudflare con IA por 5 dólares al mes
Y el tuyo podría costar 0. La migración desde AWS la hizo Claude Code en 43 minutos. El resto del día fue lo que una IA no debe hacer sola: pagar, crear tokens y tocar el DNS.
En este post 11 secciones
- Cuánto cuesta, pieza por pieza
- Lo que vas a necesitar
- Paso 1: decidir con números, no con costumbre
- Paso 2: la migración, en local (43 minutos)
- Paso 3: lo que hice yo (y por qué no la IA)
- Paso 4: publicar con cada push
- Paso 5: pruebas y producción
- Lo que salió mal
- Resultados
- Lo que aprendí
- Hazlo tú: la lista completa
Este sitio corre completo en Cloudflare: el servidor de Next.js, la lista de testers en una base de datos, los correos de confirmación con su firma, la caché de las páginas, una tarea diaria que borra los datos vencidos, HTTPS y un sitio de pruebas con contraseña. Cada vez que subo un cambio a GitHub, se publica solo en unos 2 minutos. Todo eso me cuesta 5 dólares al mes, y en el primer día el panel marca 0,00 USD de uso facturable encima de esa cuota.
Hace dos días el plan era AWS. Lo cambiamos con Claude Code en un día, y aquí está todo para que hagas tu versión: lo que cuesta cada pieza, cuándo podría costarte 0, los prompts, los comandos y los errores, que fueron varios.

Cuánto cuesta, pieza por pieza
Lo primero que preguntas es el precio, así que va primero. Las cifras son de la documentación de Cloudflare del 20 de octubre de 2026; el uso es el de mi panel.
| Pieza | Para qué | Costo |
|---|---|---|
| Workers (plan Paid) | El servidor de Next.js, con el adaptador OpenNext | 5 USD al mes: incluye 10 millones de peticiones y 30 millones de ms de CPU |
| Archivos estáticos | CSS, JavaScript, fuentes e imágenes | Gratis e ilimitados |
| D1 | La lista de testers (SQLite) | Incluido: 25.000 millones de filas leídas al mes. Usé 398 |
| R2 + Durable Object | La caché de las páginas que se regeneran solas | 10 GB gratis al mes. Uso 47 MB |
| Email Service | Confirmación, bienvenida, códigos de beta | 3.000 correos al mes incluidos con el plan Paid |
| Cron | Borrar cada día los datos vencidos | Incluido |
| DNS, CDN y HTTPS | Los DNS, la red de Cloudflare y el certificado | Gratis (plan Free del dominio) |

¿Y el tuyo en 0?
Puede ser. El plan gratis de Workers da 100.000 peticiones al día, archivos estáticos sin límite, D1 con 5 millones de lecturas diarias y 5 GB, R2 con 10 GB y hasta 5 tareas programadas. Para un portafolio, un blog o una página de presentación, sobra.
Decidí pagar el plan de 5 USD por dos razones:
- Los correos. Email Service solo envía a cualquier destinatario con el plan Paid. En el gratis, solo a direcciones que verificaste en tu cuenta.
- El límite de CPU. El plan gratis da 10 ms de CPU por petición. Next.js renderizando en el servidor se pasa con facilidad, y la petición falla.
Los 5 USD alcanzan para varios proyectos
Los 5 USD son por cuenta, no por sitio. Con esa misma cuota puedes tener hasta 500 Workers, y todos comparten lo incluido: los 10 millones de peticiones, la base D1 y los 3.000 correos al mes. Yo ya tengo dos sitios ahí, producción y pruebas, y en el primer día usaron 3.370 peticiones de esos 10 millones. Si tus proyectos son pequeños, la misma cuota te sirve para publicar varios.
Lo que vas a necesitar
- Una cuenta de Cloudflare (dash.cloudflare.com) con el DNS de tu dominio ahí. Email Service lo exige.
- Claude Code (documentación) o el agente que uses para programar.
- El MCP de Cloudflare, para que el agente busque en la documentación y revise tu cuenta.
- wrangler, el CLI de Cloudflare. Se instala en el proyecto con
pnpm add -D wrangler. - El adaptador OpenNext, si tu sitio es Next.js.
- GitHub y su CLI
gh, para que el despliegue sea automático.
El MCP de Cloudflare
Servidores MCP oficiales de Cloudflare: el agente busca en la documentación y usa la API de tu cuenta, con tu permiso por OAuth.
Lo usé para consultar precios y límites vigentes, y verificar lo que yo hacía en el panel.
Se agrega a Claude Code con un comando y se autoriza en el navegador:
claude mcp add --transport http cloudflare https://mcp.cloudflare.com/mcpMe sirvió sobre todo para una cosa: leer la documentación del día. Ahí vimos que el correo exigía el plan Paid, antes de decidir. Los precios y límites cambian, y el agente no los sabe de memoria.
OpenNext para Cloudflare
Adaptador que convierte el resultado de next build en un Worker de Cloudflare: páginas en el servidor, ISR, middleware y archivos estáticos.
Lo usé para correr Next.js 16 en Workers sin reescribir el sitio.
Paso 1: decidir con números, no con costumbre
Yo conozco AWS, así que el plan era Amplify para el sitio, DynamoDB para la lista y SES para los correos. La noche antes de publicar pregunté qué cambiaría para usar Cloudflare. Al día siguiente lo comparamos con AWS y Vercel, y lo que decidió fue simple: el sitio todavía no estaba publicado, así que migrar costaba poco. Después habría costado mucho más.
Para el correo comparamos con lo que ya conocía. Ese día: Mailgun Basic cobraba 15 USD por 10.000 correos, Resend Pro 20 USD por 50.000 y SES 0,10 USD por 1.000. Email Service venía con 3.000 al mes dentro de los 5 USD que ya iba a pagar por Workers.
Paso 2: la migración, en local (43 minutos)
A las 11:53 dije «dale, haz la prueba en local con Cloudflare en una rama nueva». A las 12:36 estaba el commit: 47 archivos, 3.525 líneas nuevas y 913 borradas, con todo probado contra el Worker en mi máquina.
Servidor
Next.js en un Worker
El adaptador OpenNext convierte el build en un Worker. Los archivos estáticos salen del mismo lugar, gratis.
Datos
DynamoDB → D1
La lista pasó a SQLite con migraciones. Lo que vence se deja de leer y una tarea diaria lo borra.
Correo
SES → Email Service
Un binding en el Worker. En local, los correos se imprimen en la terminal.
Caché
R2 + Durable Object
Las páginas que se regeneran solas (las apps cada minuto, los posts cada día) se guardan en R2.
Secretos
wrangler secret
Nada en .env ni en el build. Cada entorno tiene su clave.
Publicar
GitHub Actions
Push a dev publica pruebas; push a main, producción.
$ pnpm cf:preview$ pnpm exec tsx e2e.mts http://localhost:8787✓ alta → 200 {"estado":"pendiente"}✓ confirmar → 303 /confirmado✓ cookie de sesión verificada✓ votos guardados✓ baja → 200✓ deshacer → 200✓ límite de intentos → 400 400 400 429 429 429 429Todo bienPaso 3: lo que hice yo (y por qué no la IA)
Le pedí a Claude una guía paso a paso para hacer el setup yo, porque quería entender qué estaba creando. Hay cosas que una IA no debe hacer sola: pagar, crear tokens con permisos sobre tu cuenta y tocar el DNS. Yo hacía cada paso y Claude lo revisaba con la API.
- Activar Workers Paid y R2 en el panel. La tarjeta la pones tú.
- Arreglar el DNS del correo. Tenía dos registros SPF (uno viejo de Namecheap) y eso rompe la verificación. Dejé uno.
- Agregar el dominio a Email Service. Cloudflare crea sus registros MX, SPF y DKIM en un subdominio, sin tocar tu correo de siempre.
- Crear la base D1, la caché R2 y los secretos con wrangler.
- Crear un token de API para GitHub, con permisos solo para Workers, D1 y las rutas del dominio, y que vence en un año.
pnpm exec wrangler loginpnpm exec wrangler d1 create emanuelconia-pruebaspnpm exec wrangler d1 migrations apply DB --remote --env pruebaspnpm exec wrangler r2 bucket create emanuelconia-pruebas-cacheopenssl rand -hex 32 | pnpm exec wrangler secret put TOKENS_SECRETO --env pruebasgh secret set CLOUDFLARE_API_TOKENgh secret set CLOUDFLARE_ACCOUNT_ID
D1 · la lista de testers
R2 · la caché de las páginas
Email Service · el dominio listo
Paso 4: publicar con cada push
El despliegue lo hace GitHub Actions: instala, aplica las migraciones de D1, compila y publica. Push a dev publica el sitio de pruebas; push a main, producción. Tarda unos 2 minutos.
1- run: pnpm install --frozen-lockfile2- name: Migraciones de D13 run: pnpm exec wrangler d1 migrations apply DB --remote --env "$ENTORNO"4- name: Compilar5 run: pnpm cf:build6- name: Publicar7 run: pnpm exec opennextjs-cloudflare deploy --env "$ENTORNO"
Paso 5: pruebas y producción
A las 15:20 estaba en vivo pruebas.emanuelconia.com, con contraseña y sin indexar. Ahí probé el flujo con mi correo, revisamos todo el sitio y arreglamos lo que salió. A las 17:21 creé la base de producción, y cerca de las 19:18 emanuelconia.com estaba en vivo. El certificado HTTPS lo emitió Cloudflare solo, en segundos.
Lo que salió mal
- Una función que en mi máquina sí existía. La forma de cargar los posts daba falso dentro del Worker y tres páginas respondían error 500. Lo encontró la prueba contra el Worker local, no el servidor de desarrollo.
- Next 15 se quedó sin soporte el mismo día. Next.js da dos años de soporte por versión, y Next 15 los cumplió esa tarde. El adaptador dejó de compilarlo. Migramos a Next 16 antes de producción.
- El dominio no se dejaba conectar. La vista previa de Cloudflare dijo «sin conflictos», pero al publicar el dominio seguía tomado por dos registros de la página de estacionamiento de Namecheap. Los borré y repetimos solo el paso que falló.
- El MCP no podía con todo. Leía la documentación y la cuenta, pero no tenía permiso para el DNS ni para activar «Always Use HTTPS». Esos pasos los hice en el panel.
- La auditoría contaba páginas 404 como correctas. Ahora falla si una página no responde lo que debe.
- La analítica podía filtrar tokens. Al pasar de un enlace de baja a otra página, la URL completa (con su token) podía llegar a Mixpanel. Ahora se manda sin la consulta.
- La política de privacidad prometía cosas que el código no hacía. Arreglamos el código, no el texto.
Resultados
| Qué | Resultado |
|---|---|
| Migración del código | 43 minutos: 47 archivos, +3.525 / −913 líneas |
| De decidir a producción | De las 11:53 a cerca de las 19:18, con auditoría, privacidad y Next 16 en el medio |
| Despliegue | 2 min 14 s por push |
| Worker | 4,5 MB comprimido |
| Costo | 5 USD al mes; 0,00 USD de uso facturable en el período |
| Uso del período | 3.370 peticiones, 110 segundos de CPU, 398 filas leídas en D1, 47 MB en R2 |
emanuelconia.com en vivo
El correo de confirmación
Lo que aprendí
- Decide la infraestructura antes de publicar. Migrar un sitio sin usuarios costó un día. Con usuarios y datos, habría sido otra historia.
- La IA migra rápido; lo lento es lo tuyo. El código tardó 43 minutos. Las cuentas, los pagos, los tokens y el DNS tardaron más, y está bien que sea así.
- Pídele que lea la documentación del día. Con el MCP, los precios y límites salen de la fuente, no de lo que el modelo recuerda.
- Prueba en el mismo entorno donde vas a publicar. El error 500 no aparecía en el servidor de desarrollo, solo en el Worker.
- Publica solo desde CI. Así no viaja nada de tu máquina al sitio.
Hazlo tú: la lista completa
- Pasa el DNS de tu dominio a Cloudflare.
- Agrega el MCP de Cloudflare a tu agente.
- Pide la comparación con documentación vigente (prompt 1) y decide.
- Pide la migración en una rama, probada contra el Worker local (prompt 2).
- Activa el plan que necesites: Free si es estático, Paid si manda correos o renderiza en el servidor.
- Agrega tu dominio a Email Service y deja un solo registro SPF.
- Crea la base D1, el bucket R2 y los secretos con wrangler.
- Crea un token de API con permisos mínimos y guárdalo en GitHub.
- Publica pruebas, prueba con tu correo y revisa todo.
- Publica producción. Si el dominio tiene registros viejos, bórralos primero.
1{2 "name": "mi-sitio",3 "main": ".open-next/worker.js",4 "compatibility_date": "2026-10-01",5 "compatibility_flags": ["nodejs_compat"],6 "assets": { "directory": ".open-next/assets", "binding": "ASSETS" },7 "triggers": { "crons": ["17 7 * * *"] },8 "env": {9 "produccion": {10 "name": "mi-sitio",11 "routes": [{ "pattern": "midominio.com", "custom_domain": true }],12 "d1_databases": [{ "binding": "DB", "database_name": "mi-sitio", "database_id": "<id>" }],13 "r2_buckets": [{ "binding": "NEXT_INC_CACHE_R2_BUCKET", "bucket_name": "mi-sitio-cache" }],14 "send_email": [{ "name": "EMAIL" }]15 }16 }17}