1---
2title: Drizzle Postgres on Fly
3description: Connect a Fly Service to Managed Postgres and deploy reviewed Drizzle migrations.
4---
5
6Run Drizzle in a `Fly.Service`, with [Fly Managed Postgres](/fly/data/postgres) on the same organization's private network. Start with [Fly setup](/fly/setup), the [Service tutorial](/fly/tutorial/part-1), and a `src/schema.ts` exporting `Users` from the [Postgres client guide](/sql/drizzle/postgres).
7
8## Install the database dependencies
9
10```sh
11pnpm add drizzle-orm@1.0.0-rc.5-ab785fc @effect/sql-pg pg
12pnpm add -D drizzle-kit@1.0.0-rc.5-ab785fc
13```
14
15The Drizzle versions match the repository's Effect integration.
16
17## Configure migration generation
18
19```typescript
20// drizzle.config.ts
21import { defineConfig } from "drizzle-kit";
22
23export default defineConfig({
24 dialect: "postgresql",
25 schema: "./src/schema.ts",
26 out: "./drizzle",
27 dbCredentials: { url: process.env.DATABASE_URL! },
28});
29```
30
31`DATABASE_URL` is the direct Postgres URL used by the migration runner, not the pooled runtime URL.
32
33## Generate after changing the schema
34
35```sh
36pnpm exec drizzle-kit generate
37```
38
39Review the generated SQL and snapshots, including any rename decisions, before applying them.
40
41## Commit the migration
42
43```sh
44git add src/schema.ts drizzle.config.ts drizzle
45git diff --cached
46```
47
48```sh
49git commit -m "Add users migration"
50```
51
52Commit the schema, SQL, and snapshots together; CI deploys this reviewed history rather than generating it.
53
54## Declare the cluster
55
56```typescript
57// src/db.ts
58import * as Fly from "alchemy/Fly";
59
60export const Db = Fly.Postgres("Db", { region: "iad" });
61```
62
63This provisions a billed Managed Postgres cluster, not an unmanaged Postgres Machine.
64
65## Keep the driver outside the bundle
66
67```diff lang="typescript"
68 // src/api.ts — the Service from the tutorial
69 {
70 main: import.meta.url,
71 region: "iad",
72+ build: { install: ["pg"] },
73 }
74```
75
76`pg` must be installed in the Service image because bundling its CommonJS exports breaks the driver's interop.
77
78## Bind the cluster
79
80```diff lang="typescript"
81+import { Db } from "./db.ts";
82
83 Effect.gen(function* () {
84+ const conn = yield* Fly.ConnectPostgres(Db);
85 return {
86 fetch: Effect.succeed(HttpServerResponse.text("Hello from Fly!")),
87 };
88-}),
89+}).pipe(Effect.provide(Fly.ConnectPostgresHttp)),
90```
91
92The binding attaches the cluster to the Service's App and supplies pooled and direct connection Effects. These excerpts use the tutorial's minimal text handler until the database is migrated.
93
94## Provision before the first migration
95
96```sh
97pnpm alchemy deploy
98```
99
100Keep the tutorial's `yield* Api` in the Stack, using `Fly.providers()`. This first deployment creates the database and a handler that does not yet query application tables.
101
102## Apply the committed SQL
103
104```sh
105pnpm exec drizzle-kit migrate
106```
107
108Supply the cluster's `connectionUri` as the migration runner's secret `DATABASE_URL`, and run where the direct hostname resolves and is reachable on the Fly organization network, such as a correctly configured WireGuard peer or a Machine on 6PN. A pooled URI is not the migration endpoint.
109
110## Open the runtime client
111
112```diff lang="typescript"
113+import * as Drizzle from "alchemy/Drizzle/Postgres";
114
115 const conn = yield* Fly.ConnectPostgres(Db);
116+const db = yield* Drizzle.Postgres(conn.connectionString);
117```
118
119The pooled connection opens lazily when a request first queries the database.
120
121## Query from the Service
122
123```diff lang="typescript"
124+import { Users } from "./schema.ts";
125
126 return {
127- fetch: Effect.succeed(HttpServerResponse.text("Hello from Fly!")),
128+ fetch: Effect.gen(function* () {
129+ const users = yield* db.select().from(Users).pipe(Effect.orDie);
130+ return yield* HttpServerResponse.json({ users });
131+ }),
132 };
133```
134
135Queries run inside the request scope, rather than during infrastructure construction.
136
137## Deploy the application
138
139```sh
140pnpm alchemy deploy
141```
142
143For subsequent schema changes, generate, review, and commit first, then apply the committed migrations before deploying compatible application code. `alchemy deploy` does not replace `drizzle-kit migrate` in this guide.
144
145## Choose one migration owner
146
147`Fly.Postgres` also supports a `migrations` directory, but this guide deliberately leaves it unset and keeps Drizzle as the migration owner. Switching an existing database to Alchemy's migration history is a separate [migration adoption decision](/sql/drizzle/migrations#existing-migration-history), not a command substitution.
148
149## Continue
150
151See the [Managed Postgres guide](/fly/data/postgres), [portable Postgres queries](/sql/drizzle/postgres), and [migration workflow](/sql/drizzle/migrations). The [fly-postgres example](https://github.com/alchemy-run/alchemy/tree/main/examples/fly-postgres) demonstrates the Service binding, but its test-only HTTP migration endpoint is not used here.