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.

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