1---
2title: Drizzle MySQL on Railway
3description: Connect an Effect-native Railway Service to MySQL and deploy committed Drizzle migrations.
4---
5
6Run Drizzle in a Railway container beside [Railway MySQL](/railway/data/mysql). Start with [Railway setup](/railway/setup), the [Service tutorial](/railway/tutorial/part-2), and a `src/schema.ts` exporting a MySQL `Users` table from the [MySQL client guide](/sql/drizzle/mysql).
7
8## Install the database dependencies
9
10```sh
11pnpm add drizzle-orm@1.0.0-rc.5-ab785fc @effect/sql-mysql2 mysql2
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 migrations
18
19```typescript
20// drizzle.config.ts
21import { defineConfig } from "drizzle-kit";
22
23export default defineConfig({
24 dialect: "mysql",
25 schema: "./src/schema.ts",
26 out: "./drizzle",
27 dbCredentials: { url: process.env.DATABASE_URL! },
28});
29```
30
31`DATABASE_URL` here is a MySQL URL for the migration runner, separate from the Service's bound `MYSQL_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; deployment must consume the reviewed history.
53
54## Declare MySQL
55
56```typescript
57// src/db.ts
58import * as Railway from "alchemy/Railway";
59import { Site } from "./project.ts";
60
61export const Db = Railway.MySQL("Db", { project: Site });
62```
63
64This creates the MySQL service, persistent volume, and a public TCP proxy by default; keep the database and application in the same Railway environment.
65
66## Install the driver in the Service image
67
68```diff lang="typescript"
69 // src/api.ts
70 {
71 project: Site,
72 main: import.meta.url,
73+ build: { install: ["mysql2"] },
74 }
75```
76
77Use the container-backed `Railway.Service` packaging path, not the single-file Canvas `Railway.Function` path.
78
79## Bind MySQL
80
81```diff lang="typescript"
82+import { Db } from "./db.ts";
83
84 Effect.gen(function* () {
85+ const conn = yield* Railway.ConnectMySQL(Db);
86 return {
87 fetch: Effect.succeed(HttpServerResponse.text("Hello from Railway!")),
88 };
89-}),
90+}).pipe(Effect.provide(Railway.ConnectMySQLHttp)),
91```
92
93The binding supplies an Effect-valued, redacted private connection string rather than a SQL query method. These excerpts use the tutorial's minimal text handler until the database is migrated.
94
95## Provision before the first migration
96
97```sh
98pnpm alchemy deploy
99```
100
101Keep `yield* Api` in the tutorial's Stack with `Railway.providers()`. The initial handler does not query tables, so the database can be provisioned before its first migration.
102
103## Apply the committed SQL
104
105```sh
106pnpm exec drizzle-kit migrate
107```
108
109Supply `publicConnectionUri` as secret `DATABASE_URL` for an external runner, or run inside the same Railway environment with `connectionUri` when `public: false`. Configure and verify TLS for any public database traffic; do not disable certificate verification to make a migration connect.
110
111## Open the runtime client
112
113```diff lang="typescript"
114+import * as Drizzle from "alchemy/Drizzle/MySQL";
115
116 const conn = yield* Railway.ConnectMySQL(Db);
117+const db = yield* Drizzle.MySQL(conn.connectionString);
118```
119
120The application uses Railway's private MySQL endpoint, not the public TCP proxy.
121
122## Query from the Service
123
124```diff lang="typescript"
125+import { Users } from "./schema.ts";
126
127 return {
128- fetch: Effect.succeed(HttpServerResponse.text("Hello from Railway!")),
129+ fetch: Effect.gen(function* () {
130+ const users = yield* db.select().from(Users).pipe(Effect.orDie);
131+ return yield* HttpServerResponse.json({ users });
132+ }),
133 };
134```
135
136Use MySQL query semantics; PostgreSQL's `.returning()` is not part of this example.
137
138## Deploy the application
139
140```sh
141pnpm alchemy deploy
142```
143
144For later changes, generate, review, and commit the schema, SQL, and snapshots before applying migrations and deploying compatible code. `Railway.MySQL` has no `migrations` prop, so `alchemy deploy` does not replace `drizzle-kit migrate`.
145
146## Continue
147
148See the [SQL database comparison](/sql/databases), [portable MySQL queries](/sql/drizzle/mysql), and [migration workflow](/sql/drizzle/migrations). The [MySQL database guide](/railway/data/mysql) covers the resource's image, volume, private hostname, and public proxy.