Platform
Hosting & deploysPush to a branch, get a live linkDatabaseReal Postgres, one per appSign-inEmail and Google, users includedFile storageUploads, photos and documentsAI gatewayEvery model, with a hard spend cap
Resources
DocsGuides written for people who are newFor AI agentsClaude Code, Cursor, MCP, JSON CLICompareCamplax beside the stack it replacesPricingFrom $25/month plus prepaid creditsSupportA person answers, not a queue botPoliciesTerms, privacy and how we handle data Get started
Open in ChatGPT Open in Claude Last updated 2026-11-07

Hosting and deploys

Connect a repo, push to deploy, preview every branch, and roll back in one step.

Every Camplax project is a live app with its own URL from the first deploy: https://<slug>.camplax.app, with https handled for you. You get there by connecting a GitHub repo — or by shipping from the CLI.

Connect a repo

In the console, open your project and go to Deploys, then connect the GitHub repo — this installs the Camplax GitHub App on the account or organisation that owns it. After that, a push to the default branch builds and deploys production. There is no pipeline file to write and no separate CDN to point at anything.

A few switches on the git settings change what a push does:

  • Auto-deploys can be paused — pushes stop queueing deploys, while camplax deploy still works.
  • [skip ci] in a commit message skips the deploy for that commit.
  • Verified commits — when required, a push whose commit GitHub cannot verify is skipped rather than built.
  • Default branch, root directory and Git LFS are set under Settings → Git.

Commit status is reported back to GitHub, so a deploy's progress is visible on the commit itself.

Deploy from the terminal

camplax deploy

This queues a deploy of the connected repository and prints the deploy id. Useful flags:

camplax deploy --branch feature-x   # deploy a specific branch
camplax deploy --json               # machine-readable output for scripts and agents

The branch decides the environment: the project's default branch deploys to production, any other branch deploys to a preview. Add --json and the output is JSON with a deployId, status, branch and environment.

The same call exists on the API — POST /v1/projects/:slug/deploys with { "branch", "environment", "commitSha" }, using a project key with the deploy scope or your CLI sign-in.

If the repo has a camplax.json, it is the source of truth for the project — name, region, build commands, env var names, database options, cron and domains. camplax deploy tells you when it is using one. Once a production deploy is live, Camplax also reconciles the file's declarative sections — cron, flags, realtime channels and topics, buckets — from the code that deploy shipped, whether it came from GitHub or an upload: creates and updates only, it never deletes anything, and each version of the file is applied once.

Deploy a folder without GitHub

camplax deploy --local

Run from the project directory, this packs the folder into a tarball, uploads it, and queues a deploy that builds the upload instead of a repository. A repo never has to be connected — a fresh folder works, which is also how a coding agent can ship what it just wrote without setting up GitHub first.

What the upload carries is decided on your machine: .gitignore and .camplaxignore are honoured (.camplaxignore adds rules on top), and .git, node_modules, .camplax and .env* files are always left out — secrets live in the project's env vault, not in source uploads. The server secret-scans the archive anyway before storing it, caps it at 50 MB, and the builder fetches it through a token that only works for that one deploy.

--local follows the same branch rule: the default branch deploys to production, any other branch to a preview. The two API steps it performs are PUT /v1/projects/:slug/source (gzipped tar body, returns sourceKey) then POST /v1/projects/:slug/deploys with { "branch", "environment", "sourceKey" }.

A production deploy that names no source on a project with no repo — Deploy now, a retry, a deploy hook — rebuilds the upload production is serving, so redeploying an uploaded app never needs the folder again.

Unchanged source does not rebuild. When camplax deploy --local ships a folder byte-for-byte the same as one already built for that environment, and nothing else that goes into the build changed since — your build-time and public variables, the app's address, the root directory, Git LFS — the deploy releases that stored build instead of building again. Redeploy, deploy hooks and promoting a preview always build, so a CMS publish that calls a hook gets fresh content, and a build that reads your database always builds too. If production is serving a build from a repo whose link was removed, nothing is rebuilt: the deploy fails and asks you to connect a repository, rather than shipping an older upload.

Starter templates without GitHub

Pick a starter in the console with no GitHub connected and it still goes live: Camplax uploads the starter's code itself, exactly as --local would, and the first deploy builds it. Redeploys and rollbacks work the same as for any upload.

Connect GitHub later from the project and Camplax makes a private repo holding the same starter code; pushes to it deploy from then on. If you have already shipped your own changes with camplax deploy --local, Camplax does not make a repo from the starter — that repo would replace your changes on the next build. Push your code to a GitHub repo yourself and pick it under Deploys.

What a deploy does

A deploy moves through queued → building → deploying → ready (or failed). The queue serialises production: only one production deploy runs at a time, and a second request gets a production_deploy_busy error to retry once it finishes. Pushes to the same preview branch cancel the older queued or building preview, so the newest commit is the one that ships.

A build produces a stored artifact; a rollout then points the app's compute at that artifact and waits for it to answer healthy — including its TLS certificate — before traffic moves and the deploy reports ready. A failed build leaves production untouched.

How the build runs

  • Node version. Camplax reads build.node in camplax.json, then .nvmrc, .node-version, volta.node and engines.node. A range like >=18 <21 gets the newest long-term-support release that fits it (Node 20 here), not the newest Node. With nothing set, builds use Node 24.
  • Package manager. The lockfile decides: pnpm, Yarn, Bun or npm. Without a packageManager field, pnpm runs at the version that wrote the lockfile, and Yarn 2+ repositories install with yarn install --immutable. Bun repositories install with Bun and run the build script with Node.
  • Native packages on pnpm 11+. Common native packages (esbuild, sharp, Prisma, bcrypt, better-sqlite3 and similar) may run their install scripts. Any other package that needs one has to be allowed in the repository's pnpm-workspace.yaml.
  • Size. A deploy is at most 300 MB once compressed. A bigger build fails with its size and the limit in the build log. Most of a big build is usually node_modules: put packages only the build needs in devDependencies (they are left out of the deploy), and keep large media out of the repository — put it in object storage.
  • Memory. The build gets 4 GB, and Node may use three quarters of it for its heap. To choose your own, set NODE_OPTIONS=--max-old-space-size=... in the project's env vars.
  • Cache. Package downloads and .next/cache are kept between builds of the same environment, so a second build is faster. The cache is only a speed-up: if it is missing, the build simply starts cold.
  • Git. The builder checks out the commit with its last 10 commits of history and fetches submodules. Git LFS files are fetched only when Git LFS is on under Settings → Git. It is off for new projects, because every build would otherwise spend the repository's GitHub LFS bandwidth.
  • Release step (migrations). Set build.release in camplax.json, for example "release": "npx prisma migrate deploy". It runs after a successful build and before any traffic moves, with this environment's own database, so a preview migrates the preview's database. If it fails, the deploy fails and the running version keeps serving. Keep migrations out of the build script: there they change the database before the new version is known to work.
  • Moving from Vercel. Camplax does not read vercel.json. The build log lists what it found: cron jobs (with the cron block to paste into camplax.json — camplax init copies them for you; Camplax calls them with a signed POST, so accept POST and check the call with verifyCronRequest instead of CRON_SECRET), redirects, rewrites and headers (move them to next.config in a Next.js app, or into your server), and packages that need a Vercel service, such as @vercel/kv or @vercel/blob, with what to use instead.
  • Headless browsers. Puppeteer and Playwright cannot run inside a Camplax app: the browser they download during the build is not part of the deployed app, and the app's sandbox has no Chrome libraries. To take screenshots or render PDFs, call a hosted browser service over HTTPS instead.

Build minutes

Each plan includes build minutes every month, shared by all your projects: 1,000 on Indie, 3,000 on Pro and 10,000 on Scale. The Usage page shows how many you have used.

  • What counts. A build's time on the build machine, to the second, from when it starts to when it ends. Builds that fail or are cancelled count too, because they used the machine. A build can run for at most 60 minutes.
  • What does not. Rollbacks, restarts and a deploy that releases a build Camplax already has (an unchanged upload) use no build minutes. There is no daily limit on deploys.
  • Past the plan. More minutes come out of your account's prepaid credits, 1,000 minutes for $5, bought before the build that needs them starts. Nothing is bought while the plan's own minutes are left, and a build that starts inside them and runs past the end is not charged for the extra. You get an email the first time this happens in a month.
  • When the credits run out. A build still starts while you have minutes left, and minutes it runs past them are free. Once they are used up, new builds are refused: the deploy fails with "This month's build minutes are used up" and you get an email. Your live apps keep running, and rollbacks and restarts still work. Add credits on the Billing page and the next push or Redeploy builds at once; otherwise builds start again on the 1st (UTC).
  • Nothing is billed later. Minutes are always paid for before they are used, and what a block does not use by the end of the month is not carried over.

Rollback

camplax rollback

With no argument this re-promotes the previous production build — the artifact already exists, so nothing is recompiled. Pass a deploy id to pick a specific one:

camplax rollback dep_abc123

The same rollback is a click on the deploy's row in the console, and POST /v1/projects/:slug/deploys/:id/rollback on the API. Either way, traffic moves back when the old build reports healthy — there is no rebuild and no downtime window. Until then the current build keeps serving. The CLI and the console wait for that and say Rolled back only once the old build is live; if it never gets healthy, they show why and production stays on the build it had.

On the API, the rollback answers as soon as Camplax has started it, with an operationId. Ask GET /v1/projects/:slug/deploys/:id/rollback/:operationId how it is going: {"state":"rolling_back"}, {"state":"live"}, or {"state":"failed","message":"…"}.

Once it is serving, GET /v1/projects/:slug/deploys/current names that older deploy as the live one, and a later Deploy now or deploy hook rebuilds what is serving, not the build you rolled back from.

How far back you can go

Builds follow your plan's history window, the same as backups:

PlanRoll back to production deploys from
Indiethe last 7 days
Prothe last 14 days
Scalethe last 30 days

The build that is live and the production build before it are always kept, however old, so one step back always works. After a rollback, the build you rolled back from is kept too, so you can go forward again.

Older builds are deleted along with their build logs. They stay in the deploy history, but without Roll back to this; asking for one anyway answers 409 historical_rollback_unavailable with how long your plan keeps builds. To ship that code again, redeploy its commit — it builds fresh. Build logs of failed deploys stay for the same window.

A preview keeps only its newest ready build. When a branch's preview is torn down, its builds and logs go with it.

Rollback restores the build artifact, not the database.

Run a command once

Some jobs happen once, not on every deploy: filling a new database with npm run seed, a one-time data fix, node scripts/backfill.js. Run them on the live app from Deploys → ⋯ → Run a command, or from a terminal:

camplax run npm run seed
camplax run --env feat-login -- node scripts/backfill.js
  • Where it runs. On Camplax, in a sandbox of its own next to your app, from the release the environment is serving, with the app's variables and its own database — production's for production, a preview's for that preview. Visitors are never touched: the command does not run inside the app that answers them, and it never receives traffic.
  • What it can reach. The same as your app: its own database, its own environment and the public internet over HTTPS. Never another app or another environment.
  • Output. It streams into the console or your terminal as it runs, and stays in Logs under the run's request id (run:…). camplax run exits with the command's exit code.
  • Who can run one. Only the project's Owner, and only signed in as themselves: the console asks you to confirm it is you, and camplax run uses your camplax login. A project key never can. Production also asks you to type the project's name.
  • Limits. A command stops after 15 minutes and is never retried, so make it safe to run again if it is stopped half-way. If the server running your app cannot be reached within 5 minutes, the command is not run at all rather than run late. It gets up to 512 MB of memory out of your plan's memory pool (256 MB when the pool is nearly full; if there is no room at all, it says so and what to free) and one CPU. One command runs at a time per account, and a project can start 20 an hour — ask Support if you need more.

Deploy hooks

A deploy hook is a secret URL that queues a deploy — the way to trigger one from a CI job, a headless CMS publish button, or anything that can POST:

curl -X POST https://camplax.dev/v1/hooks/<token>
# → 202 { "ok": true, "deployId": "dep_…" }

Create one with POST /v1/projects/:slug/deploys/hooks — a project API key cannot mint hooks, so use a signed-in user token. The token (cxhook_…) is shown once — Camplax stores only its hash. A hook can be bound to a branch; unbound hooks deploy the default branch. Delete the hook to revoke it.

Anyone holding the URL can deploy, so treat it like a password. If you want more than a secret URL, sign the request body: send X-Camplax-Signature: sha256=<hex> (or X-Hub-Signature-256, the GitHub convention) where the hex is the HMAC-SHA256 of the raw request body, keyed by the hook token. A header that does not verify fails the request.

Deploy logs

  • In the console, every deploy row opens its full build log — GET /v1/projects/:slug/deploys/:id/log returns it as plain text, and …/deploys/latest/log always names the newest production attempt.
  • camplax logs shows recent build, request and database logs; camplax logs --tail streams them.
  • Failure emails carry a public link of the form GET /v1/deploy-logs/:slug/:deployId?token=… — a bearer token that reads exactly that one build log, so a teammate can see the failure without a console sign-in. The link is marked noindex and expires with its token.

Build, request and error logs sit on one timeline in the console, so "did it break in the build or at runtime" is one look, not two dashboards.

The GitHub integration, in detail

GitHub calls POST /v1/webhooks/github. Each delivery is verified against the app's webhook secret (X-Hub-Signature-256, HMAC-SHA256) and deduplicated by delivery id, so retries cannot queue a deploy twice. Two event types matter:

  • push — a push to the default branch queues a production deploy; a push to any other branch queues a preview. Re-deliveries of a commit that already has a production deploy are deduplicated, and a stale delivery that no longer matches the branch head is dropped.
  • pull_request — opening, reopening or updating a PR creates the branch's database and deploys a preview; closing it tears the preview down — compute, DNS and the database branch together.

Preview deployments

Every branch that is not the default branch gets its own preview at its own URL. A preview is private to your project, and it never receives production credentials, database, files or secrets — a bad change on a branch cannot touch the live app. When you are happy with one, POST /v1/projects/:slug/deploys/previews/:branch/promote turns that branch into a production deploy.

Open a pull request, push the branch, and hand the preview link to someone before it is real. When the branch is deleted, the preview and its data go with it. The full picture — isolation, the optional password gate, and the database branch underneath — is in Preview deployments.

Limits at the edge

Every request to your app passes through Cloudflare first, which sets three limits no setting on Camplax can raise:

  • A request body is at most 100 MB. A bigger upload fails with 413 before your app sees it. Send big files straight to object storage instead — a signed upload URL takes up to 95 MB in one request, and a resumable upload takes files of any size in parts.
  • Your app has 125 seconds to start answering. If no byte of the response arrives in that time the visitor gets Cloudflare's 524 page, even though your app is still working. Stream the response (send headers and the first chunk early — server-sent events and streamed AI replies are fine), or do the slow part in a background job or a workflow and let the page poll for the result.
  • A connection that goes quiet for about 125 seconds is closed. That includes server-sent events and WebSockets. Send a heartbeat every 30 seconds or so — an SSE comment line (: ping) or a WebSocket ping — and have the browser reconnect when a stream drops.
  • No video or large media from the app itself. Every app shares one Cloudflare zone, and Cloudflare's terms do not let that zone serve video or large media files unless they are stored in R2. Camplax object storage is Cloudflare R2: put video, audio and big downloads there and link the file's URL. If an app serves more than a gigabyte of video or audio in a day, a warning with this fix appears in its logs and Camplax is told.

Inside the running app

  • Node.js only. Camplax builds and runs Node apps (Node 20, 22 or 24). A Python, Go, Ruby or PHP project does not deploy.
  • Start within 30 seconds. The app must be listening on PORT within 30 seconds of starting, or the deploy fails and the running version keeps serving. Do slow setup (warming a cache, loading a model) after the server is listening.
  • Three paths are Camplax's. /health, /healthz and /readyz are answered by Camplax itself, never by your app, so it can tell whether your app is up. Give your own health route another name.
  • Files. Only /tmp is writable, and it is wiped on every deploy and every change to the app's settings (it survives a crash restart). It holds up to 2 GB, whatever the disk size your code reads. Writing anywhere else fails with EACCES ("permission denied"), not EROFS: the disk is read-only, but the sandbox your app runs in checks permissions first. Keep uploads and anything that must last in object storage or the Postgres database.
  • The visitor's IP address is in the CF-Connecting-IP header, which Cloudflare sets and a visitor cannot fake. X-Forwarded-For ends with the same address, but its earlier entries are whatever the visitor sent — don't trust those. Forwarded and True-Client-IP also arrive as the visitor sent them.
  • An encoded slash. A %2F in the path arrives as a plain / (/files/a%2Fb reaches your app as /files/a/b), so an edge rule on a path can't be dodged by spelling a slash differently. The query string is passed through still encoded: put a value that may contain a slash there.
  • Outbound connections. Your app can call any public address on any port except 25, the email port mail providers block, and the ports crypto-mining pools use (3333, 4444, 5555, 7777, 14433, 14444 and 45700). Send email through Email, or through another provider on port 587 or 465. There is no outbound IPv6, so a service that only has an IPv6 address can't be reached.
  • Outbound speed. Everything your app's own code sends out — an upload to another service, or a file it writes to Camplax storage — goes at up to 100 Mbit/s, about 12 MB a second. Pages and files your visitors load are not part of this and are not slowed. If your app needs more, write to support with what it sends and how much; once raised, the new speed applies from your next deploy.
  • No abuse. Scanning other servers or Camplax's own network, flooding a server with connections, sending mail straight to mail servers, and crypto mining are caught within seconds: the app is cut off and the account closed, with no refund (acceptable use).

Domains and SSL

Every project starts on <slug>.camplax.app with an SSL certificate already in place. Point your own domain at the project from Domains in the console and its certificate follows — see Domains, DNS and SSL. Customer apps live on camplax.app, which is deliberately separate from camplax.dev, where the console and API run — the reason is in How Camplax works.

Under the hood: each environment runs in its own isolated namespace on a Camplax cell, behind Cloudflare DNS and SSL. Production never sleeps; a preview suspends when idle and wakes on its first request.

Camplax

Hosting, database, sign-in and storage in one project. One login, one bill.

Platform
More
Legal
Company
© 2026 Camplax From $25 / month · credits stop at zero