Oct 23, 202610 min read

Best Database Setup for AI Coding Agents

Migrations in code, a local Postgres per developer, a fresh database per test and a branch per preview environment, compared for teams where agents write a large share of the schema changes.

PostgreSQL is the database most respondents to the 2025 Stack Overflow Developer Survey worked with, at 55.6 percent, so it is usually the database an agent is changing. A coding agent changes a database the same way it changes code: it writes a migration, runs the tests, reads the errors and tries again, often several times in one session.

That loop only works when the database is something the agent can create, migrate, break and recreate without asking anyone, and it breaks down when the only Postgres around is a shared staging instance, because a half-applied migration from one session becomes the starting state of the next one, and a test that passed against yesterday's rows fails against today's.

Claude Code's documentation recommends that you give the agent a check it can run, such as tests or a build, and for anything touching a database that check needs a database behind it, and the setup has to make it cheap enough that the agent runs it every time.

What to weigh when choosing:

  • Whether the schema lives in migration files the agent can read and edit, or in a console and someone's memory.
  • Whether every developer and every agent session has its own local Postgres, or shares one.
  • Whether tests get an isolated, migrated database, or reuse whatever state the last run left.
  • Whether each pull request gets its own database, so a migration is tried before it reaches staging.
  • Whether production credentials are reachable from the agent's environment at all.

Database setups for AI coding agents at a glance

SetupHow it worksBest fit
EncoreDatabase declared in code with migrations, started locally by encore run, isolated per test, provisioned in your AWS or GCP accountTeams that want one declaration to cover local, test, preview and production
Local Postgres with a migration toolPostgres installed on the machine or run by Compose, with Alembic, Django migrations, Prisma Migrate or sqlxSingle services where the team already manages local tooling
Testcontainers per test suiteA throwaway Postgres container started from the test codeIntegration tests in CI without a shared database
Neon branching per pull requestA copy-on-write branch of a Postgres database for each preview environmentPreview environments that need realistic data
Shared staging databaseEvery developer and agent points at one remote databaseRead-only exploration, not agent-written migrations
Compare features across the setups
CapabilityEncoreLocal Postgres + migration toolTestcontainersNeon branchingShared staging
Schema in the repositoryMigration files next to the database declarationYeswith the tool you chooseUses your migration toolUses your migration toolOften drifts from the repository
Local database per developerencore run starts Postgres locallyYesset up by each developerOnly during testsRemote branch per developer if you create oneNo
Isolated test databaseSeparate test databases, cloned from a migrated templateManual, through scripts or fixturesOne container per suiteOne branch per run if you script itNo
Per-PR databasePreview environments, with Neon branching as an optionNot includedNot includedYesNo
Production provisioningProvisioned in your AWS or GCP accountSeparate toolingNot applicableNeon projectAlready exists
Agent can see the schema and query itLocal MCP server exposes schemas and queriesThrough psql or a separate MCP serverDuring tests onlyThrough psql or a separate MCP serverRisky, since the data is shared

Encore: one database declaration for every environment

Encore

Encore combines the database declaration, its migrations and the code that queries it in one place, as part of an open source SDK where Postgres databases, Pub/Sub topics, buckets, cron jobs and secrets are declared in TypeScript, Go, Python or Rust, and Encore provisions the same declarations in each environment. encore run starts Postgres locally and applies the migrations before the app starts, encore test sets up separate test databases, every pull request can get its own preview environment, and a deploy creates RDS on AWS or Cloud SQL on GCP in your own cloud account from the same line of code.

Application code passes through Encore and infrastructure guardrails to resources provisioned in your own AWS or GCP account.
Encore provisions the infrastructure your application needs in your own AWS or GCP account.

In TypeScript, the database is a top-level declaration that points at a directory of SQL migrations, which is also where an agent adds a new numbered file when it changes the schema:

import { SQLDatabase } from "encore.dev/storage/sqldb"; // Encore applies ./migrations on encore run, in tests and on deploy. export const db = new SQLDatabase("orders", { migrations: "./migrations", }); export async function countOrders(): Promise<number> { const row = await db.queryRow<{ n: number }>` SELECT count(*)::int AS n FROM orders `; return row?.n ?? 0; }

Tests share one migrated database by default, which keeps them fast, and when a test needs a clean slate, Go has et.NewTestDatabase, which clones a fully migrated database from a template and drops it when the test ends:

package orders import ( "context" "testing" "encore.dev/et" ) func TestCreateOrder(t *testing.T) { ctx := context.Background() // A fresh, fully migrated database, isolated to this test. testDB, err := et.NewTestDatabase(ctx, "orders") if err != nil { t.Fatal(err) } _ = testDB // run the test against testDB }

The Rust SDK goes further and gives every test its own database, bucket and topics under encore test, and the Python SDK runs pytest against an isolated database, so a Django test suite can run migrate against a database no other test sees. Test databases skip fsync and use an in-memory filesystem, since durability does not matter for a throwaway database.

The agent also gets a view into the running database. The local MCP server runs next to encore run and exposes tools such as get_databases, which returns the schema and tables, and query_database, which runs SQL against the local database, so an agent can check that its migration produced the columns it expected instead of guessing from the SQL file.

Key features

  • Databases are declared in code, with SQL migrations applied locally, in tests and on deploy.
  • encore run starts Postgres locally, and encore db reset recreates a database from its migrations.
  • Test databases are cloned from a migrated template, with per-test isolation in Go and Rust.
  • Preview environments per pull request, optionally on Neon branches seeded from staging.
  • Production databases are provisioned as RDS or Cloud SQL in your own cloud account.

Good to know

Encore uses plain SQL migration files for the databases it manages, and ORMs such as Prisma, Drizzle or the Django ORM can still sit on top through the connection string. Existing RDS or Cloud SQL databases can be imported rather than recreated. Agents declare infrastructure in code, and cloud access sits with Encore's IAM role, not the agent.

Try Encore →

Local Postgres with a migration tool

PostgreSQL

The most common setup is a PostgreSQL server on each laptop, either installed natively or started by Docker Compose, with a migration tool that matches the stack: Alembic for SQLAlchemy, Django migrations, Prisma Migrate for TypeScript or sqlx for Rust. The migration files live in the repository, so an agent can read the history and add the next step.

# compose.yaml services: db: image: postgres:17 environment: POSTGRES_PASSWORD: postgres POSTGRES_DB: app ports: - "127.0.0.1:5432:5432"

Key features

  • Works with any language and any migration tool.
  • The agent can run migrations and psql directly in the terminal.
  • No dependency on a hosted service for day-to-day work.

Good to know

The connection string, the migration command and the order of steps usually live in a README or a Makefile, so it helps to write them into AGENTS.md or CLAUDE.md, the files Codex and Claude Code read at the start of a session. Production is a separate system, where the database still needs its own provisioning and its own way of running the same migrations.

Try PostgreSQL →

Testcontainers: a throwaway database per test suite

Testcontainers

Testcontainers starts a real Postgres container from the test code and removes it afterwards, which gives an agent's test run a database nobody else is using. The Python version wraps it in a context manager:

from sqlalchemy import create_engine, text from testcontainers.postgres import PostgresContainer def test_orders_table(): with PostgresContainer("postgres:17") as pg: engine = create_engine(pg.get_connection_url()) with engine.begin() as conn: conn.execute(text("CREATE TABLE orders (id serial PRIMARY KEY)")) count = conn.execute(text("SELECT count(*) FROM orders")).scalar() assert count == 0

Key features

  • Real Postgres in tests, in CI and on laptops.
  • Libraries for Python, Java, Go, Node.js, Rust and other languages.
  • The database disappears when the suite ends.

Good to know

Each container adds startup time, so most suites start one per module or session and clean tables between tests, which brings back some of the shared-state problems the container was meant to remove. The migrations still have to be applied by the test setup, and the agent's sandbox must be allowed to reach a container runtime.

Try Testcontainers →

Neon branching: a database per pull request

Neon

Neon branching creates a copy-on-write branch of a Postgres database, including its data, so a preview environment can start from a copy of staging without a dump and restore. A CI job creates the branch for the pull request and passes its connection string to the app:

~/app
$ neon branches create --name pr-142 --parent staging$ neon connection-string pr-142
A branch for one pull request, created from the staging branch.

Key features

  • Branches include the parent's data without a dump and restore.
  • Each branch has its own connection string and can be reset to its parent.
  • Works well for agent-written migrations that need to run against realistic data.

Good to know

Branching solves the preview and review stage rather than the local loop, so an agent working on a laptop still needs a local database or its own branch, and someone has to script branch creation and cleanup in CI. Encore supports Neon as a database provider, and its preview environments can start from a Neon branch of another environment's database.

Try Neon →

Shared staging database: the setup to move away from

A single remote database that every developer and agent points at is the easiest setup to start with and the hardest to trust once agents are writing migrations. One session's half-finished change becomes everyone's problem, test data drifts, and the credentials in .env often reach further than staging. In July 2025 a Replit agent deleted a live company database during a code freeze, which is the outcome the other setups in this article are designed to rule out. A shared database still has a place for read-only exploration, but agent-written schema changes belong in a database the agent can throw away.

How to choose

  • Encore when you want the database declared once and the same migrations applied locally, in isolated test databases, in preview environments and in production in your own AWS or GCP account.
  • Local Postgres with a migration tool for a single service where the team already maintains local tooling and production provisioning elsewhere.
  • Testcontainers for integration tests in CI that need a real database without a shared server.
  • Neon branching for preview environments that need a copy of realistic data for each pull request.
  • A shared staging database only for read-only work, never as the place an agent applies migrations.

For more on giving agents a working environment, see how to give AI coding agents real infrastructure to test against, and for provisioning without Terraform see deploying a database to AWS without Terraform.

Frequently asked questions

What is the best database setup for AI coding agents?

Give the agent a real Postgres database it can create, migrate and throw away without touching anything shared. In practice that means schema changes as migration files in the repository, a local Postgres per developer, a fresh migrated database for each test or test run, and an isolated database per pull request, for example through Neon branching. Encore covers the local and test parts from a database declaration in code, applies the migrations on encore run and in every environment, and provisions the production database in your own AWS or GCP account.

Should an AI coding agent have access to the production database?

No. Keep production credentials out of the agent's environment and let it work against a local or branched database instead. In July 2025 a Replit agent deleted a live company database during a code freeze, which is the failure this setup avoids.

How do I give each test its own database when an agent runs the tests?

Clone each test database from a migrated template so it starts empty and fully migrated. With Encore, encore test sets up the test databases itself, in Go et.NewTestDatabase(ctx, "orders") gives one test its own database, and the Rust SDK isolates the database for every test automatically. Without Encore, Testcontainers can start a throwaway Postgres container per test suite.

Deploy to your cloud on AWS and Google Cloud
without managing infrastructure

Encore lets developers and agents define application infrastructure directly in code, then automatically provisions it from local development to production in your AWS or GCP account.

~/orders — claude
Claude Code
Claude Code v2.1.180Opus 5.5~/orders