Forge CLI documentation
Quickstart
Three commands, about sixty seconds. The CLI is a single ~6 MB binary with no daemon and no background agent — install it, and everything else happens over stdout.
1
Install the CLI
Via npm, Homebrew, or the curl installer — all three land the same binary on your PATH.
$ npm i -g forge
# or: brew install forge-dev/tap/forge
# or: curl -fsSL https://get.forge.run | sh
$ forge --version
forge 3.2.0 (darwin-arm64)
2
Link your project
Run it in your repo root. Forge sniffs the runtime, entrypoint and port, then writes a project ID to .forge/project.json — commit it or don't, it holds no secrets.
$ forge init
✓ detected node 22 · express · port 3000
? project name (api-service):
? primary region (iad1):
✓ linked project_9f3c1a · .forge/project.json written
3
Ship it
Build, upload, provision, route — one command, one URL. Traffic only shifts after the healthcheck passes, so a failed deploy leaves production untouched.
$ forge deploy
→ resolving project… api-service (node 22)
✓ build complete in 2.4s · 142 modules
↑ uploaded 3.1 MB in 1.8s
✓ deployed https://api-service.forge.run · 9.2s
note: the first deploy of a project provisions the runtime and takes longer than the 9.2s benchmark figure — that number is a cold rebuild of an already-provisioned service. Warm deploys with a primed cache land around 3.9s.
Command index
The full top-level tree. Every command accepts --json for machine-readable output and --project to override the linked project.
forge init
Detect the runtime and link the working directory to a project.
forge deploy
Build, upload and route a new immutable version to production.
forge preview
Deploy the current branch to an isolated preview URL.
forge rollback
Shift traffic back to a previous version, usually in under a second.
forge env
Set, list and remove encrypted environment variables per environment.
forge logs
Stream or tail structured logs from any version or region.
forge db
Provision managed Postgres and fork it for preview branches.
forge export
Emit the generated Dockerfile and compose file — your exit hatch.
forge deploy
Builds the project, uploads the artifact, provisions runtimes in every configured region and shifts traffic once the healthcheck returns 2xx. Each deploy is an immutable version tagged v1, v2, … — nothing is ever overwritten in place.
$ forge deploy [--prod|--preview] [flags]
Exit codes
Deploys are safe to chain with && — the command exits non-zero on any failure, including a healthcheck that never goes green.
0 — deployed, traffic shifted, healthcheck green.
1 — build failed. Production is untouched; failing log lines print to stderr.
2 — healthcheck never passed within the timeout. The new version is left parked, not routed.
3 — auth or project resolution failed. Check $FORGE_TOKEN and .forge/project.json.
forge rollback
Shifts traffic to an earlier immutable version. Nothing rebuilds — the artifact is already provisioned, so this is a routing change measured in hundreds of milliseconds.
$ forge rollback --to v41
✓ traffic shifted to v41 in 0.8s
v42 kept for diffing: forge diff v41 v42
Omit --to and Forge steps back exactly one version. Add --list to print the retained versions with their deploy messages and timestamps before choosing.
retention: the free tier keeps the last 5 versions, Team keeps 50, Enterprise is configurable. Rolling back to an evicted version rebuilds from source instead of routing — still correct, just not instant.
forge env
Environment variables are encrypted with AES-256 at rest, scoped per environment, and injected into the runtime at boot. They are never written to disk on your machine and never appear in build logs.
$ forge env set STRIPE_KEY --prod
? paste value (hidden): ••••••••
✓ encrypted & stored prod only
$ forge env ls --prod
STRIPE_KEY set 2d ago ••••••••
DATABASE_URL set 2w ago ••••••••
LOG_LEVEL set 2w ago info
Values marked non-secret at set time (like LOG_LEVEL above) render in plaintext in env ls; everything else stays masked and can only be overwritten, never read back.
forge.toml
Optional. Forge detects the runtime, port and build step on its own — reach for a config file only when you outgrow the defaults. Every key below has a working default, and the whole file is usually under fifteen lines.
# forge.toml — every key optional
runtime = "node22"
regions = ["iad1", "fra1", "sin1"]
healthcheck = "/healthz"
[build]
command = "npm run build"
output = "dist"
[scale]
min = 1
max = 12
# monorepo: one block per service
[[service]]
name = "worker"
root = "apps/worker"
CI & tokens
Forge is non-interactive whenever a token is present, so it drops into any pipeline that can run a shell command. Mint a scoped token with forge token create, store it as a secret, and let your existing CI keep doing what it's good at — tests.
# any CI runner that can run a shell step
$ npm i -g forge
$ forge deploy --prod --token $FORGE_TOKEN
✓ deployed → https://api-service.forge.run · 9.2s
This works the same way under GitHub Actions, GitLab CI, CircleCI, Jenkins or a cron job on a box you own — Forge only needs a shell and outbound HTTPS. Because the command exits non-zero on a failed healthcheck, your pipeline goes red without any extra wiring.
- Tokens are scoped per project and per environment; a preview token cannot touch production.
forge token ls shows last-used timestamps so you can revoke what's gone stale.
- Add
--json in CI to capture the deploy URL for a downstream step.
tip: keep tests in CI and let Forge own the deploy step. Teams that move the whole pipeline into forge deploy usually end up rebuilding a test runner inside it.