1---
2title: Migrations
3description: Apply SQL migrations and seed data to Neon projects and branches as part of every deploy — ordered, hashed, and tracked in Alchemy's __alchemy_migrations table.
4---
5
6Both `Neon.Project` and `Neon.Branch` accept a `migrations` prop — a
7folder of `.sql` files (or a [`Drizzle.Schema`](/sql/drizzle/migrations)
8resource) applied in order as part of every deploy. On a project,
9migrations run against the default branch's primary database; on a
10branch, they run against the branch itself:
11
12```typescript
13const project = yield* Neon.Project("my-project", {
14 migrations: "./migrations",
15});
16```
17
18Because branches are copy-on-write forks, a branch created from a
19migrated parent already contains the parent's schema — and its
20tracking table. Point the branch at the same `migrations` directory and only
21migrations added after the fork are applied:
22
23```typescript
24const featureBranch = yield* Neon.Branch("feature", {
25 project,
26 migrations: "./migrations",
27});
28```
29
30## Ordering and hashing
31
32Files are discovered recursively under the migrations directory and sorted by
33their numeric prefix (`0001_init.sql`, `0002_users.sql`, …), falling
34back to name order for files without one. Each file's contents are
35SHA-256 hashed and the hashes are persisted in the resource's state
36(`migrationsHashes`), so adding a migration file — or editing an
37existing one — is what marks the resource for an update on the next
38deploy. When nothing changed, the migration step is skipped
39entirely.
40
41## The tracking table
42
43Applied migrations are recorded in Alchemy's `__alchemy_migrations`
44table (created automatically), one row per file:
45
46```sql
47CREATE TABLE IF NOT EXISTS "__alchemy_migrations" (
48 id SERIAL PRIMARY KEY,
49 hash text NOT NULL,
50 created_at bigint,
51 name text,
52 applied_at timestamp with time zone DEFAULT now()
53);
54```
55
56A database previously migrated with drizzle-kit or Prisma is
57[adopted automatically](/sql/effect-sql/migrations#adopting-an-existing-database):
58its old tracking table's history is copied in once and the old table
59is frozen. Projects deployed by older Alchemy versions (which used a
60`neon_migrations` table) are upgraded in place.
61
62On each deploy, files whose names already appear in the table are
63skipped. Use `migrations: { dir, table }` to put the bookkeeping in
64a differently-named table:
65
66```typescript
67const project = yield* Neon.Project("my-project", {
68 migrations: { dir: "./migrations", table: "my_migrations" },
69});
70```
71
72## Transactional apply
73
74Each pending migration runs inside a transaction together with its
75bookkeeping `INSERT` into the tracking table, so partial application
76is impossible: a failing statement rolls the whole file back and
77fails the deploy, while migrations that already committed stay
78applied. Fix the file and re-deploy — the run resumes from the
79failed migration.
80
81## Seed data with importFiles
82
83`importFiles` lists additional `.sql` files to apply after
84migrations. Paths are resolved relative to the working directory:
85
86```typescript
87const project = yield* Neon.Project("my-project", {
88 migrations: "./migrations",
89 importFiles: ["./seed/users.sql"],
90});
91```
92
93Unlike migrations, import files are not recorded in the tracking
94table. Each file is content-hashed and re-applied whenever its
95contents change, so write them to be safe to run more than once
96(`INSERT ... ON CONFLICT DO NOTHING`, `CREATE OR REPLACE`, and the
97like). Files whose hashes match the previous deploy are skipped.
98
99## Where next
100
101Guides:
102
103- [Add Drizzle ORM](/cloudflare/data/drizzle) — typed schemas and
104 generated migrations.
105
106Related:
107
108- [Neon overview](/neon) — projects, branches, and composing with
109 your cloud.
110- [Setup](/neon/setup) — credentials and provider registration.
111
112Reference:
113
114- [Project API reference](/providers/neon/project)
115- [Branch API reference](/providers/neon/branch)