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.

128 linesCodeBlameRaw
1---
2title: How a self-hosted g1t runs
3description: What runs inside the Docker Compose installation, what stands in for each Cloudflare service, and what each part of g1t does when you run it yourself.
4---
5
6[Run g1t yourself](/guides/self-hosting/) starts g1t with Docker Compose.
7This page is for the person who keeps that installation running: what is
8inside it, what stands in for each Cloudflare service g1t.sh uses, and
9where the limits are. How the services themselves work, wherever they
10run, is in the [Architecture](/architecture/overview/) section.
11
12## The shape
13
14g1t is a set of Workers, one per service, each with its own SQLite
15database. They call each other over HTTP (`POST /rpc/<method>` with a JSON
16body). A self-hosted installation runs the same Workers, built from the
17same source, in workerd, the open-source Workers runtime, under
18`wrangler dev`:
19
20- **The site and every core service** run in one workerd process on
21 `G1T_PORT` (8787). Their databases, key-value stores and queues are
22 SQLite files on the `g1t-data` volume.
23- **The API and the MCP server** run in a second workerd on `API_PORT`
24 (8789), started once the first answers. It reaches the other services
25 through Wrangler's local registry.
26- **The status page** runs in a process of its own (`status.sh`), so it
27 keeps answering when the site does not.
28
29Three Cloudflare services are replaced by small Workers bound in their
30place, so no service's code changes:
31
32| Binding | Stands in for | Self-hosted |
33| --- | --- | --- |
34| `GITSTORE` | Cloudflare Artifacts, where g1t.sh keeps repositories (the repos service's git store) | `deploy/self-host/workers/gitstore`, in front of the git store: bare repositories on the `g1t-git` volume, served with `git http-backend`. Access tokens are HMAC-signed, scoped and expire. Forks are clones with hard-linked objects. |
35| `EMAIL` | Cloudflare Email Sending | `deploy/self-host/workers/mail`: logs each message and hands it to Mailpit, which can relay through your SMTP server |
36| The runner | Containers | `deploy/self-host/workers/off`: answers that the feature is off, which every page that uses them shows |
37
38## What is in `deploy/self-host`
39
40| File | What it is |
41| --- | --- |
42| `docker-compose.yml` | The services: `g1t`, `status`, `gitstore`, `rustfs` (S3-compatible storage), `storage-setup` (makes the buckets once, then exits) and `mailpit`. Volumes: `g1t-data`, `g1t-git`, `g1t-objects`, `g1t-status`, `g1t-secrets`. |
43| `Dockerfile` | Compiles the Rust services to WebAssembly and builds the site, as g1t.sh's deploys do. The image has Node, Wrangler, workerd and the built Workers. |
44| `start.sh` | Makes the sealing keys on first start, writes the configs, applies database migrations, then starts both workerd processes and the scheduler. |
45| `configs.mjs` | Writes each Worker's self-hosted config from its `wrangler.jsonc`: drops routes, the account and placement, binds the stand-ins above, points packages, backups, clone packs and files in pages at the S3 store, and replaces g1t.sh's addresses with yours. What runs, and what is bound to the off stand-in, comes from each part's `self_host` field in `deploy/stack.jsonc`. |
46| `scheduler.mjs` | Runs scheduled jobs (below). |
47| `gitstore/` | The git store. |
48| `workers/` | The three stand-ins. |
49| `smoke.sh` | The end-to-end check in [Check an installation](/guides/self-hosting/#check-an-installation). |
50
51## Each part
52
53| Part | Self-hosted |
54| --- | --- |
55| `apps/web` | Runs: the site, on `G1T_PORT` |
56| `apps/api` | Runs in the second workerd, on `API_PORT`. `API_URL` is its OAuth issuer and MCP is at `/mcp` on it. Actions artifacts need the runner, which is off. |
57| `apps/status` | Runs in a process of its own; see [the status page](/guides/self-hosting/#the-status-page) |
58| `apps/sudo` | Not run. It is g1t.sh's staff console, behind Cloudflare Access. |
59| `apps/docs` | Not run: docs.g1t.sh serves these pages |
60| `services/identity` | Runs; its email goes to Mailpit |
61| `services/repos` | Runs, against the git store. Nightly backups are queued but cut by the runner, which is off, so none are made yet. Clone packs are kept in the `g1t-git-packs` bucket. |
62| `services/work` | Runs |
63| `services/events` | Runs: the event bus, on local queues |
64| `services/projects` | Runs |
65| `services/search` | Runs: site search is SQLite full-text search |
66| `services/chat` | Runs, with its Durable Objects; custom emoji are kept with avatars |
67| `services/artifacts` | Runs: [artifacts](/guides/artifacts/) and their spaces, with their live rooms. Files people add are kept in the `g1t-docs-files` bucket. Search matches words, not meaning, because there is no embedding model. |
68| `services/telemetry` | Runs. Observability for projects: each project's logs, traces, metrics and errors in a Durable Object's SQLite storage. Software sends OpenTelemetry to the API's port, at `http://localhost:8789/otlp` ([Observability](/guides/observability/)). Deployments on `g1t.page`, the other source, are off. |
69| `services/notify` | Runs. Notifications are live in open tabs; browser push needs a VAPID key pair, which an installation does not make. Push to the phone app goes through Expo and needs nothing set; `PHONE_PUSH=off` sends phones nothing ([the phone app](/guides/self-hosting/#the-phone-app)). |
70| `services/billing` | Runs with nothing charged and no usage limit |
71| `services/integrations` | Runs |
72| `services/webhooks` | Runs, retries included |
73| `services/actions` | Runs. Jobs need the runner, which is off. |
74| `services/packages` | Runs, with files in the `g1t-packages` bucket and no request size limit |
75| `services/security` | Runs, with its sweeps on schedule |
76| `services/deployments` | Runs with no Cloudflare token, so nothing deploys; its pages and settings still show |
77| `services/runner` | Off: checks run by g1t, merge queue testing and workflow jobs |
78| `services/pages` | Not run: there is no `g1t.page` |
79| `services/og` | Not run: pages have no social cards |
80
81## Scheduled jobs
82
83workerd runs a Worker's scheduled handler only when asked. `scheduler.mjs`
84asks once a minute, inside the `g1t` container, through Wrangler's local
85API, for each due schedule in the Workers' own configs: repos, events,
86identity, security, webhooks, packages and docs. A job still running from
87the minute before is not started again, and one is given up on after ten
88minutes.
89
90Not run: Actions schedules (`on: schedule`, which would need the runner),
91billing and deployments.
92
93`node deploy/self-host/scheduler.mjs --once /data/generated/schedules.json`
94runs every job once and exits non-zero if one failed.
95
96## Storage
97
98Every bucket is on the bundled RustFS, or on any S3-compatible store you
99point `S3_ENDPOINT` at (MinIO, Ceph, Garage or AWS S3). `storage-setup`
100makes them on the bundled store; on another store, make them yourself.
101
102| Bucket | Holds | Setting |
103| --- | --- | --- |
104| `g1t-packages` | Container image layers and other package files | `S3_BUCKET` |
105| `g1t-git-packs` | Packs for fresh clones, a cache. Give it a rule that deletes objects after 7 days and aborts multipart uploads after a day. | `PACK_S3_BUCKET` |
106| `g1t-backups` | Nightly repository backups, once the runner cuts them | `BACKUP_S3_BUCKET` |
107| `g1t-docs-files` | Images and files added to pages and artifacts | `DOCS_S3_BUCKET` |
108
109Requests to the store are signed with AWS Signature Version 4, path style.
110
111## Limits
112
113- **Use it on `localhost` or a private network you trust.** `wrangler dev`
114 is a development server: it answers its own development endpoints
115 (`/cdn-cgi/...`) on the same port as the site.
116- **One machine.** Every database is a SQLite file with one writer. It
117 suits a team, not a large organisation.
118- **HTTPS anywhere but `localhost`.** Sign-in cookies are `Secure`; put a
119 proxy with a certificate in front and set `PUBLIC_URL` to its
120 `https://` address.
121- **If the API says a binding is `[not connected]`**, restart the
122 container: the API finds the other services through Wrangler's local
123 registry.
124- **Building takes minutes and several GB**, because every Rust service is
125 compiled from source. A change that does not compile breaks the image,
126 so build from a released commit.
127- **Some links still leave your installation**: the docs links go to
128 docs.g1t.sh, and the Status links to status.g1t.sh.