# Schema migrations

> Connect a migration tool to one project, keep schema changes in your repository, and apply reviewed migrations from CI.

Source: https://docs.fortly.com/data/migrations

Migrations are versioned files that describe changes to your database, such as adding a table or column. Keeping them with your application code lets your team review and reproduce each schema change.

Open your project's **Database** and choose **Migrations** beside **Connect**. Choose **Prisma Migrate**, **Drizzle Kit**, or **SQL files**. The project in the console URL is the target, and its selected table stays in context when you close setup. **Connect → Migrations** opens the same setup.

## From local development to production

### Create and test locally
Use a local PostgreSQL database to develop your schema changes. Generate migration files with [Prisma Migrate](#prisma-migrate) or [Drizzle Kit](#drizzle-kit), or write SQL files yourself. Review the SQL and test it before applying it to a live project.

### Commit migration files with your code
Commit your schema and migration files to Git. Once a migration has been applied, create a new migration for the next change instead of editing the applied file. Use the same tool for the project so its migration history stays consistent.

### Deploy, and Fortly applies them
When a deployment builds your application, Fortly finds committed Prisma Migrate and Drizzle Kit migrations and applies the new ones to the project's database before the release goes live. See [Applied on every deploy](#applied-on-every-deploy). To run them from CI instead, copy the project's connection from **Database → Migrations**, save it as a CI secret, and run your tool's production migration command. Both record in the same table, so neither runs a migration twice.

## Applied on every deploy

Fortly reads your migrations from the source it builds, the way your tool would:

| Tool | Where Fortly looks | Recorded in |
| --- | --- | --- |
| Prisma Migrate | `prisma/migrations/<name>/migration.sql`, or the `migrations` folder beside the schema `package.json` names, when `migration_lock.toml` says `postgresql` | `_prisma_migrations`, as `prisma migrate deploy` records them |
| Drizzle Kit | The `out` folder of `drizzle.config` (default `drizzle/`), in the order of `meta/_journal.json`, when the dialect is `postgresql` | `drizzle.__drizzle_migrations`, as `drizzle-kit migrate` and Drizzle's `migrate()` record them |
| D1 (SQLite) | The binding's migrations folder | `d1_migrations`. See [D1 databases](https://docs.fortly.com/migrate/cloudflare#d1-databases) |

New migrations run after the build and before the release goes live, as the project's owner role, so the new code meets the schema it expects. The deployment panel shows the server-recorded **Database migrations** stage and the active migration tool. The deployment log lists each migration it applied. A build-output outage does not hide the deployment stage. If one fails, the deployment stops with the migration's name and PostgreSQL's error, and your live release keeps serving. A Prisma migration that failed is refused on later deploys until you resolve it with `prisma migrate resolve`, as Prisma itself refuses it.

Migrations are not applied for a service without access to the project's database, or for a site that builds no server. Prisma for SQLite or MySQL and Drizzle for other dialects are skipped with a note in the build output. A `drizzle.config` that names its own migrations table is also skipped; apply those from your release workflow.

An existing database needs a baseline before adopting a migration tool. Follow the tool's instructions to capture the current schema rather than rerunning statements that create tables you already have.

## Connect your migration tool

Owners and workspace admins can copy the project's connection string. Choose the tool's format so its TLS settings are correct. Copying or revealing credentials is explicit; opening the panel does not return a password.

The native endpoint is **db.fortly.com:5432**, through Fortly's session gateway. Your project has its own database and login. This owner connection can change your project's schema and data; keep it in server-side or CI secrets, not frontend code. It does not authorize Fortly account-management APIs.

Use the public connection from your computer or CI. Inside Fortly, keep using the private `DATABASE_URL` the runtime receives automatically. The HTTP Data API is a separate interface and cannot replace a PostgreSQL connection for migration CLIs.

## Prisma Migrate

Develop against local PostgreSQL and commit your schema and generated migrations:

```bash
# DATABASE_URL points to your local development database
npx prisma migrate dev --name add_projects
```

`migrate dev` needs a separate shadow database. Your Fortly role cannot create databases, so keep development and shadow databases local.

For Prisma 7, configure the CLI to read the connection from its environment:

```ts title="prisma.config.ts"
import { defineConfig, env } from "prisma/config";

export default defineConfig({
  schema: "prisma/schema.prisma",
  migrations: { path: "prisma/migrations" },
  datasource: { url: env("DATABASE_URL") },
});
```

Set **DATABASE_URL** in CI secrets to the **Prisma** connection copied from this project's Connect panel. Then apply committed migrations:

```bash
npx prisma migrate deploy
```

`migrate deploy` does not need a shadow database. The public gateway uses session pooling, preserving the session features migration tools need. See [Prisma's development and production workflow](https://www.prisma.io/docs/orm/v7/prisma-migrate/workflows/development-and-production).

If the database already has tables, Prisma may return `P3005`. Follow [Prisma’s baseline workflow](https://www.prisma.io/docs/orm/v7/prisma-migrate/workflows/baselining) to adopt the existing schema. Mark a baseline as applied only after checking it matches the actual database.

## Drizzle Kit

Generate and review migrations locally:

```bash
npx drizzle-kit generate
```

Use the **Drizzle / node-postgres** connection format as a CI secret:

```ts title="drizzle.config.ts"
import { defineConfig } from "drizzle-kit";

export default defineConfig({
  dialect: "postgresql",
  schema: "./src/schema.ts",
  out: "./drizzle",
  dbCredentials: { url: process.env.DATABASE_URL! },
});
```

Apply pending migrations and record their history:

```bash
npx drizzle-kit migrate
```

See [Drizzle's migration workflows](https://orm.drizzle.team/docs/migrations).

## Apply a SQL file in the console

Choose **SQL files** in migration setup. **Open SQL Editor** opens this project's editor; **Import SQL file** opens its import flow. Import runs a script in one transaction and rolls it back if a statement fails. See [Importing a database](https://docs.fortly.com/data/import).

These controls do not maintain an ORM migration ledger or detect whether a file already ran. Use one migration workflow consistently. Console changes can create drift from your repository's schema.

## Connection limits and schema refresh

The public session gateway allows two active connections per project and up to eight connected or waiting clients, within the existing private pool allocation. Database roles default to a thirty-second statement timeout; a reviewed migration can adjust its own session timeout. The public gateway still enforces a five-minute statement timeout and a sixty-second idle-transaction timeout. Source-IP connection and rate limits also apply; see [external connections](https://docs.fortly.com/data#external-postgresql).

After a native schema change, refresh Fortly's schema through SQL Editor or a console schema operation, or let the project sleep and wake. Native clients see their changes immediately; Fortly's Data API schema cache is separate. Review the affected table's **API** tab: creating a table does not open public access. Rolling back an application release does not reverse database migrations.
