---
title: coduck.json reference
description: The project config file — every field, how it drives a deploy, and the reserved env keys.
category: reference
order: 1
agent: "Authoritative reference for coduck.json — the per-project config that drives every deploy. Fields: dir, runtime, install, build, preStart, start, port, instanceSize. Re-read on EVERY deploy (edit + push to change). Also lists reserved env keys and the deploy phase order. Read this before debugging why a build/start/migration didn't run as expected."
---

# coduck.json reference

`coduck.json` is the source of truth for how CoDuck builds and runs your project.
`coduck create-existing` writes it; you can edit it by hand. It is **re-read on
every deploy** — change a command, `coduck push`, `coduck deploy`, and the new
command runs. (Earlier behavior baked config at create-time; that is no longer
the case.)

```jsonc
{
  "projectId": "ff0c35e6-…",     // set by create-existing; don't change
  "name": "my-app",
  "dir": "./",                    // source root to push + run (see "Monorepos")
  "runtime": "node20",           // node20 | node22 | static
  "install": "npm ci",           // optional — default: npm install --include=dev
  "build": "npm run build",      // optional — default: npm run build
  "preStart": "npm run migrate", // optional — runs after build, before start
  "start": "node server.js",     // optional — default: npm start
  "port": 3000,                  // informational; bind process.env.PORT (see below)
  "instanceSize": "small"        // small | medium | large
}
```

## How each field drives the deploy

A deploy runs these steps in order, in your container:

1. **install** — your `install`, else `npm install --include=dev --no-audit --no-fund`.
   (Dev deps are installed by default because build tools like Tailwind/PostCSS
   live there. If you override with `npm ci`, note `NODE_ENV=production` is set, so
   add `--include=dev` yourself if your build needs dev deps.)
2. **prisma generate** — only if `prisma/schema.prisma` exists *and* declares
   `model`s. Skipped otherwise.
3. **build** — your `build`, else `npm run build`.
4. **schema bootstrap** — if `prisma/schema.prisma` declares models *and* you have
   **no** `preStart`, CoDuck runs `prisma db push`. If you set `preStart`, CoDuck
   does **not** auto-push — you own schema setup (see [Database](/docs/database/overview)).
5. **preStart** — your `preStart` (e.g. `prisma migrate deploy`, `drizzle-kit push`,
   `node scripts/migrate.js`). The right place for migrations.
6. **start** — your `start`, else `npm start`.

**Skip a step** by setting it to `""` (empty string). E.g. a static/no-build app:
`"build": ""`.

## Ports — bind `process.env.PORT`

CoDuck assigns the port and injects it as `PORT`. **Your app must listen on
`process.env.PORT`**, not a hardcoded value. The `port` field in `coduck.json` is
informational only — if your app hardcodes `3000` but CoDuck health-checks the
assigned port, the deploy fails with "responding on :3000 but health-checking
:<port> — bind process.env.PORT".

## Instance size

`instanceSize` (or `coduck deploy --size`) selects container resources. The CLI
flag wins; otherwise `coduck.json` is used; default `small`.

| Size | Memory | CPU | Plan required |
|---|---|---|---|
| `small` | 1.5 GB | 1.0 | any paid plan |
| `medium` | 2.5 GB | 2.0 | Pro or Studio |
| `large` | 4 GB | 3.0 | Studio |

A request above your plan is clamped down (and the deploy tells you). The Node
build-heap ceiling auto-scales to ~80% of the tier, so heavy client builds
(three.js, remotion, recharts) that OOM on `small` usually succeed on `large`.

## Monorepos / subdirectory source

`dir` can point at a subdirectory (e.g. `"./web"`) while `coduck.json` stays at the
repo root. The CLI uploads the repo-root `coduck.json` (and root
`.gitignore`/`.coduckignore`) alongside the subtree so the server still sees your
config. **This requires `@coduckai/cli` ≥ 0.1.11** — older CLIs pushing a subdir
silently omitted `coduck.json`, so declared commands were ignored. Run
`coduck --version` and upgrade if needed.

One CoDuck project = one container. For a separate frontend + backend, wrap them
in a single `start` (e.g. `concurrently`) or create two projects.

## Reserved environment variables

CoDuck injects these into every container and **rejects them on write** (setting
them has no effect — platform values win). The live, authoritative list is
`coduck env reserved`.

| Key / pattern | What it is |
|---|---|
| `DATABASE_URL` | Pooled Postgres connection string (see [Database](/docs/database/overview)). |
| `DIRECT_URL` | Direct (non-pooled) Postgres URL for migrations/`prisma db push`. |
| `PORT` | The port to listen on. Bind `process.env.PORT`. |
| `NODE_ENV` | Set to `production` in the container. |
| `HOME` | Container home dir. |
| `CODUCK_*` | All CoDuck-managed keys (`CODUCK_API_KEY`, `CODUCK_AUTH_KEY`, `CODUCK_PROJECT_ID`, `CODUCK_API_URL`, `CODUCK_STORAGE_DIR`, …). |
| `NEXT_PUBLIC_CODUCK_*` | Browser-exposed CoDuck values (project id, auth/api URLs, subdomain). |

Stripe keys (`STRIPE_SECRET_KEY`, `STRIPE_PUBLISHABLE_KEY`,
`NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY`) are injected **only when you connect
Stripe** — until then you may set them yourself.

`coduck env import .env` skips reserved/invalid keys with a warning and imports
the rest, so a `.env` containing `DATABASE_URL` won't abort the whole import.

## Next

- [Deploying](/docs/projects/deploy) — the deploy lifecycle + failure messages.
- [Import an existing project](/docs/projects/create-existing)
- [Database](/docs/database/overview) — the managed Postgres starts empty.
