Forgeon Docs

Documentation, guides, and patterns to help you build on Forgeon — from your first deploy to running serious infrastructure.

Forgepacks Builds

Build using a Forgeon Template (pre-wired build + runtime rules). Best for monorepos, non-standard apps, or when you want repeatable “golden paths”.

Templates are Forgeon’s opinionated build recipes: a curated set of install/build/start rules (and sometimes OS deps) that produce predictable results.

If Nixpacks is “auto-detect” and Buildpacks is “standard ecosystem,” Templates are: “don’t guess—do exactly this.”

Pick Template when you want repeatable deployments for a framework, monorepo, or stack that needs specific wiring.

When to use Template

Use Templates when:

  • Your project is a monorepo (apps in subfolders)
  • You need a known-good recipe (Node + Bun, Next.js, Hono, Django, etc.)
  • Your runtime needs extra wiring (workdir, custom start, build outputs)
  • You want “golden path” deployments across many repos

Avoid Templates when:

  • Your app is simple + standard → use Nixpacks or Buildpacks
  • You already have a production Dockerfile → use Dockerfile flow instead

What a Template controls

A Template can define:

  • workdir (build from apps/web, not repo root)
  • install/build/start commands
  • runtime port + host binding rules
  • required environment variables
  • optional OS packages (if your platform supports them)

In other words: Template = Build Plan + Runtime Contract.

Required runtime contract (same as all Forgeon builds)

Your app must:

  • Bind to 0.0.0.0
  • Use the injected PORT
  • Stay alive as the main process (don’t daemonize and exit)

If your server binds to 127.0.0.1, it will work locally and fail remotely. Bind to 0.0.0.0.

This is how you tell Forgeon: “Use Template X, and here’s the exact behavior.”

.forgeon.json
{
  "builder": "template",
  "template": {
    "id": "node-bun-web",
    "workdir": ".",
    "install": "bun install --no-save",
    "build": "bun run build",
    "start": "bun run start"
  },
  "runtime": {
    "internalPort": 3000,
    "healthcheckPath": "/healthz"
  }
}

Template fields explained

  • template.id — which Forgeon recipe to use
  • template.workdir — where to run commands (great for monorepos)
  • install / build / start — explicit commands (no detection)
  • runtime.internalPort — internal port your app listens on

Monorepo example

Repo structure:

example monorepo
$apps/web(Next.js)
$apps/api(Hono)

Deploy apps/web as a service:

.forgeon.json (apps/web)
{
  "builder": "template",
  "template": {
    "id": "nextjs-web",
    "workdir": "apps/web",
    "install": "bun install --no-save",
    "build": "bun run build",
    "start": "next start -p $PORT -H 0.0.0.0"
  },
  "runtime": { "internalPort": 3000 }
}

Deploy apps/api as a second service with a different template/workdir.

Environment variables (Template builds)

Templates can ship defaults, but you can still override env vars in Forgeon:

  • Project → Environment Variables
  • Service → Env Overrides

Common ones:

  • PORT (injected)
  • APP_HOST (usually 0.0.0.0)
  • any framework-specific settings (DATABASE_URL, JWT_SECRET, etc.)

Template vs Nixpacks vs Buildpacks

Nixpacks: fastest auto-detect + easy overrides


Buildpacks: standardized ecosystem, Heroku-like behavior


Template: explicit recipe, best for monorepo + no guessing

Debugging Template deployments

Runtime fails with “pull access denied … run:latest”

That means Forgeon tried to boot a placeholder image instead of your built artifact.

Most common causes:

  • Build step didn’t produce/publish an image
  • The boot request had image_ref missing/empty
  • A code path overwrote the real image ref during normalization

What to check:

  • Build logs: confirm the final image tag and digest
  • Deployment DB record: confirm image_ref is present
  • Runtime logs: confirm docker run ... REAL_IMAGE_REF

Commands succeed locally, fail on Forgeon

Likely causes:

  • workdir is wrong (monorepo)
  • build output path differs (dist, .next, build)
  • your start command is dev-mode (e.g., bun run dev)
  • app binds to localhost

Best-practice recipes

Next.js (production)

Use real production start:

  • build: next build
  • start: next start -p $PORT -H 0.0.0.0

API services (Node/Bun)

  • start: bun run start (must bind to 0.0.0.0:$PORT)

Vite frontend

Prefer Template → Static Deploy if possible:

  • build: vite build
  • publish directory: dist/

If your app is purely static, don’t pay the “runtime tax.” Deploy static.

  • Always set template.workdir explicitly for monorepos
  • Never use dev commands for production runtimes
  • Always bind to 0.0.0.0 and use PORT

Templates are the “chef’s menu.” You can still ask for changes—just don’t argue with the oven temperature.