Drizzle Postgres on Railway
Run Drizzle in a Railway container beside Railway Postgres. Start with Railway setup, the Service tutorial, and a src/schema.ts exporting Users from the Postgres client guide.
Install the database dependencies
Section titled “Install the database dependencies”pnpm add drizzle-orm@1.0.0-rc.5-ab785fc @effect/sql-pg pgpnpm add -D drizzle-kit@1.0.0-rc.5-ab785fcThe Drizzle versions match the repository’s Effect integration.
Configure migrations
Section titled “Configure migrations”import { defineConfig } from "drizzle-kit";
export default defineConfig({ dialect: "postgresql", schema: "./src/schema.ts", out: "./drizzle", dbCredentials: { url: process.env.DATABASE_URL! },});DATABASE_URL here belongs to your migration runner; the deployed Service receives its own private connection through a binding.
Generate after changing the schema
Section titled “Generate after changing the schema”pnpm exec drizzle-kit generateReview the generated SQL and snapshots, including any rename decisions, before applying them.
Commit the migration
Section titled “Commit the migration”git add src/schema.ts drizzle.config.ts drizzlegit diff --cachedgit commit -m "Add users migration"Commit the schema, SQL, and snapshots together rather than generating migrations during deployment.
Declare Postgres
Section titled “Declare Postgres”import * as Railway from "alchemy/Railway";import { Site } from "./project.ts";
export const Db = Railway.Postgres("Db", { project: Site });Use the same Project and environment as the Service; omitting environment on both selects the Project’s primary environment.
Install the driver in the Service image
Section titled “Install the driver in the Service image”{ project: Site, main: import.meta.url, build: { install: ["pg"] },}Use Railway.Service, whose image can install pg, rather than assuming the single-file Canvas Railway.Function has the same packaging support.
Bind Postgres
Section titled “Bind Postgres”import { Db } from "./db.ts";
Effect.gen(function* () { const conn = yield* Railway.ConnectPostgres(Db); return { fetch: Effect.succeed(HttpServerResponse.text("Hello from Railway!")), };}),}).pipe(Effect.provide(Railway.ConnectPostgresHttp)),The binding injects the private URI into the Service environment; conn.connectionString is an Effect yielding a redacted URL, not a query client. These excerpts use the tutorial’s minimal text handler until the database is migrated.
Provision before the first migration
Section titled “Provision before the first migration”pnpm alchemy deployKeep yield* Api in the tutorial’s Stack with Railway.providers(). This initial deployment provisions Postgres while the handler still avoids application tables.
Apply the committed SQL
Section titled “Apply the committed SQL”pnpm exec drizzle-kit migrateFor an external CI runner, supply the database’s publicConnectionUri as secret DATABASE_URL; the default public: true creates that TCP proxy. With public: false, run migrations inside the same Railway environment using connectionUri instead.
Review public TLS
Section titled “Review public TLS”The generated Railway Postgres URLs currently include sslmode=no-verify; a public TCP proxy does not provide certificate verification. For production, configure a verifiable server certificate and migration-client trust, or run migrations within the private environment.
Open the runtime client
Section titled “Open the runtime client”import * as Drizzle from "alchemy/Drizzle/Postgres";
const conn = yield* Railway.ConnectPostgres(Db);const db = yield* Drizzle.Postgres(conn.connectionString);Runtime traffic stays on Railway’s private network, regardless of whether a public migration endpoint exists.
Query from the Service
Section titled “Query from the Service”import { Users } from "./schema.ts";
return { fetch: Effect.succeed(HttpServerResponse.text("Hello from Railway!")), fetch: Effect.gen(function* () { const users = yield* db.select().from(Users).pipe(Effect.orDie); return yield* HttpServerResponse.json({ users }); }),};The request opens the pool lazily and closes it when its scope ends.
Deploy the application
Section titled “Deploy the application”pnpm alchemy deployFor later changes, generate, review, and commit the schema, SQL, and snapshots before running migrations and deploying compatible code. Railway.Postgres has no migrations prop, so deployment does not replace drizzle-kit migrate.
Continue
Section titled “Continue”See the SQL database comparison, Postgres client guide, and migration workflow. The railway-service example contains the Service, pg installation, and Postgres binding used here.