Deployment

Deploy to Gencow Cloud — frontend, backend, static assets, environments, CI/CD

Gencow supports four deployment modes: gencow dev (real-time backend with watch), gencow deploy (one-shot backend deploy), gencow deploy --static (backend + built frontend), and gencow static (frontend files only).

Frontend HTML served by Gencow can also receive same-origin hosted analytics automatically. See Hosted App Analytics for script injection, privacy rules, Dashboard metrics, and country attribution.

Environments

Gencow BaaS provides two isolated environments:

Dev Production
Domain *.gencow.dev *.gencow.app
Access All plans Startup / Enterprise
Command gencow dev / gencow deploy gencow deploy --prod
Database app_{name} schema app_{name}-prod schema
Env vars gencow env set KEY=VAL gencow env set KEY=VAL --prod

Self-hosted users: You manage your own deployment infrastructure. gencow deploy is not available for cloud targets. Connect to your own PostgreSQL via DATABASE_URL.

Plan Limits

Public pricing plans are Hobby, Startup, and Enterprise. Enterprise is handled through a custom inquiry path.

Plan App slots Realtime connections/app Production deploy Custom domain
Hobby 5 50 Not available Not available
Startup 50 5,000 Available Available
Enterprise Custom Custom Custom Custom

App slots count dev apps and production apps separately. For example, a first gencow deploy --prod creates a separate production app and consumes one additional slot.

Realtime limits apply to concurrent WebSocket subscriptions for one app. HTTP requests and static page views are metered separately through platform credits.

See Cloud Plan Limits for the full feature matrix, credit pools, storage/image limits, and custom-domain policy.

Authentication

Login

gencow login

This opens your browser for Device Auth:

  1. A code is displayed in the terminal
  2. Browser opens to gencow.app/cli-auth
  3. Confirm the code in the browser
  4. Token is saved to ~/.gencow/credentials.json

Check Status

gencow whoami
# → Email, User ID, Token info, Expiry

Logout

gencow logout

Deploying Marketplace Templates

Marketplace templates are cloned as complete source projects. Do not initialize them again with gencow init . --force; that command can overwrite Gencow-owned scaffold files.

gencow templates clone <template-slug> my-app
cd my-app
bun install
gencow login
gencow deploy

For templates that include a frontend build, deploy backend + static files together:

bun run build
gencow deploy --static dist/

On the first deploy, Gencow creates a new cloud app for your account and writes local app metadata to gencow.json. Marketplace downloads intentionally exclude gencow.json, .env, .gencow/, platform tokens, and preview app bindings.

Backend Development (Real-time)

Start real-time development — watches for changes and auto-deploys:

gencow dev

What Happens

  1. Creates app on platform (if first time) → assigns unique appId
  2. Bundles gencow/ + package.json + lockfiles → tar.gz
  3. Uploads bundle to platform
  4. Platform provisions: PostgreSQL database + isolated container
  5. Watches for file changes → auto-redeploys
  6. Saves appId to gencow.json

Dev Output

  Gencow Dev — Watch Mode

  ▸ 앱:    null-mint-9625
  ▸ URL:   https://null-mint-9625.gencow.app

  ✓ 초기 배포 완료 (42.5 KB)
  ⏳ Watching for changes...

One-Shot Deploy (Dev)

Deploy your backend to the dev environment without watch mode:

gencow deploy

This is useful for CI/CD pipelines or when you want a quick push without staying attached.

Production Deploy (Startup / Enterprise)

gencow deploy --prod
gencow deploy --prod --region kr

Startup / Enterprise only. Hobby plan users will see a blocking message with instructions to use gencow deploy (dev) or gencow dev instead.

On first production deploy, Gencow automatically creates a separate production app (my-app-prod):

  🚀 First production deployment!
  This will create a production app: my-app-prod
  Proceed? (y/N): y

  ✓ Prod 앱 생성 완료: my-app-prod
  ▸ URL: https://my-app-prod.gencow.app
  • DB isolation: Separate database schema (app_my-app vs app_my-app-prod)
  • Process isolation: Separate port, separate process
  • Env vars copied: Dev env vars are copied to prod on first deploy
  • Slot cost: Prod app counts as 1 app slot
  • gencow.json updated: prodApp field added automatically
  • Region placement: Omit --region to inherit the dev app home node region. Pass --region <slug> only for first prod app creation; existing prod apps are not moved. If no active node exists in that region, deploy fails instead of falling back.

Subsequent gencow deploy --prod deploys directly to the prod app.

Rollback

Roll back to the previous deployment:

gencow deploy --rollback          # Rollback dev app
gencow deploy --rollback --prod   # Rollback prod app
  • Code only: Restores the previous bundle (database is NOT rolled back)
  • Safe: Requires additive-only migrations (column additions, not deletions)
  • Bundle retention: Last 5 deployment bundles are kept on disk
  • Environment-aware: Rollback targets dev by default, use --prod for production
  🔄 롤백 완료! (1.2s)
  ▸ 롤백: #5 → #4
  ▸ 번들:  a1b2c3d4
  ▸ URL:   https://my-app-prod.gencow.app
  ⚠  코드만 롤백되었습니다. 데이터베이스는 변경되지 않았습니다.

Static Deployment (Frontend)

Deploy a built frontend to your dev environment. For new Gencow apps, prefer Vite + React; use Next.js only for existing Next.js projects or explicit SSR requirements:

# Build your frontend first
VITE_API_URL=https://my-app.gencow.app npm run build

# Deploy the build output only
gencow static dist/

Auto-Detection

If you don't specify a directory, Gencow auto-detects in this order:

  • dist/out/build/.next/out/
gencow static   # auto-detects dist/

Production Static Deploy (Startup / Enterprise)

gencow static --prod dist/

Guardrails

The CLI automatically warns about common issues:

  • API references in static files: If your build contains /api/query or /api/mutation and no backend is detected, Gencow warns that static hosting has no API server
  • Fullstack command: If you need backend + frontend together, use gencow deploy --static dist/

When you run gencow static from a frontend subdirectory, Gencow detects the parent backend root for gencow.json and gencow.config.js metadata. The build output directory is still resolved from the frontend directory where you ran the command.

For projects with both backend and frontend, gencow deploy --static automatically detects and deploys both:

# 1. Build frontend with the backend URL
VITE_API_URL=https://my-app.gencow.app npm run build

# 2. Deploy — backend is auto-detected and deployed first, then frontend
gencow deploy --static dist/

When gencow/ is detected in the current or parent directory, the CLI:

  1. Deploys the backend first
  2. Deploys the frontend static files
  3. Reports both URLs

If your API entry point includes defineApi({ crons }), deploy also packages schedule metadata so the platform scheduler can run the registered procedure.internal handlers. No extra cron transport configuration is required in app code.

To upload only frontend files, use the static-only command:

gencow static dist/

Public origins: *.gencow.app app origins and active custom domains are managed automatically. For external frontends, declare frontendOrigins in gencow.config.js and redeploy.

Public Origins for External Frontends

When your frontend is hosted outside Gencow, such as Vercel or Netlify, declare the exact browser origin in code:

export default {
  frontendOrigins: ["https://myapp.vercel.app"],
};
gencow origins list
gencow origins check https://myapp.vercel.app
  • Active custom domains are included automatically.
  • Wildcards and paths are rejected; use exact bare origins only.
  • gencow domain set myapp.com makes your dev frontend and API same-origin; use gencow domain set myapp.com --prod for the production app.

Environment Variables

By default, gencow env commands target the cloud app. For local development, use .env file directly.

Set Variables (Cloud)

# Single variable → cloud
gencow env set OPENAI_API_KEY=sk-...

# Multiple → cloud
gencow env set DATABASE_URL=postgres://... SECRET_KEY=abc123

Local dev: Edit .env file directly — gencow dev loads it automatically. See Local Development.

Idle apps: cloud env changes are stored immediately even when the app is idle. A running app hot-reloads them right away; an idle app picks them up on its next wake.

Self-hosted Platform Runtime Config

Self-hosted platform operators can keep admin-only settings that do not yet have a dashboard UI in a JSON file:

GENCOW_PLATFORM_CONFIG_FILE=/etc/gencow/platform-config.json

Use this for platform-owned document conversion and OCR settings. The file is read at platform startup, so restart the platform after changing it. For user-facing conversion API usage and routing behavior, see Document Conversion.

These settings must stay platform-side. Tenant app workers should not receive GENCOW_PLATFORM_CONFIG_FILE, GENCOW_DOCUMENT_*, PLATFORM_OPENAI_KEY, or PLATFORM_GOOGLE_KEY.

document.convert() safe auto uses local/self-hosted providers first. Gemini, OpenAI, OCR, and custom VLM fallback only run when the workflow request sets paidFallback: true or chooses a paid provider explicitly. Provider endpoints, credentials, prompts, model choices, and fallback caps are platform-owned configuration and should be managed through platform operations runbooks rather than tenant app environment variables.

List Variables

gencow env list           # Dev app env vars
gencow env list --prod    # Prod app env vars (Startup / Enterprise)

Remove Variables

gencow env unset OPENAI_API_KEY          # Dev app
gencow env unset OPENAI_API_KEY --prod   # Prod app (Startup / Enterprise)

Push Local .env to Cloud

gencow env push           # Push .env → dev app
gencow env push --prod    # Push .env.production → prod app

Security: Environment variables are encrypted at rest. .env is gitignored by default.

Requires: gencow.json must exist (created by gencow dev or gencow deploy). Must be logged in (gencow login).

Database Migrations

Gencow uses a local-generate-first migration workflow. The CLI generates SQL migration files locally, bundles them with your code, and the platform applies only the pending migrations on deploy.

How It Works

schema.ts  →  [deploy: drizzle-kit generate]  →  gencow/migrations/  →  [bundle]  →  [Platform: migrate()]
  1. gencow deploy (and gencow dev) automatically run npx drizzle-kit generate before bundling
  2. The generated gencow/migrations/ files are bundled into the deploy archive
  3. On the platform, drizzle-orm/migrator applies only the pending migrations using __drizzle_migrations history table

Interactive prompts supported: If drizzle-kit detects a column rename, it will ask you in the terminal. The prompt passes through (stdio: inherit).

⚠️ Generate failed? If drizzle-kit generate fails (e.g., missing drizzle.config.ts), the deploy continues with a warning. If gencow/migrations/ is still missing, the platform will skip schema migration.

Workflow

# First deploy — everything is automatic:
gencow deploy           # dev deploy
gencow deploy --prod    # production deploy
# 1. drizzle-kit generate runs locally → creates gencow/migrations/
# 2. gencow/ is bundled (including migrations/)
# 3. Platform applies pending migrations

# After schema.ts changes:
gencow deploy           # same flow — platform applies only the new migrations

# Want to preview what migrations will be generated?
gencow db:generate    # generate only, no deploy

Migration Output

When deploying, you'll see:

  ✓ Schema → migrations synced      ← drizzle-kit generate ran automatically
  ✓ Bundle created: 42.5 KB         ← gencow/migrations/ included in bundle

On the platform:

[provisioner] my-app: migrations applied ✓

drizzle.config.ts

The drizzle.config.ts in your project root controls migration generation:

import { defineConfig } from "drizzle-kit";

export default defineConfig({
    dialect: "postgresql",
    schema: ["./gencow/schema.ts", "./gencow/schema-auth.ts"],
    out: "./gencow/migrations",   // ← must be inside gencow/ to be bundled
    tablesFilter: ["!_system_*", "!_gencow_*"],
    ...(process.env.DATABASE_URL
        ? { dbCredentials: { url: process.env.DATABASE_URL } }
        : {}),
});

Important: The out path must be inside gencow/ so migrations are included in the deploy bundle. The default template sets this to ./gencow/migrations.

Platform-Owned Runtime Tables

Gencow reserves two internal table families:

  • _system_* — framework/system settings and storage metadata
  • _gencow_* — workflow runtime state (_gencow_workflows, steps, events)

Your app should treat them as read-only platform internals:

  • keep them excluded from Drizzle with tablesFilter: ["!_system_*", "!_gencow_*"]
  • never declare them in schema.ts
  • never ship migrations that alter or drop them

If these filters are missing, Drizzle can interpret platform tables as app schema drift and attempt rename/drop prompts during generate or push.

Migration Commands

gencow db:generate    # Generate SQL files from schema.ts (no DB connection needed)
gencow db:migrate     # Apply pending migrations to cloud DB
gencow db:push        # Instant schema sync without migration files (dev only)
Command When to use
db:generate After every schema.ts change
db:migrate Manually apply migrations to cloud DB
db:push Quick prototyping (local dev, no production migration tracking)

Platform behavior: The platform uses __drizzle_migrations table to track applied migrations. Running gencow deploy multiple times is safe — only new migrations are applied.

Dependencies

Platform Packages (Pre-installed)

The following packages are included in the cloud runtime — no npm install needed:

Package Description
@gencow/core Core framework (defineApi, procedure builders, scheduler, auth)
drizzle-orm ORM and query builder
better-auth Authentication system
postgres PostgreSQL driver
hono HTTP framework
ai, @ai-sdk/* AI SDK (OpenAI, Anthropic, Google)
zod Schema validation
esbuild Build tools

Third-Party Packages (Auto-installed)

Any additional npm packages in your package.json (e.g., langfuse, axios, cheerio) are automatically installed when you deploy:

gencow deploy           # dev
gencow deploy --prod    # production
#   📦 서드파티 패키지 감지: langfuse, cheerio
#      → 클라우드 배포 시 자동 설치됩니다.

Limits

Limit Hobby Startup Enterprise
Deploy bundle size 50 MB max (gencow/ + lockfiles) 200 MB max Custom
Installed node_modules 500 MB max after bun install 500 MB max Custom
Install timeout 60 seconds 60 seconds 60 seconds
  • On install failure: deployment continues but imports will fail at runtime
  • Heavy packages like puppeteer, sharp, tensorflow may exceed the 500MB limit — use lightweight alternatives

Blocked modules: child_process, vm, os, cluster, worker_threads are blocked for security. OS-level isolation is handled by cowbox (Landlock + seccomp-bpf + cgroups v2).

Function Execution Limits

Function Type Time Limit
query 30 seconds
mutation 30 seconds
httpRoute 5 minutes
cron / procedure.internal scheduled action 10 minutes

For long-running tasks, split work across multiple bounded calls. In Gencow Cloud, ctx.scheduler.runAfter() and runAt() use the BaaS Platform DB-backed durable scheduler:

  • the tenant app persists a scheduled job through the Platform
  • the Platform poller wakes the app when the job is due
  • app sleep or crash does not discard the callback
  • retries use bounded backoff before terminal failure handling

Local dev timers are in-memory, so restarting the local process can lose pending callbacks. Test app-sleep and crash-recovery behavior on the cloud path when the task is business critical.

Use this decision guide:

Work shape Recommended primitive
A mutation needs to continue with the next small batch ctx.scheduler.runAfter(0, "internal.step", args)
Something must run every hour/day/week cron with a procedure.internal handler
A process has approvals, signals, resumable state, or many named steps workflow()

Example deploy-safe batch continuation:

await ctx.scheduler.runAfter(0, "imports.processBatch", {
    importId,
    cursor: nextCursor,
});

Keep scheduled arguments compact and non-secret. Store large payloads in your database or storage, then pass IDs/cursors to the scheduled action.

CI/CD Deployment

For automated deployments from GitHub Actions, GitLab CI, etc.:

1. Create a Deploy Token

Go to Dashboard → Settings → Deploy Tokens → Create Token

2. Set as CI Secret

# GitHub Actions: Settings → Secrets → GENCOW_TOKEN
# GitLab CI: Settings → CI/CD → Variables → GENCOW_TOKEN

3. Use in CI Pipeline

# .github/workflows/deploy.yml
name: Deploy
on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: oven-sh/setup-bun@v1
      - run: bun install
      - run: npx gencow@latest deploy          # dev deploy
      # - run: npx gencow@latest deploy --prod  # production deploy
        env:
          GENCOW_TOKEN: ${{ secrets.GENCOW_TOKEN }}

When GENCOW_TOKEN is set, gencow deploy uses it instead of interactive login.

Custom Domains

Connect your own domain to your Gencow app. Custom domains are available on Startup and Enterprise plans.

# Set domain on the dev app
gencow domain set myapp.com
gencow domain set myapp.com --prod --dry-run
gencow domain set myapp.com --prod --yes

# Set domain on the production app
gencow domain set myapp.com --prod

# Check DNS/TLS status
gencow domain status
gencow domain status --prod

# Remove domain
gencow domain remove
gencow domain remove --prod

If your project has a production app and you want the public domain to serve production traffic, bind the domain to the production app with --prod. Without --prod, the CLI targets the dev app. If the domain is currently attached to the related dev app, --dry-run previews the move and --yes confirms it for scripts.

DNS Setup

For the lowest-friction setup, connect a www hostname and add a CNAME record pointing to the selected app target:

Type Name Value
CNAME www.myapp.com null-mint-9625-prod.gencow.app

Gencow treats a directly connected www hostname as canonical and does not redirect it to the apex.

For an apex domain, use an apex-compatible DNS record:

Type Name Value
ALIAS / ANAME / CNAME flattening myapp.com null-mint-9625-prod.gencow.app
A fallback myapp.com Gencow stable edge IP shown by the CLI

If myapp.com is active and www.myapp.com is not directly connected, www.myapp.com can fall back to the bare domain and redirect to myapp.com. Do not rely on that fallback for www-only domains.

TLS certificates are provisioned automatically via Let's Encrypt.

Deploy Commands Reference

Command Description
gencow login Authenticate via browser
gencow logout Clear saved credentials
gencow whoami Show current user info
gencow dev Real-time backend dev (watch + auto-deploy)
gencow static [dir] Deploy static files to dev
gencow static --prod [dir] Deploy static files to production (Startup / Enterprise)
gencow deploy Deploy backend to dev (one-shot)
gencow deploy --prod Deploy backend to production (Startup / Enterprise)
gencow deploy --static [dir] Deploy backend first, then static files
gencow deploy --rollback Roll back dev deployment
gencow deploy --rollback --prod Roll back production deployment
gencow deploy logs Follow server logs
gencow deploy status Check container status
gencow env list List cloud env vars
gencow env list --prod List prod app env vars (Startup / Enterprise)
gencow env set K=V Set cloud env var (hot-reload)
gencow env set K=V --prod Set prod app env var (Startup / Enterprise)
gencow env unset KEY Remove cloud env var
gencow env push Push .env to cloud
gencow env push --prod Push .env.production to prod app
gencow domain set Connect custom domain to dev app (Startup / Enterprise)
gencow domain set --prod Connect custom domain to production app (Startup / Enterprise)
gencow domain status Check domain DNS/TLS
gencow domain status --prod Check production domain DNS/TLS
gencow domain remove Disconnect dev custom domain
gencow domain remove --prod Disconnect production custom domain

Next Steps