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).
Examples show the command name for readability. For ad-hoc use, the recommended
runner is bunx gencow@latest; Node.js users can use npx gencow@latest.
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.
For publishing a frontend and connecting DNS/TLS, see the dedicated Hosting & Custom Domains guide.
Environments
Gencow BaaS provides two isolated environments:
| Dev | Production | |
|---|---|---|
| Domain | Separate *.gencow.app dev app |
Separate *.gencow.app production app |
| Access | All plans | Startup / Enterprise |
| Command | gencow dev / gencow deploy |
gencow deploy --prod |
| Database | Isolated app database | Separate isolated production database |
| Env vars | gencow env set KEY=VAL |
gencow env set KEY=VAL --prod |
Deployment target safety: A plain request to “deploy” targets the development app. Use
gencow deploy,gencow deploy --static dist/, orgencow static dist/without--prodduring development. Add--prodonly when the user or operator explicitly requests a production deployment. AI agents and automation must not infer production authorization from a generic request to deploy, publish, or release. If the target is unclear, stop before the production command and ask.
Self-hosted users: You manage your own deployment infrastructure.
gencow deployis not available for cloud targets. Connect to your own PostgreSQL viaDATABASE_URL.
Plan Limits
Public pricing plans are Hobby, Startup, and Enterprise. Enterprise is handled through a custom inquiry path.
| Plan | App slots | Realtime account/app safety | Production deploy | Custom domain |
|---|---|---|---|---|
| Hobby | 5 | 1,000 / 500 | Not available | Not available |
| Startup | 30 | 100,000 / 25,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.
The live plan policy is the source of truth for app slots. Public pricing reads the same current limit, and the platform enforces it for every app creation and ownership transfer. Audited platform-administrator exceptions are reserved for operations and do not change customer plan entitlements.
Realtime limits apply to concurrent WebSocket subscriptions. The first number is the account-wide cap; the second is the per-app noisy-neighbor safety cap. 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 loginThis opens your browser for Device Auth:
- A code is displayed in the terminal
- Browser opens to
gencow.app/cli-auth - Confirm the code in the browser
- Token is saved to
~/.gencow/credentials.json
Check Status
gencow whoami
# → Email, User ID, Token info, ExpiryLogout
gencow logoutDeploying 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 deployFor 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 devWhat Happens
- Creates app on platform (if first time) → assigns unique
appId - Bundles
gencow/+package.json+ lockfiles →tar.gz - Uploads bundle to platform
- Platform provisions: PostgreSQL database + isolated container
- Watches for file changes → auto-redeploys
- Saves
appIdtogencow.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 deployThis is useful for CI/CD pipelines or when you want a quick push without staying attached.
Production Deploy (Startup / Enterprise)
Production is a separate, live target. Run these commands only after an explicit
production request; a deploy token, CI environment, or --yes flag does not
grant production authorization by itself.
gencow deploy --prod
gencow deploy --prod --region krStartup / Enterprise only. Hobby plan users will see a blocking message with instructions to use
gencow deploy(dev) orgencow devinstead.
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-appvsapp_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:
prodAppfield added automatically - Region placement: Omit
--regionto 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)
- Schema compatibility required: Code rollback does not reverse an already-applied database migration
- Bundle retention: Last 5 deployment bundles are kept on disk
- Environment-aware: Rollback targets dev by default, use
--prodfor 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 bun 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)
Use the production form only when production was explicitly requested:
gencow static --prod dist/Guardrails
The CLI automatically warns about common issues:
- Missing site entrypoint: The selected output must contain a regular root
index.html. Compiler-only output such as a TypeScriptdist/is rejected before production target creation, fullstack backend deployment, or static packaging. The Platform responds with400 STATIC_SITE_ENTRYPOINT_REQUIRED, and the current frontend remains unchanged - API references in static files: If your build contains
/api/queryor/api/mutationand 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.
Static Redirects and Custom 404
Add a _redirects file to the deployed output when a static site needs clean
same-origin redirects, internal rewrites, or a terminal custom 404:
/old-guide /docs/guides/new-guide 301
/docs/guides/new-guide /docs/guides/new-guide/index.html 200
/* /404.html 404Gencow supports fixed same-origin absolute paths with status 200, 301,
302, 307, 308, or 404. Exact rules are evaluated in file order.
Trailing /* prefix rules are fallback rules and do not replace a concrete
deployed file. Targets containing an external origin, protocol-relative URL,
query, fragment, backslash, or traversal segment are ignored. Runtime-reserved
paths such as /api, /platform, /internal, and /ws never enter static
redirect or SPA fallback handling.
This is a deliberately safe subset of common _redirects syntax. Wildcard
capture substitution and cross-origin redirects are not supported. For a
terminal custom not-found page, deploy the target HTML file and put the
/* /404.html 404 rule last.
Fullstack Deployment (Recommended)
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 bun 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:
- Deploys the backend first
- Deploys the frontend static files
- 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.appapp origins and active custom domains are managed automatically. For external frontends, declarefrontendOriginsingencow.config.jsand 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.commakes your dev frontend and API same-origin; usegencow domain set myapp.com --prodfor the production app.
Environment Variables
By default, gencow env commands target the cloud app. For local mode,
frontend build variables belong in root .env and backend secrets belong in
gencow/.env by default.
Set Variables (Cloud)
# Single backend variable → cloud
gencow env set MY_BACKEND_API_KEY=replace-me
# Multiple backend variables → cloud
gencow env set EMAIL_PROVIDER_KEY=replace-me WEBHOOK_SECRET=replace-meLocal mode: Edit
gencow/.envfor backend secrets. Frontend tools read root.env. Defaultgencow devtargets cloud and does not upload either file 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.
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 Backend Env to Cloud
gencow env push # Push gencow/.env → dev app by default
gencow env push --prod # Push gencow/.env.production → prod app by default
gencow env push --file ./config/backend.env # Push an explicit backend env fileSecurity: Environment variables are encrypted at rest. Root
.envandgencow/.envshould both stay gitignored.Requires:
gencow.jsonmust exist (created bygencow devorgencow deploy). Must be logged in (gencow login).
Database Migrations
Gencow uses a local-generate-first, versioned migration workflow. The CLI generates Drizzle SQL locally, bundles the immutable migration history with your code, and the platform applies only the pending plan.
How It Works
schema.ts
→ Drizzle Kit generates gencow/migrations/
→ Gencow verifies the app target and immutable bundle
→ Drizzle ORM migrator applies pending files
→ PostgreSQL decides SQL/constraint/transaction results
→ Gencow verifies the exact journal outcomegencow deployandgencow devrun the supported Drizzle Kit generator before bundling when a database schema source exists.- The generated
gencow/migrations/files are included in the archive. - Gencow validates bundle integrity, app/database ownership, protected platform objects, and pending history.
- Drizzle ORM applies pending migrations and records them in the Drizzle journal.
- PostgreSQL enforces SQL syntax, constraints, locks, transactions, and rollback.
- Gencow verifies the post-execution journal before reporting success.
Interactive prompts supported: If drizzle-kit detects a column rename, it will ask you in the terminal. The prompt passes through (
stdio: inherit).
No database schema? If no schema source exists, migration generation and tenant database access are skipped. If canonical generation succeeds with no schema changes, the deployment continues as a verified no-op.
Generate failed? A schema import, config, or generator error stops packaging before upload. Run
gencow doctorfor a source/history preview, then fix the reported cause and rungencow db:generate. Existing migration files are never used to hide a failed generation.
See Database Migrations for the full responsibility model, db:check, failure states, constraint changes, and retry rules.
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
# Generate and review without applying
gencow db:generate
# Inspect the exact cloud plan without applying DDL
gencow db:check
gencow db:check --prodMigration Output
When deploying, you'll see:
✓ Schema → migrations synced ← drizzle-kit generate ran automatically
✓ Bundle created: 42.5 KB ← gencow/migrations/ included in bundleOn 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
outpath must be insidegencow/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:check # Inspect the DEV cloud plan without applying DDL
gencow db:check --prod
gencow db:push --existing-bundle # Apply the committed plan already inspected by db:check
gencow db:push --prod --existing-bundle # Same immutable path for production, with confirmation
gencow db:migrate # Apply pending migrations through the configured project/server DB runtime| Command | When to use |
|---|---|
db:generate |
After every schema.ts change |
db:check |
Generator-free, project-unchanged cloud preflight for manifest, history, pending plan, and platform policy |
db:push |
Generate and apply a plan by default, or apply the checked committed plan with --existing-bundle |
db:migrate |
Direct project/server migration flow; it does not replace managed cloud db:push controls |
Platform behavior: The platform uses
__drizzle_migrationsto track exact migration names and hashes. If execution returns an error, Gencow verifies the journal before deciding whether the transaction rolled back, already committed, or needs manual recovery. Running the same applied immutable bundle again is a no-op.Generator identity and version are audit metadata, not an allowlist. The platform verifies the bundle protocol, ordered SQL hashes, ledger, dialect/policy, lock, atomicity, and rollback contract instead.
db:checkscope: It is a read-only preflight, not a temporary PostgreSQL execution rehearsal. Test high-impact migrations against representative PostgreSQL data and traffic before Production.
Dependencies
Platform Packages (Pre-installed)
The following packages are included in the cloud runtime — no manual install is 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 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,tensorflowmay exceed the 500MB limit — use lightweight alternatives
⛔ Blocked modules:
child_process,vm,os,cluster,worker_threadsare 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_TOKEN3. 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: bunx gencow@latest deploy # dev deploy
# - run: bunx gencow@latest deploy --prod # production deploy
env:
GENCOW_TOKEN: ${{ secrets.GENCOW_TOKEN }}When
GENCOW_TOKENis set,gencow deployuses it instead of interactive login.
Custom Domains
Custom domains are available on Startup and Enterprise plans. The complete
Dashboard, CLI, DNS, apex/www, automatic TLS, rebind, and troubleshooting
workflow now lives in
Hosting & Custom Domains.
gencow domain set www.myapp.com --prod
gencow domain status --prodUse --prod when the public domain should serve the production app. Without it,
the CLI targets the dev app.
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 the backend env file to cloud |
gencow env push --prod |
Push the backend production env file 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 |
Cloudflare custom domains should use DNS only or Full (strict); Full is supported, while Flexible and Off are not. Gencow activates a domain only after DNS and origin HTTPS readiness checks complete. Active domains redirect safe HTTP requests to HTTPS, and Flexible traffic receives a configuration error page without reaching the tenant app.
Next Steps
- AI Engine — Add AI capabilities
- Components —
gencow addcomponents - CLI Reference — All CLI commands