Skip to content
181 linesCodeBlameRaw
1# Self-hosted g1t, phase 1: the core forge on your own machine.
2#
3# docker compose -f deploy/self-host/docker-compose.yml up --build
4#
5# Then open http://localhost:8787. Mail (the confirmation link at sign-up)
6# lands in Mailpit at http://localhost:8025. The API is at
7# http://localhost:8789 and the MCP server at http://localhost:8789/mcp.
8#
9# What runs: the site and every core service in one workerd (g1t), git
10# repositories as bare repos on a volume (gitstore), the API in a second
11# workerd beside it, packages' files, backups and the clone pack cache in
12# RustFS (S3-compatible storage), and Mailpit for mail.
13# Agents, deployments, context search and billing are off. See
14# docs/SELF_HOSTING.md.
15name: g1t
16
17services:
18 g1t:
19 build:
20 context: ../..
21 dockerfile: deploy/self-host/Dockerfile
22 ports:
23 - "${G1T_PORT:-8787}:8787"
24 - "${API_PORT:-8789}:8789"
25 environment:
26 # Where people reach this installation. Links in mail, clone URLs and
27 # the site's meta tags point here.
28 PUBLIC_URL: ${PUBLIC_URL:-http://localhost:8787}
29 # Where the API (REST, OAuth) is reached, and its OAuth issuer; empty
30 # means PUBLIC_URL's host on API_PORT. MCP_URL, empty, is API_URL/mcp.
31 API_URL: ${API_URL:-}
32 MCP_URL: ${MCP_URL:-}
33 # Where repository files and avatars are served: a host of its own
34 # that reaches this container, so they never share the site's
35 # cookies; empty serves them under PUBLIC_URL/-/usercontent.
36 USERCONTENT_URL: ${USERCONTENT_URL:-}
37 API_PORT: ${API_PORT:-8789}
38 GITSTORE_URL: http://gitstore:8080
39 GITSTORE_SECRET_FILE: /secrets/gitstore
40 MAIL_URL: ${MAIL_URL:-http://mailpit:8025}
41 MAIL_FROM: ${MAIL_FROM:-g1t <noreply@localhost>}
42 # Your own GitHub App, for "Continue with GitHub" and importing from
43 # GitHub. Leave these unset to have neither. See the self-hosting guide.
44 GITHUB_APP_ID: ${GITHUB_APP_ID:-}
45 GITHUB_APP_SLUG: ${GITHUB_APP_SLUG:-}
46 GITHUB_APP_CLIENT_ID: ${GITHUB_APP_CLIENT_ID:-}
47 GITHUB_APP_CLIENT_SECRET: ${GITHUB_APP_CLIENT_SECRET:-}
48 GITHUB_APP_PRIVATE_KEY: ${GITHUB_APP_PRIVATE_KEY:-}
49 GITHUB_APP_WEBHOOK_SECRET: ${GITHUB_APP_WEBHOOK_SECRET:-}
50 # open: anyone may register. invite: a new account needs an invite
51 # code, as on g1t.sh; the owners of INVITE_STAFF_WORKSPACES (slugs,
52 # comma separated) invite without limit, everyone else INVITES_PER_USER.
53 REGISTRATION_MODE: ${REGISTRATION_MODE:-open}
54 INVITE_STAFF_WORKSPACES: ${INVITE_STAFF_WORKSPACES:-}
55 INVITES_PER_USER: ${INVITES_PER_USER:-}
56 # Where summaries of new access requests go; empty sends none.
57 WAITLIST_NOTIFY_EMAIL: ${WAITLIST_NOTIFY_EMAIL:-}
58 # Packages' files (container images and the rest), in RustFS below or
59 # any S3-compatible store. S3_PUBLIC_ENDPOINT, when clients can reach
60 # the store, sends large downloads there directly.
61 S3_ENDPOINT: ${S3_ENDPOINT:-http://rustfs:9000}
62 S3_BUCKET: ${S3_BUCKET:-g1t-packages}
63 S3_REGION: ${S3_REGION:-us-east-1}
64 S3_ACCESS_KEY_ID: ${S3_ACCESS_KEY_ID:-g1t}
65 S3_SECRET_ACCESS_KEY: ${S3_SECRET_ACCESS_KEY:-g1t-packages-secret}
66 S3_PUBLIC_ENDPOINT: ${S3_PUBLIC_ENDPOINT:-}
67 # Nightly backups' bundles and manifests, in a bucket of their own on
68 # the same store (docs/SELF_HOSTING.md, "Backups").
69 BACKUP_S3_BUCKET: ${BACKUP_S3_BUCKET:-g1t-backups}
70 # Packs kept for fresh clones, so the next clone of the same commit
71 # does not rebuild one; storage-setup expires them after 7 days.
72 PACK_S3_BUCKET: ${PACK_S3_BUCKET:-g1t-git-packs}
73 volumes:
74 - g1t-data:/data
75 - g1t-secrets:/secrets:ro
76 depends_on:
77 gitstore:
78 condition: service_healthy
79 mailpit:
80 condition: service_started
81 storage-setup:
82 condition: service_completed_successfully
83 restart: unless-stopped
84
85 # The status page, in a process of its own so it stays up when the site
86 # does not: http://localhost:8788. It checks the site every minute.
87 status:
88 build:
89 context: ../..
90 dockerfile: deploy/self-host/Dockerfile
91 command: ["bash", "deploy/self-host/status.sh"]
92 ports:
93 - "${STATUS_PORT:-8788}:8788"
94 environment:
95 PUBLIC_URL: ${PUBLIC_URL:-http://localhost:8787}
96 # How the status page reaches the site, from inside Compose.
97 STATUS_CHECK_URL: http://g1t:8787
98 # A public repository, `workspace/repo`, whose branches it lists as a
99 # clone would. Empty: git is not checked.
100 STATUS_PROBE_REPO: ${STATUS_PROBE_REPO:-}
101 volumes:
102 - g1t-status:/data
103 restart: unless-stopped
104
105 gitstore:
106 build:
107 context: ./gitstore
108 environment:
109 GITSTORE_URL: http://gitstore:8080
110 GITSTORE_SECRET_FILE: /secrets/gitstore
111 volumes:
112 - g1t-git:/data/git
113 - g1t-secrets:/secrets
114 # Not published: only the g1t container reaches it.
115 restart: unless-stopped
116
117 # Packages' files, backups and clone packs, in RustFS (S3-compatible).
118 # Not published: only the g1t container reaches it, unless you publish
119 # 9000 and set S3_PUBLIC_ENDPOINT. Any S3-compatible store works in its
120 # place (S3_ENDPOINT).
121 rustfs:
122 image: ${RUSTFS_IMAGE:-rustfs/rustfs:1.0.1}
123 environment:
124 RUSTFS_ACCESS_KEY: ${S3_ACCESS_KEY_ID:-g1t}
125 RUSTFS_SECRET_KEY: ${S3_SECRET_ACCESS_KEY:-g1t-packages-secret}
126 RUSTFS_CONSOLE_ENABLE: "false"
127 volumes:
128 - g1t-objects:/data
129 healthcheck:
130 test: ["CMD", "curl", "-fsS", "-o", "/dev/null", "http://localhost:9000/health/ready"]
131 interval: 5s
132 retries: 20
133 restart: unless-stopped
134
135 # Makes the buckets once, then exits: packages' files, backups, and
136 # clone packs, with a lifecycle rule on the packs' bucket that deletes
137 # packs 7 days old and uploads left unfinished after a day.
138 storage-setup:
139 image: ${AWS_CLI_IMAGE:-amazon/aws-cli:2.37.10}
140 depends_on:
141 rustfs:
142 condition: service_healthy
143 entrypoint:
144 - sh
145 - -c
146 - >-
147 set -e;
148 for bucket in "$$S3_BUCKET" "$$BACKUP_S3_BUCKET" "$$PACK_S3_BUCKET"; do
149 aws s3api head-bucket --bucket "$$bucket" >/dev/null 2>&1 || aws s3api create-bucket --bucket "$$bucket" >/dev/null;
150 done;
151 aws s3api put-bucket-lifecycle-configuration --bucket "$$PACK_S3_BUCKET" --lifecycle-configuration
152 '{"Rules":[{"ID":"expire-packs","Status":"Enabled","Filter":{"Prefix":"packs/"},"Expiration":{"Days":7}},{"ID":"abort-unfinished-uploads","Status":"Enabled","Filter":{"Prefix":""},"AbortIncompleteMultipartUpload":{"DaysAfterInitiation":1}}]}';
153 echo "buckets ready"
154 environment:
155 AWS_ENDPOINT_URL: http://rustfs:9000
156 AWS_ACCESS_KEY_ID: ${S3_ACCESS_KEY_ID:-g1t}
157 AWS_SECRET_ACCESS_KEY: ${S3_SECRET_ACCESS_KEY:-g1t-packages-secret}
158 AWS_DEFAULT_REGION: ${S3_REGION:-us-east-1}
159 S3_BUCKET: ${S3_BUCKET:-g1t-packages}
160 BACKUP_S3_BUCKET: ${BACKUP_S3_BUCKET:-g1t-backups}
161 PACK_S3_BUCKET: ${PACK_S3_BUCKET:-g1t-git-packs}
162
163 mailpit:
164 image: axllent/mailpit:latest
165 ports:
166 - "${MAILPIT_PORT:-8025}:8025"
167 # To deliver for real, relay through your SMTP server:
168 # environment:
169 # MP_SMTP_RELAY_HOST: smtp.example.com
170 # MP_SMTP_RELAY_PORT: "587"
171 # MP_SMTP_RELAY_USERNAME: ...
172 # MP_SMTP_RELAY_PASSWORD: ...
173 # MP_SMTP_RELAY_ALL: "true"
174 restart: unless-stopped
175
176volumes:
177 g1t-data:
178 g1t-objects:
179 g1t-status:
180 g1t-git:
181 g1t-secrets: