DocsGuidesDatabasesFor AI agents
Guides

Databases

Postgres is the platform database. Here is what happens when your AI wrote something else.

4 minutes to read

Every app that needs a database gets its own Postgres database, reachable only from the app, with a copy taken before each deploy that changes its shape and, on the paid plans, every day.

If your AI used Postgres

Nothing to do. Drizzle, Prisma, Kysely, TypeORM, Sequelize, Knex or the plain pg driver all work: the app receives DATABASE_URL. Its database changes run as part of each deploy, before the new version goes live, with the same settings the app runs with. Prisma, Drizzle, Knex (with a knexfile), Sequelize (with sequelize-cli), node-pg-migrate, db-migrate, kysely-ctl, Alembic, Rails, Laravel (php artisan migrate) and Django (manage.py migrate, for an app whose settings name a Postgres database) are found by themselves. For anything else, including TypeORM and Kysely's own migrator, put the command in package.json's scripts as migrate (or db:migrate) and it runs instead.

A Rust app with a Postgres driver (sqlx, Diesel or SeaORM with their Postgres feature, tokio-postgres, deadpool-postgres) gets its database the same way. The copy of the app that runs has no Cargo, so its migrations run from its own program: give it a migrate mode that runs them (sqlx's migrate!(), Diesel's embed_migrations!, refinery or SeaORM's Migrator) and exits, and name it in deploy.json as "database": { "migrate": "./bin/<name> migrate" }. The platform runs it once before each version goes live, after copying the database, as it does for every other stack. Turn the migrator's own lock off (sqlx: set_locking(false)): one deploy migrates at a time, and through the connection pool described below a lock can be left held. The platform does this for Prisma by itself.

A Laravel app is given DB_CONNECTION and DB_URL for its database as well, because Laravel reads those rather than DATABASE_URL. A Java app is given SPRING_DATASOURCE_URL, SPRING_DATASOURCE_USERNAME and SPRING_DATASOURCE_PASSWORD, which Spring Boot reads by itself, so it needs no database settings at all, and JDBC_DATABASE_URL, JDBC_DATABASE_USERNAME and JDBC_DATABASE_PASSWORD, the names Heroku uses.

DATABASE_URL starts with postgresql://, the form every driver reads, SQLAlchemy included (with psycopg 3, write postgresql+psycopg://). Drivers that prepare statements, such as Java's, psycopg 3, asyncpg, postgres.js and Prisma, need no special setting. A setting sent while connecting, such as node-postgres's statement_timeout, is refused by the connection pool in front of the database: set it inside a transaction with SET LOCAL instead. The pool works in transaction mode: between two transactions, or two statements outside one, the app may be on a different connection, so nothing a connection remembers lasts beyond a transaction — a SET, a lock taken with pg_advisory_lock, LISTEN, a temporary table. Take locks with pg_advisory_xact_lock inside a transaction, and run migrations the platform's way (once per deploy, above) rather than at every start, where several copies starting together each take a migration tool's lock.

If your AI used MySQL or MongoDB

The deploy stops with ENGINE_CONVERSION_REQUIRED and a fix written for the ORM in use, for example:

In prisma/schema.prisma change the datasource provider to "postgresql", replace MongoDB-specific attributes with relational equivalents, run prisma migrate dev --name init, then re-run deploy.

Your AI does this in a minute or two. It is a mechanical change and the kind of thing it does all day. You will not notice.

Why not just host MySQL and MongoDB? Because doing it well for one app costs more than the whole Personal plan, and the price you pay is only possible because every app speaks the same database.

If your AI used SQLite

Switch to Postgres before you deploy. Ask your AI: "move this app from SQLite to Postgres".

SQLite keeps everything in a file next to the app. Apps here do not have a disk that survives — the machine running your app is created when someone visits and thrown away afterwards, and there can be several at once. A SQLite file will look like it works while you are testing, then lose what people wrote.

The platform gives every app a Postgres database, included, with backups. It is the same kind of change as the MySQL one above and your AI does it in a minute or two.

Copies of your data

A copy of the database is taken before every migration and, on the paid plans, once a day. Each copy is kept for 30 days. Getting one back yourself is not available yet; going back to a version puts back the code and keeps today's data.

This page as Markdown: /docs/databases.mdllms.txt
↑ ↓ to moveEnter to open