Skip to content
Noite
Esc
↑↓navigate↵open⌘Jpreview
On this page

Deploy

Create an app and ship it — push over Git, deploy from CI, and what each push triggers.

Create the app

Open the control UI and go to Apps → New app. Enter a name and slug. Creating the app provisions its git remote; deploys follow each push.

Slug rules: 1–48 chars, lowercase letters/digits/hyphens, starting and ending with a letter or digit. Reserved: _control, app, api, git.

Push with git

Every app has a Git remote at https://git.<domain>/<slug>. Authenticate as user git with an account API key (Account → API keys) as the password; the push needs the push role on the app. Each push to main deploys.

The smallest app is a Wrangler config plus a module Worker:

{
  "name": "hello",
  "main": "index.js",
  "compatibility_date": "2026-09-01",
}
export default {
  fetch: () => new Response("Hello from Noite"),
};
git init -b main
git add -A && git commit -m "first deploy"
git remote add origin "https://git:<api-key>@git.<domain>/<slug>"
git push -u origin main

Open the result at https://<slug>.<domain>. With a package.json, the runner installs and builds it first with the project’s package manager (npm, pnpm, Yarn or Bun), so Vite and Rsbuild apps need no CI (Build); without one it deploys the tree as is. The Wrangler config is optional: a static site (a build that writes dist/index.html) or a package.json main that exports fetch deploys without one (No config at all). Pushes must fast-forward unless you hold the admin role.

Your first app, in order

  1. Create the app and an API key (above).
  2. Describe the Worker with a wrangler.jsonc or, if you use the cf CLI, a cloudflare.config.ts (which one). Or neither, for a static site or a fetch module (no config at all).
  3. Add a build if you have one: a build script or build.command (Build).
  4. Set env vars in the app’s Settings if the build or release command needs them. They do not reach the running Worker; use vars in the config for that (Configure).
  5. Push to main, watch the Deployments tab, open https://<slug>.<domain>.
  6. Check what does not run: Node HTTP servers (Express, Fastify, app.listen()) are refused at deploy (Frameworks and Node servers). Quotas, rate limits and memory bounds are on Limits.

Release command

A release string in the Wrangler config runs once per deploy, after the build and before celld deploy: the place for database migrations.

{
  "name": "my-app",
  "main": "src/index.ts",
  "compatibility_date": "2026-09-01",
  "release": "bun run db:migrate",
}

It runs with sh -c in the project with your app’s env vars, and is killed after 10 minutes. The command is capped at 500 characters. A non-zero exit fails the deploy and the previous deployment keeps serving. release is removed from the config before celld deploy.

Deploy from CI

@noitenow/cli pushes a prebuilt dist/ plus wrangler.jsonc as a synthetic fast-forward commit — no server build, push role suffices, never force-pushes.

bunx @noitenow/cli deploy --slug <slug> --url https://git.<domain> --token "$NOITE_API_KEY"
- run: bun run build
- run: bunx @noitenow/cli deploy --comment update
  env:
    NOITE_API_KEY: ${{ secrets.NOITE_API_KEY }}
    NOITE_BASE: https://git.<domain>
Flag Env Default
--dist — dist
--slug NOITE_SLUG repo name, slugified
--url GIT_PUBLIC_BASE / NOITE_BASE http://git.localhost:9080
--token NOITE_API_KEY / NOITE_GIT_TOKEN —
--wrangler — wrangler.jsonc
--message — deploy <sha12>
--comment — update (create / off)

In Actions it writes url= + sha= to $GITHUB_OUTPUT and posts (or updates, via marker) a PR comment. A Worker may be declared in cloudflare.config.ts instead of wrangler.json(c) — the runner converts it after the build when no Wrangler config exists.

What happens on push

  1. Tip .bundle lands; the runner updates its bare mirror and worktree.
  2. The sandboxed install and build (build.command or the build script) run when the tree declares them, with the project’s package manager.
  3. The runner picks the config to deploy: the build’s output when it produced one, else the source config, else one it generates (Which config deploys).
  4. Optional release command (e.g. migrations) runs. A failed release keeps the old deployment serving.
  5. The config is stripped of runner-only keys (release, build), then celld deploy ships the fleet; a new fleet spawns or the existing one gets POST /reload.

The Deployments tab shows status per push with a build-log pane: one ▸ step: command line per step and its output tail, and on failure the step that failed. Deploy logs keep a 64 KB tail.

Rollback

POST /v1/apps/<id>/rollback {sha} redeploys a past successful SHA — bundles are immutable per SHA. The UI offers rollback on success rows.

Was this page helpful?