Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

g1t.sh
1,009 linesCodeBlameRaw
1---
2title: Deployments
3description: A project's production on every push and a live preview of every branch with a pull request, on g1t.page. Scales to zero, and billed to the workspace.
4---
5
6Deployments put your [project](/guides/projects/) on the web. They are off
7for every project until you turn them on. Once they are on, every branch
8with a pull request gets its own live preview, linked on the pull request,
9and the default branch goes to production on every push. Reviewers
10click through the change instead of reading a diff.
11
12Apps run on Cloudflare Workers, on `g1t.page`: sites, Workers projects,
13and Node.js servers such as Express apps. A server that cannot run as a
14Worker runs as a container on Cloudflare Containers behind one; see
15[What a project is built as](#what-a-project-is-built-as) and
16[Docker deployments](#docker-deployments):
17
18| | Address |
19| --- | --- |
20| Production | `https://<project>-<workspace>.g1t.page` |
21| Preview of the branch `fix-login` | `https://<project>-git-fix-login-<workspace>.g1t.page` |
22
23A preview's address follows its branch, so it stays the same for every push
24to it. A pull request from a fork, as coding agents make them, is named
25`pr-<number>` in place of the branch. A name too long for an address, or
26one another app already has, is shortened or given a short suffix.
27
28A workspace whose name ends like a domain is written without that last
29hyphen: `flagon-io` gives `https://web-flagonio.g1t.page`, not
30`web-flagon-io.g1t.page`. Browsers read `flagon-io` in an address as a
31copy of `flagon.io`, and warn people who visit flagon.io that the page
32looks fake. Apps made before this moved to the new address on their own;
33the old one redirects to it.
34
35When a workspace is [renamed](/guides/workspaces/#rename-a-workspace), or a
36repository is [transferred](/guides/transferring-repositories/#deployments)
37to another workspace or [renamed](/guides/managing-repositories/), its apps
38are built again under addresses with the new name, and the old addresses
39redirect to them for 90 days. A rebuild that fails is tried again an hour
40later, then two hours after that. After three failures with the same
41error, or after one whose commit no longer exists, it is not tried again
42until you push or choose **Redeploy**.
43
44When the [default branch changes](/guides/managing-repositories/), production
45is built again from the new default branch. An
46[archived](/guides/managing-repositories/) repository's apps keep serving.
47
48Every build is also a deployment in the repository's one list of
49deployments, beside those reported from any CI and those g1t Actions
50jobs make, with the source `g1t_page` and the environment `production` or
51`preview`. See [Deployments API](/guides/deployments-api/) to read them,
52report your own, and hear of them by webhook.
53
54An app runs only while it answers a request. One nobody visits runs
55nothing and costs nothing, and the next visit wakes it in milliseconds. A
56container wakes in seconds, and sleeps again after five minutes without a
57request.
58
59Deployments are part of the [g1t plan](/guides/usage-and-billing/#the-g1t-plan),
60and are opt-in per project. A project set to deploy on g1t, with
61deployments off, says **Deployments are off** on its overview, with **Turn on deployments** for
62people with the Admin [role](/guides/access-and-roles/) on its repository. Nothing builds or runs until you turn them on, and one click turns
63them off again.
64
65## Turn on deployments
66
671. **Start the g1t plan for the workspace**, if it is not on. An owner
68 opens **Settings → Billing and plans**, `g1t.sh/<workspace>/-/billing`,
69 and chooses **Start the g1t plan** on the **g1t** card. Back on
70 Billing, the card says **On the g1t plan**. The trial does not pay for
71 deployments.
722. **Turn on deployments for a project.** Someone with the Admin role on
73 its repository opens the project's
74 **Settings → Deployments**, `g1t.sh/<workspace>/<project>/settings/deployments`,
75 or chooses **Deploy** on its overview,
76 and chooses **Turn on deployments**.
77
78Production starts building at once from the default branch. Every pull
79request opened or pushed to from then on gets a preview.
80
81Deployments are for projects g1t runs. Not every project is one: a
82library, a tool or documentation is published rather than deployed, and
83an app may be deployed by its own pipeline somewhere else. Only a project
84that is [an app or site deployed on g1t](/guides/projects/#what-a-project-is)
85is asked to turn deployments on. Any other project's **Deployments** page
86still turns them on; doing so makes it an app deployed on g1t, including
87one that was deployed elsewhere. A project set to be a library, a tool or
88something else cannot have deployments turned on until that setting
89changes.
90
91While payments on g1t are in test mode, no real card is charged: use the
92test card `4242 4242 4242 4242` with any future date and any code.
93
94## What deploys
95
96g1t looks at the project's root directory and builds it the way it is meant to be built.
97You do not configure anything for the common cases.
98
99| The project has | g1t |
100| --- | --- |
101| `wrangler.jsonc`, `wrangler.json` or `wrangler.toml` | Builds it as a **Workers project**: installs dependencies, runs `wrangler deploy --dry-run` to bundle it (which runs the config's own `build` command), and deploys the bundle with the config's static assets, `compatibility_date`, `compatibility_flags` and `vars`. |
102| A framework from the table below, even beside a `Dockerfile` | Installs dependencies, builds it with the framework's command, and serves the folder it writes as a **static site**, or deploys the Worker its Cloudflare adapter writes as a **Workers project**. |
103| A framework set up to need a server (Next.js without a static export, Astro's `@astrojs/node`, SvelteKit's `adapter-node`, a server-rendered React Router or Remix app) | Builds it as a **container**: from its `Dockerfile`, or one g1t writes for it. |
104| A Node.js server (Express, Koa, Hono, `node:http` and others) with no `Dockerfile` | Runs it as a **Workers project**: wraps its server in a Worker, bundles it, and checks that it starts. One that cannot run as a Worker runs as a **container** instead, and the build's log says why. See [Node.js servers as Workers](#nodejs-servers-as-workers). |
105| A Python, Go, Ruby, PHP or Rust server, or a Node.js server with a `Dockerfile` | Builds it as a **container**: from its `Dockerfile`, or one g1t writes for it and shows in the build's log. See [Docker deployments](#docker-deployments). |
106| A `build` script in `package.json`, even beside a `Dockerfile` | Installs dependencies, runs `npm run build`, and serves the output as a **static site**. |
107| An `index.html` and nothing to build, even beside a `Dockerfile` | Serves it as it is. |
108| A `Dockerfile` and nothing else to serve | Builds the image and runs it as a **container**. |
109| Nothing g1t recognises | Fails, saying to add a `Dockerfile`, a `build` script or an `index.html`. |
110
111### What a project is built as
112
113A site or a Worker is always preferred: it runs nothing while no one
114visits, so it costs nothing then, and it wakes in milliseconds. A
115container is the fallback. With **Build as** on **Auto** (the default),
116the first of these that fits is what the project is built as:
117
1181. **A Workers project**: a Wrangler config (`wrangler.jsonc`,
119 `wrangler.json` or `wrangler.toml`), or a framework's Cloudflare
120 adapter, even beside a `Dockerfile`.
1212. **A framework's site**: a framework from the [table below](#frameworks)
122 that builds a site, even beside a `Dockerfile`. A framework set up to
123 need a server is a container, from its `Dockerfile` or one g1t writes.
1243. **A Node.js server as a Worker**: a Node.js server with no
125 `Dockerfile` ([Node.js servers as Workers](#nodejs-servers-as-workers)).
126 One that cannot run as a Worker is a container, from a Dockerfile g1t
127 writes.
1284. **A container for any other server g1t recognises**: Python, Go, Ruby,
129 PHP or Rust, or a Node.js server with a `Dockerfile` of its own; from
130 that `Dockerfile`, or one g1t writes.
1315. **A site**: a `build` script's output, or an `index.html`, even beside
132 a `Dockerfile`.
1336. **A container from its `Dockerfile`** (or `Containerfile`, or the path
134 set as its **Dockerfile**), when there is nothing else to serve.
1357. Otherwise the build fails, saying what to add.
136
137When a site is built beside a `Dockerfile`, the build's log says the
138`Dockerfile` is not used, near its top: "Built as a Vite site; Dockerfile
139is not used (a container is only the fallback; choose **Build as**:
140Container to use it)." To run the project's `Dockerfile` instead, set
141**Build as** to **Container** under **Settings → Deployments**. **Build
142as** set to **Static site**, **Workers** or **Container** is what the
143project is built as, whatever else it has.
144
145**Settings → Deployments** shows what g1t detected at the project's last
146finished build and what it was built with, such as **Static site ·
147Astro**, **Workers project**, **Container · Dockerfile** or **Container ·
148Flask, with a Dockerfile g1t wrote**, and what running it costs. Before
149the first build it says **Detected at the first build**. The framework is
150named at the top of the build's log, as `Detected Astro: framework=astro.`
151**Build as**, under the same settings, overrides what is detected.
152
153Dependencies are installed by the lockfile that is there: `npm ci`,
154`pnpm install --frozen-lockfile`, `yarn install` or `bun install`, and
155`npm install` with no lockfile.
156
157### Frameworks
158
159g1t knows a framework by the packages in `package.json`
160(`dependencies` and `devDependencies`) and by its config file. The build
161command is the package's own `build` script when it has one, else the
162command in the table. A **Build command** or **Output directory** of
163your own, under **Settings → Deployments**, always wins over the table.
164
165| Framework | Found by | Builds with | Serves |
166| --- | --- | --- | --- |
167| Vite, and React, Vue, Svelte, Solid or Preact on Vite | `vite` | `vite build` | `dist`, or `build.outDir` from `vite.config` |
168| Astro | `astro` | `astro build` | `dist`, or `outDir` |
169| Astro with `@astrojs/cloudflare` | `astro`, `@astrojs/cloudflare` | `astro build` | A Workers project: the Worker and config the adapter writes |
170| Next.js with `output: 'export'` | `next`, and `output: 'export'` in `next.config` | `next build` | `out` |
171| Next.js with OpenNext | `next`, `@opennextjs/cloudflare` and a `wrangler.jsonc` | `opennextjs-cloudflare build`, then `wrangler deploy --dry-run` | A Workers project |
172| SvelteKit with `@sveltejs/adapter-static` | `@sveltejs/kit` and the adapter | `vite build` | `build`, or the adapter's `pages` |
173| SvelteKit with `@sveltejs/adapter-cloudflare` | `@sveltejs/kit` and the adapter | `vite build` | A Workers project: `.svelte-kit/cloudflare/_worker.js` and the files beside it |
174| Nuxt | `nuxt` | `npm run generate` if there is one, else `nuxi generate` | `.output/public` |
175| React Router in SPA mode | `@react-router/dev`, and `ssr: false` in `react-router.config` | `react-router build` | `build/client` |
176| Remix in SPA mode | `@remix-run/dev`, and `ssr: false` in `vite.config` | `remix vite:build` | `build/client` |
177| Gatsby | `gatsby` | `gatsby build` | `public` |
178| Docusaurus | `@docusaurus/core` | `docusaurus build` | `build` |
179| Eleventy | `@11ty/eleventy` | `eleventy` | `_site`, or `dir.output` |
180| Angular | `@angular/core` and `angular.json` | `ng build` | `dist/<project>/browser`, from `angular.json` |
181| Create React App | `react-scripts` | `react-scripts build` | `build` |
182| Hugo | `hugo.toml`, `hugo.yaml` or `hugo.json`; or a Hugo `config.toml` and no `package.json` | `hugo --minify` | `public`, or `publishDir` |
183| Jekyll | `_config.yml` and a `Gemfile` | `bundle install`, then `bundle exec jekyll build` | `_site`, or `destination` |
184| mdBook | `book.toml` | `mdbook build` | `book`, or `build.build-dir` |
185
186- **Frameworks that need a server run as containers.** A Next.js app
187 without `output: 'export'`, an Astro site with `@astrojs/node`, a
188 SvelteKit app with `adapter-node`, and a React Router or Remix app that
189 renders on the server are built from their `Dockerfile`, or one g1t
190 writes ([Docker deployments](#docker-deployments)). A static export or
191 the framework's Cloudflare adapter costs less, since it runs nothing
192 while no one visits; the build's log says so. One g1t cannot run as a
193 server, such as SvelteKit with `adapter-auto`, fails before anything is
194 installed, saying what to change or to add a `Dockerfile`. An **Output
195 directory** of your own, or **Build as** Static site, builds it as a
196 static site instead.
197- **A framework's Worker.** Astro's and SvelteKit's Cloudflare adapters,
198 OpenNext, and `@cloudflare/vite-plugin` with a Wrangler config build a
199 Worker. g1t runs the build, then bundles what it wrote with
200 `wrangler deploy --dry-run`, reading the config the build wrote
201 (`.wrangler/deploy/config.json`) when there is one. A `_worker.js`,
202 `_routes.json` or `.assetsignore` at the top of the static files, and
203 the names `.assetsignore` lists, are never served.
204- **Hugo and mdBook** use the `hugo` or `mdbook` on the machine's `PATH`.
205 Without one, the build fetches Hugo 0.166.0 (the extended edition, with
206 npm) or builds mdBook 0.5.4 with cargo, which takes a minute or two.
207 Jekyll's gems are installed into `vendor/bundle` in the project.
208
209A project that is none of these is served from the first of these that
210exists after the build: `dist`, `build`, `out`, `public`, `_site`,
211`.output/public`. Set **Output directory** to choose another.
212
213### Static sites
214
215- A site without a `404.html` is treated as a single-page app: an address
216 that matches no file serves `index.html`. With a `404.html`, that page
217 is served instead, with status 404.
218- `_headers` and `_redirects` files in the output are honored, in
219 [Cloudflare's format](https://developers.cloudflare.com/workers/static-assets/headers/).
220- Up to 20,000 files, and 25 MiB per file: Cloudflare's limits.
221
222### Workers projects
223
224- Your Worker's `fetch` handler runs as written, and its static assets
225 are served under the binding name your config gives them. Cron triggers
226 (`triggers.crons`) are not scheduled, so a `scheduled` handler never
227 runs; the deployment says so in its warnings.
228- Each request your Worker answers may use up to 50 ms of CPU time
229 (waiting on the network does not count) and make up to 50 requests of
230 its own (`fetch` calls and the like). A request that goes over either
231 is stopped and answered with a 503 page that says so. Static assets are
232 served without running your Worker, and count toward neither.
233- `vars` are deployed as plain-text bindings (or JSON, for objects). Rows
234 of the project's [secrets and variables](/guides/secrets-and-variables/)
235 available to Deployments are bound too, and replace a `var` of the same
236 name: secrets as secret bindings.
237- **Not provisioned yet:** D1, KV, R2, Durable Objects, Queues, service
238 bindings, Vectorize, Hyperdrive, Workers AI and Workflows. A project that
239 declares any of them still deploys, without them, and its deployment
240 lists each one it left out. Code that needs them should check that the
241 binding is there.
242
243### Node.js servers as Workers
244
245A Node.js server with no `Dockerfile` runs as a Worker, not a container:
246an Express, Koa, Hono, Polka, Restify or hapi app, or one on `node:http`
247alone. Workers run Node.js HTTP servers with the `nodejs_compat`
248compatibility flag, through
249[`httpServerHandler`](https://developers.cloudflare.com/workers/runtime-apis/nodejs/http/)
250from `cloudflare:node`, so the app's code runs as written. g1t knows a
251Node.js server by a server package in `package.json`, or by a `start`
252script with no `build` script or `index.html` beside it.
253
2541. **It finds the file that starts the app**: the file its `start` script
255 runs with `node`, `tsx` or `ts-node` (`node server.js`,
256 `NODE_ENV=production node dist/index.js`), else `main` in
257 `package.json`, else `server.js`, `index.js`, `app.js` or the like.
2582. **It installs and builds** as for any project: the lockfile's install,
259 then `npm run build` when there is a `build` script (a TypeScript app
260 compiled to `dist`, say).
2613. **It writes a Worker entry** that imports that file, keeps the server
262 it starts, and answers each request with `httpServerHandler`. An app
263 whose default export has a `fetch` method (a Hono app) is answered by
264 that `fetch` directly. `listen` may name any port and address: on
265 Workers the port only names the server. `PORT` is `8080` unless the
266 project's variables set it. The entry is printed whole in the build's
267 log.
2684. **It bundles the Worker** with `wrangler deploy --dry-run`, with the
269 compatibility date `2026-09-26` and `nodejs_compat`.
2705. **It starts the Worker once** with `wrangler dev`, with the project's
271 [variables and secrets](#secrets-and-variables), and asks it for `/`. A
272 Worker that fails as it starts (a top-level `await` that never settles,
273 a module that cannot load, a server that never calls `listen`) is not
274 deployed.
275
276A folder the app serves with `app.use(express.static("public"))`, with
277no path before it, is uploaded as the Worker's static files: they are
278served without running the app, and count toward none of its requests or
279CPU time.
280
281What is known not to run as a Worker is found before anything is built,
282and the app runs as a container instead, from a Dockerfile g1t writes,
283with the reason in the build's log:
284
285| The app | Why |
286| --- | --- |
287| Depends on a native addon: `better-sqlite3`, `sqlite3`, `bcrypt`, `sharp`, `canvas`, `argon2`, `puppeteer`, `playwright`, `node-pty`, `ssh2` and others | A Worker cannot load native code. |
288| Is a Fastify, NestJS or Sails app | They start on timers or read their modules from disk as they start, which a Worker cannot do before its first request. |
289| Accepts WebSocket connections with `socket.io`, `express-ws`, `WebSocketServer` or an `upgrade` handler | Workers do not support Node's `upgrade` event. |
290| Uses `child_process`, `cluster` or `worker_threads` | A Worker cannot start processes or threads. |
291| Reads or writes files while it runs: `readFile`, `writeFile`, `createReadStream`, `sendFile`, `res.render` and a view engine, `multer`'s disk storage | A Worker has no disk with the project's files. |
292| Serves files from disk under a path (`app.use("/static", express.static("public"))`), from a folder that does not exist, or from more than one folder | g1t can serve one folder, at the top, for it. |
293| Has a `start` script g1t cannot read (`nest start`, `pm2-runtime …`) | g1t cannot tell which file starts it. |
294
295g1t looks for these in the app's own JavaScript and TypeScript, not in
296`node_modules`, tests, `scripts`, `public` or a build's output. A Worker
297that does not bundle, or does not start, also runs as a container, saying
298"Could not run as a Worker: …; deploying as a container." With a
299`Dockerfile` of its own, a Node.js server is built from that `Dockerfile`
300as a container. To run one as a Worker whatever g1t finds, set **Build
301as** to **Workers**; to run it as a container, **Container**.
302
303### Docker deployments
304
305Any app that runs in a container deploys as one, whatever its framework or
306age: give it a `Dockerfile`, or let g1t write one. It runs on
307[Cloudflare Containers](https://developers.cloudflare.com/containers/)
308behind the app's Worker, at the same addresses, custom domains and
309previews as any other app.
310
311A container is the fallback, for what cannot be served as a site or run
312as a Worker ([What a project is built as](#what-a-project-is-built-as)):
313a Python, Go, Ruby, PHP or Rust server, a Node.js server with a
314`Dockerfile` of its own or one that cannot run as a Worker, a framework
315set up to need a server, or a `Dockerfile` with nothing else to serve.
316There is nothing to set for those. A site or a Node.js server is built
317without its `Dockerfile`; to deploy its container instead, set **Build
318as** to **Container** under **Settings → Deployments**.
319
320The build's log says which it chose and why, near its top: for example,
321"Built as a static site; Dockerfile is not used (a container is only the
322fallback; choose **Build as**: Container to use it)", or "Could not run as
323a Worker: it depends on `better-sqlite3`, which builds native code a
324Worker cannot load; deploying as a container."
325
326g1t writes a Dockerfile for these, when there is none:
327
328| The project has | Runs with |
329| --- | --- |
330| `package.json` with a `start` script, or a server package (`express`, `fastify`, `koa`, `@nestjs/core`, `@hono/node-server` and others) and a main file | `npm start`, or `node <main>`, on `node:22-slim` (or the major release in `.nvmrc` or `.node-version`), after `npm run build` when there is one |
331| Next.js, Astro with `@astrojs/node`, SvelteKit with `adapter-node`, React Router or Remix | `next start` (or `node .next/standalone/server.js`, with its static files beside it, for `output: 'standalone'`), `node ./dist/server/entry.mjs`, `node build`, `react-router-serve` or `remix-serve`, after the build |
332| `requirements.txt`, `pyproject.toml` or `Pipfile`, and `manage.py` (Django) | `gunicorn <project>.wsgi` on `python:3.12-slim` |
333| The same, with Flask and an `app.py`, `main.py`, `server.py`, `wsgi.py` or `asgi.py` | `gunicorn <module>:app` |
334| The same, with FastAPI or Starlette | `uvicorn <module>:app` |
335| The same, with neither | `python <module>.py` |
336| `go.mod` and one `package main`, at the top or in one folder of `cmd/` | The program, built with `golang:1.25` into an image with nothing else |
337| A `Gemfile` with Rails | `rails server`, after `assets:precompile`, on `ruby:3.3-slim` |
338| A `Gemfile` and a `config.ru` | `rackup` |
339| `composer.json`, and `artisan` with `public/index.php` (Laravel), or an `index.php` | Apache with PHP 8.3, after `composer install` |
340| `Cargo.toml` with one program and `axum`, `actix-web`, `rocket`, `warp`, `poem`, `salvo` or `tide` | The program, built with `rust:1` into `debian:bookworm-slim` |
341
342The Dockerfile g1t writes is printed whole in the build's log. To change
343how the app is built or run, commit it as `Dockerfile` and edit it. It
344leaves `.git`, `node_modules`, `.venv` and `__pycache__` out of the image.
345
346What a container gets, and what it must do:
347
348- **Listen on `0.0.0.0` at `PORT`.** `PORT` is the port its Dockerfile's
349 first `EXPOSE` names (in its last stage), else the `PORT` it sets with
350 `ENV`, else `8080`. A Dockerfile g1t writes sets `HOST` and `HOSTNAME`
351 to `0.0.0.0` too. The first request
352 waits up to 90 seconds for it to answer there; one that never does gets
353 a 503 page saying so.
354- **The project's [secrets and variables](#secrets-and-variables)** are
355 its environment variables, set by g1t when it starts, never baked into
356 the image. The image's build gets none of them: a step that needs one
357 belongs in the app's start.
358- **One instance, which sleeps.** It starts on the first request, and
359 stops after five minutes without one; asleep, it costs nothing, and the
360 next request starts it again in a few seconds. Its disk starts from the
361 image each time: keep data in a database or a bucket.
362- **Its size** is **Container size** under **Settings → Deployments**:
363 `lite` (the default) to `standard-4`, as [What it costs](#what-it-costs)
364 lists. The image may be as large as the size's disk, 2 GB for `lite`;
365 a larger image fails the build, saying how large it was. An app keeps
366 the image it runs and the one before it; older ones are deleted once a
367 deploy is live, and all of them when the app is taken down.
368- **Built for `linux/amd64`**, with Docker, in the build's sandbox (or the
369 workflow job's Docker, for [`g1t-runner deploy`](#deploying-from-a-workflow)),
370 with network access as any build has. **Build command** is not used:
371 the Dockerfile builds it.
372
373What Deployments cannot run yet is on
374[What g1t can't do yet](/about/limitations/#deployments).
375
376## Previews of branches
377
378A preview is built when a pull request is opened, when it is marked ready,
379and on every push to it. Pull requests from forks are built from the
380fork.
381
382The pull request shows the deployment as a check named **g1t / deploy**:
383
384| State | Means |
385| --- | --- |
386| Pending, "Building" | The build is running. The link opens its log. |
387| Passed, "Preview is live" | The link opens the preview. |
388| Failed, "Deployment failed" | The link opens the build log and the reason. |
389
390A newer push replaces a build that is still running for the same pull
391request. Previews are marked `noindex`, so search engines leave them alone.
392
393The **Deployments** page lists the same failure of one app, repeated, as a
394single row with how many times it happened and when it last did. The row
395opens the newest of them.
396
397## Production
398
399Each push to the default branch, which is each merge on a protected
400branch, builds and replaces production. The **Deployments** page shows the
401live address, the commit it runs and when it went up.
402
403### The production screenshot
404
405When production goes live, g1t takes a screenshot of its home page, as a
4061280 by 800 browser window sees it, and shows it on the project's
407[overview](/guides/projects/#the-overview). Choosing it opens the site.
408
409- One screenshot is taken per production deploy. g1t waits up to 12
410 seconds for the page to settle, then takes it as it is.
411- The screenshot is of the app's `g1t.page` address. A custom domain
412 serves the same app, so it looks the same.
413- Until it is ready, or if it could not be taken, the overview shows the
414 project's uploaded image, if it has one, or else a plain frame with the
415 address. A failed screenshot is tried again when the overview is next
416 opened, at most every five minutes.
417- Only people with a role on the project's repository see it, like the
418 rest of its deployments; anyone else who can see the project sees its
419 uploaded image or the frame.
420- With **Use the uploaded image instead of a screenshot** on, the
421 uploaded image shows instead.
422- Screenshots are not charged.
423
424An app deployed elsewhere gets a screenshot of its production address
425too, and any project can show an uploaded image instead. See
426[production's picture](/guides/projects/#productions-picture).
427
428## Deploying from a workflow
429
430A [workflow](/guides/actions/) job can build and deploy the project
431itself, with g1t's own action, `g1t/deploy`. Use it when the build needs
432steps of yours first (generated code, a test that must pass, a tool the
433default build lacks), or to deploy from a branch without a pull request.
434The app, its address, its checks and its custom domains are the same as
435for the builds g1t starts by itself.
436
437```yaml
438# .g1t/workflows/deploy.yml
439name: Deploy
440
441on:
442 push:
443 branches: [main]
444 pull_request:
445
446permissions:
447 contents: read
448 deployments: write
449
450jobs:
451 deploy:
452 runs-on: ubuntu-latest
453 steps:
454 - uses: actions/checkout@v5
455 - uses: actions/setup-node@v4
456 with:
457 node-version: 24
458 - run: npm ci
459 - run: npm test
460 - id: deploy
461 uses: g1t/deploy@v1
462 with:
463 build-command: npm run build
464 - run: echo "Live at ${{ steps.deploy.outputs.url }}"
465```
466
4671. Turn Deployments on for the project, as in
468 [Turn on deployments](#turn-on-deployments).
4692. Add a workflow like the one above. The job needs `deployments: write`
470 (or `pages: write`) in its `permissions:`.
4713. Push. The run on `main` deploys production; a pull request's run
472 deploys its preview.
473
474What the run builds:
475
476| The run | Builds |
477| --- | --- |
478| On the project's production branch (a push, `workflow_dispatch` or `schedule` there) | Production |
479| For a pull request (`pull_request`, `pull_request_target`) | That pull request's preview, at its branch's address, or `pr-<number>` for one from a fork |
480| On another branch | That branch's preview |
481| For a tag | Nothing: the step fails and says to run the workflow on a branch |
482
483`g1t/deploy` takes these inputs:
484
485| Input | Default | |
486| --- | --- | --- |
487| `project` | The repository's one project with Deployments on | The project's slug. Needed when the repository has several. |
488| `root-dir` | The project's **Root directory** | Where the project is in the checkout. |
489| `build-command` | The project's **Build command**, else what the build finds | What builds it, run after the install. |
490| `output-dir` | The project's **Output directory**, else the first folder found | The folder a static build writes. |
491| `sha` | The run's commit | The commit the `g1t / deploy` check goes on. |
492| `token` | `${{ github.token }}` | The job's own token. No other token can open a build. |
493
494Its outputs are `url` (the app's address), `deployment-id` (`dpl_…`) and
495`kind` (`production` or `preview`).
496
497The action calls
498[`POST /repos/{owner}/{repo}/deployments/builds`](/reference/api/deployments/start-deployment-build/),
499which checks the plan and the spend limit as any build does, sets the
500commit's `g1t / deploy` check to pending, and answers with a token for
501that build alone and the project's
502[variables and secrets](#secrets-and-variables) for production or
503previews. The action masks the token and every secret in the log, then
504runs `g1t-runner deploy` in the checkout, which installs, builds and
505uploads exactly as [What deploys](#what-deploys) says; its output is in
506the step's log and on the deployment's page. When the build fails, or
507g1t does not take what it built, the step fails and the deployment says
508why. When the job ends before the build has finished, because it was
509cancelled, failed in an earlier step or timed out, the deployment fails
510as the job ends, saying how it ended, rather than staying **Building**.
511
512- **Secrets.** A pull request from a fork or from someone without the
513 Write role, and a run that had to be
514 [approved](/guides/actions/#pull-requests), build without
515 the project's secrets, as previews of them always have. Their token is
516 read-only, so they cannot open a build at all unless the workflow runs
517 on `pull_request_target`.
518- **Cost.** The build runs in the job, so its time is the job's
519 [Actions time](/guides/usage-and-billing/), free on a
520 [self-hosted runner](/guides/self-hosted-runners/), and is not charged
521 again as build seconds. Requests, CPU time and custom domains are
522 charged as for any app.
523- **g1t's own builds still run.** A push to the production branch and a
524 pull request's push still start g1t's build too. Whichever starts
525 later replaces the other, so the app ends up at the newest commit
526 either way.
527- **On your own machines**, the action uses the `g1t-runner` on `PATH`, or
528 downloads the newest release and checks it against its `SHA256SUMS`. It
529 runs on Linux and macOS, with `curl` and `jq` or `python3`.
530
531`g1t/detect` says what a build of the checkout would find, without
532building anything: its outputs are `kind` (`workers`, `static`, `html`, `container` or
533`none`), `install`, `build`, `output-dir`, `framework` (such as
534`astro`, `nextjs` or `node`, empty when there is none), `dockerfile` and
535`port` for a container, and `entry` for a Node.js server run as a Worker. A framework set up to
536need a server is `none`, and the step's log says what to change. Use
537them to cache, or to fail early:
538
539```yaml
540- id: detect
541 uses: g1t/detect@v1
542- if: steps.detect.outputs.kind == 'none'
543 run: |
544 echo "::error::Nothing here would be served: add an index.html or a build script."
545 exit 1
546```
547
548### Build with a workflow
549
550Turn on **Build with a workflow** under **Settings → Deployments** and the
551project's builds run as a workflow g1t manages for it, instead of as
552builds Deployments starts by itself. Nothing else changes: the same
553previews and production, at the same addresses, with the same
554**g1t / deploy** check, which is now the check of the workflow's job.
555
556- The workflow is listed on the repository's **Actions** page, named
557 `g1t`, with its runs, logs and re-runs like any other. It is not a file
558 in the repository: it is made from the project's settings, and follows
559 them when they change.
560- It calls g1t's reusable deploy workflow,
561 `g1t/workflows/.g1t/workflows/deploy.yml@v1`, which checks out the
562 commit and runs [`g1t/deploy`](#deploying-from-a-workflow). As g1t
563 improves how projects are built, that workflow improves for every
564 project that uses it.
565- A pull request from a fork runs it with a read-only token, as on
566 GitHub, and still gets its preview, without the project's build
567 secrets.
568- To change it, commit a workflow named `g1t` to `.g1t/workflows/`: that
569 file runs in its place. Start from the one the **Actions** page shows,
570 or call the reusable workflow yourself:
571
572```yaml
573# .g1t/workflows/g1t.yml
574name: g1t
575
576on:
577 push:
578 branches: [main]
579 pull_request:
580
581permissions:
582 contents: read
583 deployments: write
584
585jobs:
586 g1t:
587 name: g1t
588 uses: g1t/workflows/.g1t/workflows/deploy.yml@v1
589 with:
590 project: web
591 secrets: inherit
592```
593
594Its jobs are [workflow jobs](/guides/actions/#machine-sizes): on g1t's
595machines they are charged as Actions time, at cost plus 20% like every
596build, and on a [self-hosted runner](/guides/self-hosted-runners/) they
597cost nothing.
598
599## Custom domains
600
601Production can be served at a domain of your own, such as `example.com` or
602`www.example.com`, as well as at its address on g1t.page. A domain belongs
603to the project, not to a build: every redeploy is served on it with nothing
604to change.
605
606### Add a domain
607
6081. Open the project's **Settings → Domains**,
609 `g1t.sh/<workspace>/<repo>/settings/domains`.
6102. Enter the domain and choose **Add domain**. Leave **Also add the www
611 (or apex) twin** checked to add both `example.com` and
612 `www.example.com` at once: the one you typed serves the app, and the
613 other redirects to it with a `308`, path and query kept.
6143. Add the DNS records the page lists, at whoever manages the domain's
615 DNS. Each name and value has a copy button.
6164. Wait. The page follows the domain from **Waiting for DNS** to
617 **Issuing certificate** to **Active** by itself; **Check now** asks
618 again at once.
619
620Custom domains need the workspace's g1t plan, and are $0.12 a month each
621(their cost plus 20%), as many as you like. Adding or removing one needs
622the Admin [role](/guides/access-and-roles/) on the project's repository.
623
624### A subdomain, such as www
625
626Add one record:
627
628| Type | Name | Target |
629| --- | --- | --- |
630| `CNAME` | `www` | `domains.g1t.page` |
631
632The same goes for any subdomain, such as `app.example.com` (name `app`).
633
634### The apex, such as example.com
635
636DNS does not allow a plain `CNAME` at the apex of a domain, so use your
637provider's flattened form of one, pointed at `domains.g1t.page`:
638
639| DNS provider | Record |
640| --- | --- |
641| Cloudflare DNS | `CNAME` at `@` (Cloudflare flattens it) |
642| Amazon Route 53 | Not supported by alias records to other zones; use the www pairing below |
643| DNSimple, NS1, Namecheap, Porkbun, Gandi | `ALIAS` at `@` |
644| DNS Made Easy, Constellix | `ANAME` at `@` |
645
646If your provider has no flattened `CNAME`, `ALIAS` or `ANAME`, add
647`www.example.com` on g1t instead, and set up your provider's forwarding (or
648any redirect) from `example.com` to `www.example.com`.
649
650### Verification and certificates
651
652Pointing the domain at `domains.g1t.page` is what proves it is yours: once
653the record is seen, the certificate authority checks the domain over HTTP
654and issues a certificate, renewed by itself before it expires. Visitors are
655always served over HTTPS, TLS 1.2 or newer.
656
657The page may also list a `TXT` record named `_cf-custom-hostname.<domain>`.
658It proves ownership before traffic moves, which is useful when the domain
659is serving a site elsewhere today: add the `TXT` first, wait for
660**Issuing certificate** or **Active**, then change the `CNAME`. Any other
661`TXT` records listed are for the certificate and are needed as shown.
662
663DNS changes can take from a few minutes to an hour to be seen. A domain
664that stays at **Waiting for DNS** usually has a record with a typo, a
665leftover `A` or `AAAA` record beside the new one, or (on Cloudflare DNS) a
666`CAA` record that does not allow the certificate's authority.
667
668### Remove a domain
669
670Choose **Remove** beside it. It stops serving the project at once, and any
671domain redirecting to it goes too. Your DNS records are left as they are.
672
673## When apps come down
674
675Nothing keeps running unasked. An app comes down, and stops costing
676anything, when:
677
678| | |
679| --- | --- |
680| Its pull request is merged or closed | That preview, at once. |
681| No one visits a preview for the project's **idle days** | That preview, at the next sweep (every 10 minutes). The default is 7 days. |
682| You choose **Take down** on the Deployments page | That app, at once. |
683| You turn off previews or production | All of that kind, at once. |
684| You choose **Turn off deployments** | Every app of the project, at once, and no more builds. |
685| The workspace's plan ends or its payment fails | Every app of the workspace, at the next sweep. |
686| The repository is [deleted](/guides/managing-repositories/) | Every app of its projects, at once. Custom domains stay set up and serve nothing. If you restore the repository, production is built again and its domains serve it; previews come back with their next push. When it is purged, its custom domains are removed. |
687
688A preview that came down comes back with the next push to its pull
689request, or **Redeploy** on the Deployments page.
690
691## Who can do what
692
693What you can do with a project's deployments follows your
694[role](/guides/access-and-roles/) on its repository:
695
696| | Needs |
697| --- | --- |
698| See its deployments, their build logs, previews and domains | Read |
699| **Redeploy**, take an app down | Write |
700| Turn deployments on or off, change their settings | Admin |
701| Add, verify and remove custom domains | Admin |
702
703A public repository's deployments, and their build logs, can be seen by
704anyone, signed in or not, the same as its code. Its settings and domains
705still need Admin. A private repository's deployments are seen only by
706people with a role on it.
707
708## Settings
709
710Under the project's **Settings → Deployments**,
711`g1t.sh/<workspace>/<repo>/settings/deployments`:
712
713| Setting | Default | |
714| --- | --- | --- |
715| Production | On | Deploy the default branch on every push. |
716| Previews | On | A preview for every branch with an open pull request. |
717| Build as | Auto | **Auto** (found from its files, a site or a Worker before a container), **Static site**, **Workers** or **Container**; see [What a project is built as](#what-a-project-is-built-as). |
718| Dockerfile | `Dockerfile` or `Containerfile` | For a container: its Dockerfile's path, inside the project. |
719| Container size | `lite` | For a container: the instance it runs at, with its price an hour awake. |
720| Build command | The project's own | Runs instead of `npm run build`, or before bundling a Workers project. Not used for a container. |
721| Output directory | Found by itself | What a static site serves. |
722| Idle days | 7 | 1 to 90. A preview no one visits this long comes down. |
723| Build with a workflow | Off | Build through the deploy workflow g1t manages for the project; see [build with a workflow](#build-with-a-workflow). |
724
725## Logs, traces and errors
726
727Every request an app answers shows under the project's
728[Observability](/guides/observability/) within seconds: its status,
729duration, CPU time and the lines it wrote, with its exceptions grouped into
730errors and its numbers by deployment. Nothing to set up. Each request
731reaches the app with an `x-g1t-request-id` header, its id there, and a
732`traceparent`, and the app is deployed with OpenTelemetry's
733`OTEL_EXPORTER_OTLP_*` variables set, so an OpenTelemetry SDK in it sends
734its spans to the project without configuration.
735
736## Secrets and variables
737
738Builds and running apps read the project's
739[secrets and variables](/guides/secrets-and-variables/) that are
740available to Deployments, and the workspace's that reach it:
741
742| | Reads |
743| --- | --- |
744| A production build, and production | Each key's Production row, else its row for all environments. |
745| A preview build, and the preview | Each key's Preview row, else its row for all environments. |
746
747The build gets them as environment variables, with secrets hidden in its
748log. The running app gets them as bindings, `env.KEY`, put in place by g1t
749rather than the build; a container gets them as its environment variables
750when it starts, and its image's build gets none. Secrets reach a preview only when the pull
751request's author has Write or higher on the repository, whether a member
752or an [outside collaborator](/guides/access-and-roles/#outside-collaborators),
753or it is g1t working on its own. A preview of anyone else's pull request, such as
754one from a fork or by someone with Read or Triage, is built and runs with
755variables only, no secrets.
756
757For example, a `STRIPE_KEY` secret with a Production row holding the live
758key and a Preview row holding the test key gives every preview the test
759key.
760
761## What it costs
762
763Deployments come with the
764[g1t plan](/guides/usage-and-billing/#the-g1t-plan), $20 a month per
765workspace with $10 of usage included. There are **no quotas**: as many
766projects and previews as you like, and no count of builds, requests or
767domains ever stops or pauses an app. What deployments cost g1t is metered
768from the first unit, at Cloudflare's price plus 20%, and paid from the
769plan's $10 of included usage first; past it, it is charged up to your
770[spend limit](/guides/usage-and-billing/#limits).
771
772| | Costs g1t | You pay |
773| --- | --- | --- |
774| Projects, previews and the apps behind them | Next to nothing | Not charged |
775| Builds g1t runs, by the second, whether they succeed or fail (a [workflow's build](#deploying-from-a-workflow) is its job's Actions time instead) | About $0.001 a minute | About $0.0012 a minute |
776| Requests, to all of the workspace's apps | $0.30 a million | $0.36 a million |
777| CPU time your code spends computing (waiting on the network is not counted) | $0.02 a million ms | $0.024 a million ms |
778| Custom domains, with their certificates (a www/apex pair is two) | $0.10 a month | $0.12 a month, by the most the workspace had at once that month |
779| A container, while it is awake, by the minute (see the sizes below) | From $0.0129 an hour (`lite`) | From $0.0155 an hour |
780| Data a container sends | $0.05 a GB, at most | $0.06 a GB |
781
782A container's price is its size's memory, disk and vCPU while it is awake,
783with the Durable Object in front of it, at Cloudflare's
784[Containers prices](https://developers.cloudflare.com/containers/platform/pricing/)
785plus 20%. Cloudflare bills CPU only as it is used; g1t charges every vCPU
786as busy while the container is awake, so a container that mostly waits
787costs g1t less than it is charged. Data sent is charged at the most a GB
788costs anywhere ($0.05; $0.025 in North America and Europe), since g1t
789cannot tell where it went. Each minute it is awake is charged from its
790start, so a container woken by one request, and stopped five minutes after
791it, is charged five minutes.
792
793| Size | vCPU | Memory | Disk, and the largest image | An hour awake | Awake all month (730 hours) |
794| --- | --- | --- | --- | --- | --- |
795| `lite` | 1/16 | 256 MiB | 2 GB | $0.0155 | $11.28 |
796| `basic` | 1/4 | 1 GiB | 4 GB | $0.0404 | $29.46 |
797| `standard-1` | 1/2 | 4 GiB | 8 GB | $0.0956 | $69.77 |
798| `standard-2` | 1 | 6 GiB | 12 GB | $0.1616 | $117.95 |
799| `standard-3` | 2 | 8 GiB | 16 GB | $0.2708 | $197.68 |
800| `standard-4` | 4 | 12 GiB | 20 GB | $0.4880 | $356.24 |
801
802**Settings → Deployments** shows these with the price book's prices of the
803day. A container that is visited all day never sleeps, and costs the last
804column; one visited for an hour a day costs about a thirtieth of it.
805
806Builds are charged when they finish. Requests, CPU time, custom domains
807and container time are counted through the month, so your limit and
808**Billing** follow them as they happen, and charged once, on the first
809sweep after the month ends, as one line: *Deployments in 2026-10:
8101,240,000 requests, 3,100,000 CPU ms, 2 custom domains, 41.5 container
811hours and 2.3 GB sent*.
812
813A static site, or plain HTML, runs no code when it is visited: its files
814are served as they are, so serving it adds no requests or CPU time. Its
815builds are metered. A Workers project's code runs on every request, so its
816requests and CPU time are metered too. A container's Worker is metered the
817same way, and its container by the minute while it is awake.
818
819An app that no one visits costs nothing. Previews still come down when
820their pull request closes and after their idle days, to keep the list of
821what is up short.
822
823The only things that stop a workspace's apps are its own spend limit
824(apps pause with a notice, and come back once there is room; a paused
825container is stopped and starts no more) and the plan ending (they come
826down).
827
828Deployments used to be a $5 plan of their own. A Deployments subscription
829bought before keeps working until its current period ends, and is not
830renewed.
831
832### Seeing what you use
833
834- **Usage** shows **Deployments** with *Builds*
835 (build minutes and their cost), *Requests & CPU* and *Custom domains*,
836 in dollars, beside the rest of the workspace's usage. Requests and CPU
837 time are counted from Cloudflare's analytics every 10 minutes.
838- The **statement** on Billing lists every build (*Building acme/web to
839 production (48 s), paid by your plan's included usage*) and every
840 month's requests, CPU time and custom domains.
841- Each build's page shows how long it ran.
842
843## Turn it off
844
845- **For a project:** **Turn off deployments** under its **Settings →
846 Deployments**. Every app comes down at once. Turning it on again
847 rebuilds production.
848- **For the workspace:** when an owner ends the g1t plan on Billing,
849 deployments keep working until the date shown; then every app comes
850 down and nothing more is charged.
851
852Usage from the month that is under way is still charged once it ends.
853
854## How it works
855
8561. A pull request opens, or someone pushes. The deployments service hears
857 of it, checks that the workspace's plan is on, and reserves what the
858 build may cost (30 minutes of sandbox time) with billing. If billing
859 refuses (no paid plan, the spend limit reached, compute paused), the
860 build does not start: the deployment's status is **Skipped** with the
861 reason and what to do, and **Redeploy** shows the same reason.
8622. It starts a build in a sandbox of its own, the same machines that run
863 [GitHub Actions](/guides/actions/). The sandbox checks out
864 the commit with a read-only token that expires in 30 minutes. It
865 reaches the internet, as
866 [workflow jobs](/guides/actions/#what-a-job-can-reach) do, and it
867 stops at 30 minutes.
868 When it stops, what it cost is settled against what was reserved.
8693. The sandbox builds, then lists its files. g1t opens an upload with
870 Cloudflare for exactly those files and hands the sandbox a key that can
871 upload them and nothing else. Files Cloudflare already has are skipped.
8724. The sandbox sends the Worker's code to g1t, which puts the app in
873 g1t's [Workers for Platforms](https://developers.cloudflare.com/cloudflare-for-platforms/workers-for-platforms/)
874 namespace, under the name in its address. A container build goes as
875 [below](#containers).
8765. A request to `*.g1t.page` reaches g1t's dispatcher, which runs the app
877 by the name in the hostname. Nothing else is looked up. A request to a
878 custom domain reaches the same dispatcher through Cloudflare for SaaS;
879 it looks the hostname up once, in a key-value store kept at the edge.
880
881Your code never holds a Cloudflare credential, and apps are served from
882`g1t.page`, not `g1t.sh`, so they share no cookies or origin with the site
883you sign in to.
884
885### Containers
886
8871. The build finds it is a container, and asks g1t where to push its
888 image (the build's `registry` step). g1t mints a login to Cloudflare's
889 registry, `registry.cloudflare.com`, that can **push only** and lasts
890 **15 minutes**, and names the app's own repository,
891 `<account>/g1t-<app>`, with the build's id as its tag. Asked before
892 anything is built, so a build that could not push is not run.
8932. The sandbox starts a Docker Engine of its own (as a workflow job's),
894 builds the image for `linux/amd64`, checks its size against the
895 container size's disk, pushes it with that login, and reports the
896 image by its digest.
8973. g1t runs only an image by digest in that app's repository, and asks
898 the registry, with a pull login of its own, that the digest is there.
899 It puts the app's Worker in the namespace: g1t's own code, never the
900 project's, with a Durable Object class that holds the container, the
901 project's secrets and variables, and a token for the app to report its
902 running time with. Then it creates the app's Container application
903 (at most one instance, at the project's size, attached to that class),
904 or rolls it out to the new image.
9054. A request reaches the app's Worker through the dispatcher, as any
906 app's does. The Worker hands it to the app's Durable Object, which
907 starts the container if it is asleep, with the environment, waits for
908 it to answer on its port, and passes the request to it. Every minute it
909 is awake, the object charges the next minute ahead and reports it to g1t
910 (`POST /deployments/apps/<app>/meter` on the API, with its token); five
911 minutes after the last request it stops the container.
9125. A preview's container comes down with its pull request, its
913 application and Worker deleted together.
914
915Why it is built this way, from Cloudflare's documentation and Wrangler's
916own code (October 2026):
917
918- **Workers for Platforms runs containers.** A user Worker in a dispatch
919 namespace may bind Containers, as a Durable Object
920 ([WfP bindings](https://developers.cloudflare.com/cloudflare-for-platforms/workers-for-platforms/configuration/bindings/)).
921 Wrangler's `deploy --dispatch-namespace` deploys containers by finding
922 the Durable Object namespace of the class in that dispatch namespace and
923 creating a Container application attached to it
924 (`POST /accounts/{id}/containers/applications`, with
925 `durable_objects.namespace_id`), then rolling out new images
926 (`…/applications/{id}/rollouts`). So each app is a Worker of its own,
927 with a class of its own, isolated as every app is, and the dispatcher
928 routes to it unchanged. A single host Worker of g1t's could not run every
929 app's image: a Durable Object starts only the images its Worker's
930 configuration names.
931- **The default scheduling policy.** It takes one image and size for the
932 whole application, set through the API, which suits one app per
933 application; the `durable_object` policy is in beta, and needs the
934 image map uploaded with the Worker.
935- **Registry logins.** Cloudflare mints registry credentials with
936 `POST /accounts/{id}/containers/registries/registry.cloudflare.com/credentials`
937 and `{ "permissions": ["push"], "expiration_minutes": 15 }`, as Wrangler
938 does for its own pushes. They cannot be held to one repository, so g1t
939 holds the build to its app's: it names the repository, runs nothing but a
940 digest in it, and never gives a build a login that can pull, so no build
941 can read another app's image. g1t's own Cloudflare token never reaches
942 the sandbox.
943- **Sizes and limits.** Containers' instance types are `lite` (1/16 vCPU,
944 256 MiB, 2 GB) through `standard-4` (4 vCPU, 12 GiB, 20 GB); an image
945 may be as large as its instance's disk, and an account holds 50 GB of
946 images ([limits](https://developers.cloudflare.com/containers/platform/limits/)).
947- **Scale to zero.** Containers are billed every 10 ms while running, and
948 stop being billed once asleep. The Durable Object stops its container
949 itself after five minutes without a request, rather than leaving it to
950 the inactivity timeout, since the object's own minute alarm, which
951 meters it, would otherwise keep it awake.
952
953## Troubleshooting
954
955| You see | Do |
956| --- | --- |
957| "Deployments come with the g1t plan … and `<workspace>` does not have it" | An owner starts the g1t plan under Billing. |
958| A deployment **Skipped**, saying it reached its spend limit or that new compute is paused | The build was refused before it started. Follow the link in the message: raise the spend limit, prepay, or answer the spike, on Billing. |
959| "Stopped: unusual CPU use" | The build looked like it was mining. Contact support if it was a real build; see [abuse and mining](/guides/guardrails/#abuse-and-mining). |
960| "found nothing to serve" | Add a `build` script, an `index.html`, a Workers config or a `Dockerfile`; or set **Output directory**. |
961| "Docker deployments are switched off on this g1t" | The project is a container, and this g1t (a self-hosted one) runs none. Set **Build as** to Static site or Workers if it can be built as one. See [Docker deployments](#docker-deployments). |
962| "**Build as** is Container, but this project has no Dockerfile" | Add a `Dockerfile`, or set **Dockerfile** to its path. |
963| "Could not run as a Worker: …; deploying as a container" | The Node.js server cannot run as a Worker, for the reason given; it deploys as a container. See [Node.js servers as Workers](#nodejs-servers-as-workers) for what to change, or set **Build as** to **Container** to skip the attempt. |
964| "Built as a … site; Dockerfile is not used" | A site is preferred to a container. Set **Build as** to **Container** to deploy the `Dockerfile` instead. |
965| "The image is 2.4 GB, more than the 2.0 GB an app of its size (lite) can hold" | Make the image smaller (a slim base image, a multi-stage build), or choose a larger **Container size**. |
966| A container app says "The app did not start" | It did not answer on its port within 90 seconds. Make it listen on `0.0.0.0` at `PORT`, or `EXPOSE` the port it uses in its Dockerfile. Its output is in the build's log only while it builds; run the image locally with `docker run -e PORT=8080 -p 8080:8080` to see it start. |
967| "the output directory `x` does not exist after the build" | The build wrote elsewhere: check its log, then fix **Output directory**. |
968| "`d1_databases` is not provisioned on g1t.page yet" | The app deployed without that binding. See [Workers projects](#workers-projects). |
969| "`triggers.crons` (1 schedule) is not set up on g1t.page yet" | The app deployed, but its `scheduled` handler never runs. See [Workers projects](#workers-projects). |
970| A preview page says "This preview is not up" | It came down (see [When apps come down](#when-apps-come-down)). Push, or choose **Redeploy**. |
971| "Custom domains are being switched on" | Custom domains are not on for g1t.page yet. Domains you add are kept, and set up by themselves once they are. |
972| A domain stays at **Waiting for DNS** | Check the record against the one listed, remove other `A`/`AAAA` records for the same name, then choose **Check now**. |
973| A domain says "This domain is not set up" | It points at g1t, but no project has added it. Add it under **Settings → Domains**. |
974| "This pull request's commit no longer exists" | The pull request's head was force-pushed over, or its branch deleted, after the build was asked for. Push to the pull request again, or close it. g1t does not try that commit again. |
975| g1t/deploy says "The job's token needs `deployments: write`" | Add `deployments: write` to the workflow's or the job's `permissions:`. A pull request's run from outside gets a read-only token and cannot deploy. |
976| g1t/deploy says "Name the project to build" | The repository has several projects with Deployments on: give `project:`. |
977| g1t/deploy says "g1t.page deploys branches and pull requests" | The run is for a tag. Run the workflow on a branch, such as on a push to `main`. |
978| "The workflow job was cancelled before the build finished" | The job that opened the build (`g1t/deploy`) was cancelled, or ended another way the message names, before `g1t-runner deploy` reported. Run the job again. |
979| "The build did not finish in 45 minutes" | The build stopped reporting: a build stops at 30 minutes, and one not heard from after 45 is failed. Make the build faster, or build less for previews with **Build command**. |
980
981## Running your own g1t
982
983Deployments need a Workers for Platforms namespace and a zone for apps:
984
9851. Create the namespace: `npx wrangler dispatch-namespace create g1t-deployments`.
9862. Add a proxied wildcard DNS record (`*`, `AAAA`, `100::`) on the apps'
987 zone, and set the zone in `services/pages/wrangler.jsonc`.
9883. Create an API token with **Workers Scripts: Edit** and **Account
989 Analytics: Read**, and store it:
990 `npx wrangler secret put CLOUDFLARE_API_TOKEN` in `services/deployments`.
9914. Deploy `services/deployments`, `services/pages`, and the runner.
992
993Docker deployments also need the token to have **Containers: Edit** and
994a Workers Paid plan with Containers; `CONTAINERS` in
995`services/deployments/wrangler.jsonc` is `on`, and `off` turns them off
996for an account without Containers. See
997[Docker deployments](/guides/deploy-to-cloudflare/#docker-deployments)
998for what to check on a new account.
999
1000Custom domains also need Cloudflare for SaaS on the apps' zone. Turn it on
1001under the zone's **SSL/TLS → Custom Hostnames**, give the token **SSL and
1002Certificates: Edit** on the zone, and run `scripts/setup-custom-domains.sh`:
1003it creates the `g1t-domains` KV namespace, the fallback origin's DNS record
1004(`domains`, `AAAA`, `100::`, proxied) and sets it as the fallback origin.
1005The dispatcher's `*/*` route on the zone, in `services/pages/wrangler.jsonc`,
1006is what brings custom domains' traffic to it.
1007
1008Without a card processor configured, every feature is on and nothing is
1009charged.