1---
2title: All on Cloudflare
3description: A git host on one Cloudflare account. One Worker, one bucket, two Durable Object namespaces, and dynamically loaded Workers hashing large pushes.
4---
5
6The examples use `Authentication` from
7[Getting Started](/git/getting-started): the application's request middleware.
8
9import { Code, Tabs, TabItem } from "@astrojs/starlight/components";
10
11Everything on one account, with large pushes hashed by dynamically
12loaded Workers so nothing leaves Cloudflare:
13
14```typescript
15// src/git.ts
16import * as Cloudflare from "alchemy/Cloudflare";
17import * as Git from "alchemy/Git";
18import * as GitHasher from "alchemy/Git/Hasher";
19import * as Effect from "effect/Effect";
20import * as Layer from "effect/Layer";
21import * as HttpRouter from "effect/http/HttpRouter";
22import * as Http from "alchemy/Http";
23
24export const GitObjects = Cloudflare.R2.Bucket("GitObjects");
25
26const GitLive = Git.ApiLive.pipe(
27 Layer.provide(Git.ApiHandlersLive),
28 Layer.provide(Authentication.layer),
29 Layer.provide(Git.ReposDurableObject),
30 Layer.provide(Git.RegistryDurableObject),
31 Layer.provide(GitHasher.HasherWorkerLoader()),
32 Layer.provide(Git.BlobStoreR2(GitObjects)),
33);
34
35export default class GitHost extends Cloudflare.Worker<GitHost>()(
36 "Git",
37 { main: import.meta.url, ...Git.GIT_WORKER_OPTIONS },
38 Effect.gen(function* () {
39 const fetch = yield* HttpRouter.toHttpEffect(GitLive.pipe(Layer.provide(Http.Platform)));
40 return { fetch };
41 }),
42) {}
43```
44
45```typescript
46// alchemy.run.ts
47import * as Alchemy from "alchemy";
48import * as Cloudflare from "alchemy/Cloudflare";
49import * as Effect from "effect/Effect";
50import GitHost from "./src/git.ts";
51
52export default Alchemy.Stack(
53 "GitService",
54 { providers: Cloudflare.providers(), state: Cloudflare.state() },
55 Effect.gen(function* () {
56 const git = yield* GitHost;
57 return { url: git.url.as<string>() };
58 }),
59);
60```
61
62<Tabs syncKey="pkgManager">
63 <TabItem label="bun" icon="bun">
64 <Code code="bun alchemy deploy" lang="sh" />
65 </TabItem>
66 <TabItem label="npm" icon="npm">
67 <Code code="npm run alchemy deploy" lang="sh" />
68 </TabItem>
69 <TabItem label="pnpm" icon="pnpm">
70 <Code code="pnpm alchemy deploy" lang="sh" />
71 </TabItem>
72 <TabItem label="yarn" icon="seti:yarn">
73 <Code code="yarn alchemy deploy" lang="sh" />
74 </TabItem>
75</Tabs>
76
77## What gets created
78
79- An R2 bucket for packs, clone bundles, and spilled push bodies.
80- A Worker with `nodejs_compat`, a 300 s CPU limit, a `worker_loader`
81 binding for the hasher, and the shared secret bound as a secret.
82- Two Durable Object namespaces on that Worker, one per repository and
83 one for the registry, each with SQLite storage.
84
85Nothing runs until a request arrives, and a repository that receives
86no traffic costs its storage.
87
88## Variations
89
90`HasherInline` is enough until pushes of tens of megabytes matter:
91
92```diff lang="typescript"
93const GitLive = Git.ApiLive.pipe(
94 Layer.provide(Git.ApiHandlersLive),
95 Layer.provide(Authentication.layer),
96 Layer.provide(Git.ReposDurableObject),
97 Layer.provide(Git.RegistryDurableObject),
98- Layer.provide(GitHasher.HasherWorkerLoader()),
99+ Layer.provide(Git.HasherInline),
100 Layer.provide(Git.BlobStoreR2(GitObjects)),
101);
102```
103
104A registry on D1 replicates name lookups instead of serving them from
105one region:
106
107```diff lang="typescript"
108+export const RepoIndex = Cloudflare.D1.Database("RepoIndex");
109+
110const GitLive = Git.ApiLive.pipe(
111 Layer.provide(Git.ApiHandlersLive),
112 Layer.provide(Authentication.layer),
113 Layer.provide(Git.ReposDurableObject),
114- Layer.provide(Git.RegistryDurableObject),
115+ Layer.provide(Git.RegistryD1(RepoIndex)),
116 Layer.provide(GitHasher.HasherWorkerLoader()),
117 Layer.provide(Git.BlobStoreR2(GitObjects)),
118);
119```
120
121A custom domain is a Worker option, so clone URLs read
122`https://git.example.com/acme/web.git`. [Custom domains &
123routes](/cloudflare/networking/custom-domains) covers it, and
124the [application example](https://github.com/alchemy-run/alchemy/tree/main/examples/cloudflare-git-service)
125serves a browser UI and Git on the same origin.