Skip to content
129 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.
10
11## The shape
12
13g1t is a set of Workers, one per service, each with its own SQLite
14database. They call each other over HTTP (`POST /rpc/<method>` with a JSON
15body). A self-hosted installation runs the same Workers, built from the
16same source, in workerd, the open-source Workers runtime, under
17`wrangler dev`:
18
19- **The site and every core service** run in one workerd process on
20 `G1T_PORT` (8787). Their databases, key-value stores and queues are
21 SQLite files on the `g1t-data` volume.
22- **The API and the MCP server** run in a second workerd on `API_PORT`
23 (8789), started once the first answers. It reaches the other services
24 through Wrangler's local registry.
25- **The status page** runs in a process of its own (`status.sh`), so it
26 keeps answering when the site does not.
27
28Three Cloudflare services are replaced by small Workers bound in their
29place, so no service's code changes:
30
31| Binding | Stands in for | Self-hosted |
32| --- | --- | --- |
33| `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. |
34| `EMAIL` | Cloudflare Email Sending | `deploy/self-host/workers/mail`: logs each message and hands it to Mailpit, which can relay through your SMTP server |
35| The runner, the context hub and the model proxy | Containers, Vectorize, Workers AI and AI Gateway | `deploy/self-host/workers/off`: answers that the feature is off, which every page that uses them shows |
36
37## What is in `deploy/self-host`
38
39| File | What it is |
40| --- | --- |
41| `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`. |
42| `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. |
43| `start.sh` | Makes the sealing keys on first start, writes the configs, applies database migrations, then starts both workerd processes and the scheduler. |
44| `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`. |
45| `scheduler.mjs` | Runs scheduled jobs (below). |
46| `gitstore/` | The git store. |
47| `workers/` | The three stand-ins. |
48| `smoke.sh` | The end-to-end check in [Check an installation](/guides/self-hosting/#check-an-installation). |
49
50## Each part
51
52| Part | Self-hosted |
53| --- | --- |
54| `apps/web` | Runs: the site, on `G1T_PORT` |
55| `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. |
56| `apps/status` | Runs in a process of its own; see [the status page](/guides/self-hosting/#the-status-page) |
57| `apps/sudo` | Not run. It is g1t.sh's staff console, behind Cloudflare Access. |
58| `apps/docs` | Not run: docs.g1t.sh serves these pages |
59| `services/identity` | Runs; its email goes to Mailpit |
60| `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. |
61| `services/work` | Runs |
62| `services/events` | Runs: the event bus, on local queues |
63| `services/projects` | Runs |
64| `services/search` | Runs: site search is SQLite full-text search |
65| `services/chat` | Runs, with its Durable Objects; custom emoji are kept with avatars |
66| `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. |
67| `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)). |
68| `services/agents` | Runs, but replies need the model proxy, which is off, so an agent answers with a short apology. [Customize](/guides/customize/) works: marketplaces and plugins are read from GitHub and from repositories here, and connectors' keys and sign-ins are sealed under `CONNECTORS_KEY`, one of the keys `start.sh` makes. `GITHUB_TOKEN` is optional on it, for a higher rate limit when reading GitHub. |
69| `services/billing` | Runs with nothing charged and no usage limit |
70| `services/integrations` | Runs |
71| `services/webhooks` | Runs, retries included |
72| `services/actions` | Runs. Jobs need the runner, which is off. |
73| `services/packages` | Runs, with files in the `g1t-packages` bucket and no request size limit |
74| `services/security` | Runs, with its sweeps on schedule |
75| `services/deployments` | Runs with no Cloudflare token, so nothing deploys; its pages and settings still show |
76| `services/runner` | Off: agents, checks run by g1t, merge queue testing and workflow jobs |
77| `services/context` | Off: the context hub |
78| `services/models` | Off: g1t's hosted models |
79| `services/pages` | Not run: there is no `g1t.page` |
80| `services/og` | Not run: pages have no social cards |
81
82## Scheduled jobs
83
84workerd runs a Worker's scheduled handler only when asked. `scheduler.mjs`
85asks once a minute, inside the `g1t` container, through Wrangler's local
86API, for each due schedule in the Workers' own configs: repos, events,
87identity, security, webhooks, packages and docs. A job still running from
88the minute before is not started again, and one is given up on after ten
89minutes.
90
91Not run: Actions schedules (`on: schedule`, which would need the runner),
92billing and deployments.
93
94`node deploy/self-host/scheduler.mjs --once /data/generated/schedules.json`
95runs every job once and exits non-zero if one failed.
96
97## Storage
98
99Every bucket is on the bundled RustFS, or on any S3-compatible store you
100point `S3_ENDPOINT` at (MinIO, Ceph, Garage or AWS S3). `storage-setup`
101makes them on the bundled store; on another store, make them yourself.
102
103| Bucket | Holds | Setting |
104| --- | --- | --- |
105| `g1t-packages` | Container image layers and other package files | `S3_BUCKET` |
106| `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` |
107| `g1t-backups` | Nightly repository backups, once the runner cuts them | `BACKUP_S3_BUCKET` |
108| `g1t-docs-files` | Images and files added to pages and artifacts | `DOCS_S3_BUCKET` |
109
110Requests to the store are signed with AWS Signature Version 4, path style.
111
112## Limits
113
114- **Use it on `localhost` or a private network you trust.** `wrangler dev`
115 is a development server: it answers its own development endpoints
116 (`/cdn-cgi/...`) on the same port as the site.
117- **One machine.** Every database is a SQLite file with one writer. It
118 suits a team, not a large organisation.
119- **HTTPS anywhere but `localhost`.** Sign-in cookies are `Secure`; put a
120 proxy with a certificate in front and set `PUBLIC_URL` to its
121 `https://` address.
122- **If the API says a binding is `[not connected]`**, restart the
123 container: the API finds the other services through Wrangler's local
124 registry.
125- **Building takes minutes and several GB**, because every Rust service is
126 compiled from source. A change that does not compile breaks the image,
127 so build from a released commit.
128- **Some links still leave your installation**: the docs links go to
129 docs.g1t.sh, and the Status links to status.g1t.sh.