Migracion completa del stack FastAPI + Next.js + PostgreSQL + Redis a Zeabur con despliegues automaticos, preview deployments y escalado por temporada de cosecha.
agrobot-io/agrobot-platform.zeabur.toml para override del comando de inicio con Gunicorn.NEXT_PUBLIC_API_URL apuntando al servicio FastAPI.api.agrobot.io y app.agrobot.io. Zeabur genera SSL automaticamente.min_instances=1 y max_instances=8 en septiembre-noviembre.# AgroBot โ API FastAPI (Python 3.12) # Zeabur detecta Python automaticamente via requirements.txt [build] command = "pip install -r requirements.txt" [runtime] # Override: gunicorn con workers async para FastAPI command = "gunicorn app.main:app -k uvicorn.workers.UvicornWorker --workers 4 --bind 0.0.0.0:$PORT" [env] PYTHON_VERSION = "3.12" APP_ENV = "production" LOG_LEVEL = "info" # DATABASE_URL y REDIS_URL son inyectadas automaticamente por Zeabur Marketplace [scaling] min_instances = 1 max_instances = 8 # Escala hasta x8 en temporada alta (sep-nov) [health_check] path = "/health" interval = "30s" timeout = "10s"
# AgroBot โ Frontend Next.js 14 # Auto-deteccion de Next.js via package.json [build] command = "npm run build" output = ".next" [runtime] command = "npm start" [env] NODE_ENV = "production" NEXT_PUBLIC_API_URL = "https://api.agrobot.io" NEXT_PUBLIC_APP_NAME = "AgroBot" [scaling] min_instances = 1 max_instances = 4 [health_check] path = "/" interval = "60s"
# 1. Instalar CLI de Zeabur npm install -g zeabur # 2. Autenticar (abre navegador para OAuth con GitHub) zeabur auth login # 3. Desplegar el backend FastAPI desde /backend cd backend zeabur deploy --name agrobot-api --region sin1 # Singapore (latencia baja para EU+LATAM) # 4. Variables de entorno de la API zeabur env set AGROBOT_SECRET_KEY "$(openssl rand -hex 32)" zeabur env set SENTRY_DSN "https://xxxxx@o123.ingest.sentry.io/456" # DATABASE_URL y REDIS_URL son auto-inyectadas por el Marketplace # 5. Dominio personalizado para la API zeabur domain add api.agrobot.io # Output: CNAME api.agrobot.io โ agrobot-api.zeabur.app # SSL Let's Encrypt provisionado automaticamente # 6. Desplegar frontend Next.js desde /frontend cd ../frontend zeabur deploy --name agrobot-web --region sin1 zeabur domain add app.agrobot.io # 7. Verificar estado de servicios zeabur env list zeabur domain list
DATABASE_URL.REDIS_URL.DATABASE_URL, REDIS_URL y POSTGRES_PASSWORD en todos los servicios del proyecto. No es necesario copiar ni gestionar credenciales manualmente.
| Rama Git | Entorno | URL | Auto-deploy | Base de Datos |
|---|---|---|---|---|
| main | PRODUCCION | api.agrobot.io / app.agrobot.io | โ En cada push | PostgreSQL prod |
| develop | STAGING | staging-api.agrobot.io | โ En cada push | PostgreSQL staging |
| feature/* / fix/* | PREVIEW | pr-{n}-api.zeabur.app | โ Por PR abierta | Efimera (se borra con PR) |
/** * AgroBot โ Zeabur CI/CD Automation Script * Usado en GitHub Actions para reiniciar servicios tras migraciones DB */ const ZEABUR_API = "https://gateway.zeabur.com/graphql"; const ZEABUR_TOKEN = process.env.ZEABUR_TOKEN!; const PROJECT_ID = "proj_agrobot_production"; const API_SERVICE_ID = "svc_agrobot_api"; const WEB_SERVICE_ID = "svc_agrobot_web"; async function zeaburQuery(query: string, variables?: Record<string, any>) { const res = await fetch(ZEABUR_API, { method: "POST", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${ZEABUR_TOKEN}`, }, body: JSON.stringify({ query, variables }), }); const { data, errors } = await res.json(); if (errors) throw new Error(errors[0].message); return data; } /** Reiniciar la API despues de migraciones Alembic */ async function restartAfterMigration() { console.log("[AgroBot CI] Reiniciando agrobot-api tras migracion DB..."); await zeaburQuery(` mutation ($id: ObjectID!) { restartService(_id: $id) } `, { id: API_SERVICE_ID }); console.log("โ agrobot-api reiniciado correctamente"); } /** Listar estado de todos los servicios del proyecto */ async function listProjectServices() { const data = await zeaburQuery(` query ($projectId: ObjectID!) { project(_id: $projectId) { services { _id name status domains { domain } } } } `, { projectId: PROJECT_ID }); return data.project.services; } /** Escalar instancias para temporada de cosecha (sep-nov) */ async function scaleForHarvest(maxInstances: number = 8) { console.log(`[AgroBot] Escalando a max ${maxInstances} instancias...`); // Zeabur escala automaticamente entre min y max segun CPU/memoria // Este script actualiza el limite maximo via API await zeaburQuery(` mutation ($id: ObjectID!, $max: Int!) { updateServiceScaling(_id: $id, maxInstances: $max) } `, { id: API_SERVICE_ID, max: maxInstances }); } // Entry point: ejecutar desde GitHub Actions (async () => { const cmd = process.argv[2]; if (cmd === "restart") await restartAfterMigration(); if (cmd === "status") console.log(await listProjectServices()); if (cmd === "scale-up") await scaleForHarvest(8); if (cmd === "scale-down") await scaleForHarvest(2); })();
name: AgroBot Deploy + Migrate on: push: branches: [main, develop] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Ejecutar migraciones Alembic run: alembic upgrade head env: DATABASE_URL: ${{ secrets.DATABASE_URL }} - name: Reiniciar API en Zeabur run: npx ts-node scripts/zeabur-ci.ts restart env: ZEABUR_TOKEN: ${{ secrets.ZEABUR_TOKEN }} - name: Verificar salud del servicio run: curl -f https://api.agrobot.io/health
requirements.txt y package.json. Solo se usa zeabur.toml para el override del comando de inicio con Gunicorn, que Zeabur no puede inferir.
DATABASE_URL y REDIS_URL automaticamente. El equipo de AgroBot nunca necesita copiar passwords โ ni en local ni en CI.
zeabur-ci.ts scale-up aumenta el techo de instancias a 8 antes de septiembre. Zeabur escala automaticamente dentro del rango segun CPU. En diciembre, scale-down lo reduce a 2 para controlar costes.