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 deploystill 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.
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" }.
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.
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.
Rollback restores the build artifact, not the database. A very old deploy can also fail with
historical_rollback_unavailableif its artifact was not retained — the error says so plainly.
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/logreturns it as plain text, and…/deploys/latest/logalways names the newest production attempt. camplax logsshows recent build, request and database logs;camplax logs --tailstreams 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 markednoindexand 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.
Previews
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 Previews.
Domains and https
Every project starts on <slug>.camplax.app with a certificate already in place. Point your own domain at the project from Domains in the console and the certificate follows — see Domains. 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: your app runs on Fly.io Machines — it suspends when idle and resumes in a few hundred ms — behind Cloudflare DNS and SSL.