Development databases
A development database can run inside a CoderFlow task container, alongside the application. It needs no sidecar container or Docker access from the task. Use it for disposable development and test data. Use an existing development database through a Database connection when you need shared data, an exact server version, or data that outlives a task container.
For repository setup options, see Generate from repository. Review the proposed database, version and seed steps before building the environment.
Choosing during generation
When Generate from repository finds a database, its preview has a Database row:
- A local database in each task container is offered, and selected by default, when the recipe below applies. The row shows the server, its version and the approximate idle memory.
- Connect to an existing database is always offered. When the recipe doesn't apply it is the only option, and the row gives the reason.
Your choice is recorded with the Draft. With a local database, the Draft gets the recipe's install in its pre-clone instructions (after the runtimes), a setup.sh holding the recipe's fragment between # BEGIN CoderFlow local database and # END CoderFlow local database, a Review the local development database step in Finish setup, and a Local database section in its AGENTS.md that names the server, 127.0.0.1 and its port, the connection variable names, and the status file the agent reads before using the database. With a connection, the Draft gets a Finish setup step that tests a connection named database, and nothing is installed.
The same choice is available outside the dialog: --database local or --database connection on coder env create and coder env generate, and the database parameter of the MCP generation tools. Asking for local when the recipe doesn't apply is refused; nothing is created.
A Draft that already has its own files keeps them. A Draft waiting for its configuration can be edited before it is generated. If it already has a setup.sh, pre-clone instructions or its own AGENTS.md, CoderFlow doesn't change them, so it can't add a complete local database. The Draft is then generated with Connect to an existing database, and the preview and the result say why; asking for local there is refused with a message that names those files. To use a local database there, add the recipe by hand, or remove those files before generating.
Several repositories. A local database is offered only when the engine and every seed step were found in one repository and every repository, that one included, was fully inspected. It is set up for that repository's folder. With one repository, a file CoderFlow couldn't inspect doesn't withhold the local database; the preview says it can't tell whether more seeding is needed.
Check repositories for changes never suggests adding a local database to an environment.
When a local recipe applies
The recipe requires one known PostgreSQL or MySQL-compatible engine family and either a detected Compose database service or detected migrations/dumps. A driver dependency or database variable name alone is not enough. A SQL file alone cannot identify its database dialect.
SQL Server and unknown engines use a connection instead. SQL Server for Linux needs about 2 GB of memory and has its own licence terms. MySQL driver evidence and a MariaDB Compose service count as one family; their sources are combined and the Compose image supplies the requested major. Different families, such as PostgreSQL and MySQL, still need explicit configuration. An unread or skipped file means CoderFlow cannot tell whether more seeding is needed; it does not mean there is nothing to seed. For example, a .NET solution may reference a project outside the checkout that CoderFlow could not inspect. Review those skipped projects for additional database requirements.
What gets installed
| Detected engine | Installed server | Version choice | Approximate idle memory |
|---|---|---|---|
| PostgreSQL | PostgreSQL | Numeric Compose major 14–18; default 15 | 50–100 MB |
| MySQL or MariaDB | MariaDB | Debian Bookworm's 10.11 series | 80–160 MB |
Memory figures are estimates, not measurements or hard limits. Allow additional memory for the app, migrations and concurrent queries. PostgreSQL uses 32 MB of shared buffers, 20 connections, 1 MB work memory and 16 MB maintenance work memory. MariaDB uses a 32 MB InnoDB buffer pool, a 4 MB log buffer, 8 MB key and Aria caches, 20 connections, and disables Performance Schema and the binary log. Durability is not disabled to achieve these estimates.
The official Node image manifest currently maps node:24-slim to Debian Bookworm. The recipe checks Bookworm at image build time and fails if the base changes, so it cannot silently install a different distribution's database series.
PostgreSQL 15 uses Debian packages. Other supported Compose majors use the PostgreSQL project's signed apt repository; a numeric tag such as postgres:16-alpine selects major 16, not the Alpine distribution. Unversioned or nonnumeric tags use 15. Unsupported numeric PostgreSQL majors require review or a connection. Apt supplies patch updates; this is not an exact patch pin.
MySQL requests, including mysql:8, use Debian's MariaDB 10.11 packages. The recipe reports the requested major separately from the installed version. This keeps the installation on Debian's package source instead of adding the MySQL Community repository. MariaDB is not an exact substitute for MySQL 8: SQL, collations, JSON and authentication behavior can differ. A different requested MariaDB series is also not preserved. Use a connection when those differences matter.
Packages are installed as root in the image's pre-clone instructions, without recommended packages. Package service startup is blocked during installation, PostgreSQL's default root-managed cluster is suppressed, the unused system MariaDB data is removed, and apt lists are removed afterward. Runtime database files are created later by coder.
Startup, data and access
setup.sh runs as coder, without sudo, after repository updates. Normal initialization runs it again on a container start; follow-up starts with SKIP_INIT=true deliberately skip it. The recipe:
- Initializes data once under
$HOME/.local/share/coderflow-db/postgres/dataor$HOME/.local/share/coderflow-db/mysql/data, outside repository checkouts. - Starts the server if needed, binding only to
127.0.0.1, on port 5432 or 3306. The private socket directory is the adjacentrun/folder. - Waits up to 30 seconds for readiness, in addition to bounded initialization/start commands.
- Creates database
appand userapponce, then executes known seed steps once after dependencies are available.
Restarting the same container retains its database. Recreating or deleting the container loses it. It is not backed up with the repository, carried into a new task, or saved into the environment image. A new container starts with fresh data.
No password is generated or stored. PostgreSQL trusts only the local coder socket administrator and the app role on its own database over the private socket or IPv4 loopback. MariaDB uses socket authentication for the local coder administrator and an empty-password app account restricted to localhost and database app. PostgreSQL's trust authentication guidance explains why this depends on trusting local processes. This is suitable for a single task's disposable data; every process in that container must be trusted. Do not publish these database ports, route them through a public proxy, or use this recipe for production data.
A failed local database does not stop the task. You can continue unrelated work or use Configure this environment to repair it. The setup message names the failed stage: initialization, startup, readiness, provisioning or a numbered seed step. Raw database and migration output is suppressed to keep query text and credentials out of task logs.
Check the latest database status inside the task container at $HOME/.local/share/coderflow-db/postgres/status for PostgreSQL or $HOME/.local/share/coderflow-db/mysql/status for MariaDB. $HOME is the coder user's home directory. The file contains one of these values:
| Value | Meaning |
|---|---|
ready | The server is ready and all known automatic seed steps succeeded. |
seed_review | The server is ready, but some seed steps need manual review. |
failed:initialize, failed:start, failed:readiness, failed:provision | The named stage failed. |
failed:seed-N | Seed step N failed and will be retried on the next setup run. |
The agent is instructed to check this file before work that needs the database. A missing or unreadable file means the database status is unknown, not ready. If the status file cannot be updated because of a storage or permissions problem, repair that problem before relying on an older status.
After repairing the reported stage, rerun setup or restart the same container normally. Completed steps are preserved, the failed stage is retried, and the status file is updated. Task access remains available while the database needs attention.
Connection variables
The non-secret default URLs are:
DATABASE_URL=postgresql://[email protected]:5432/app
DATABASE_URL=mysql://[email protected]:3306/app
Only the URL for the selected engine is used.
In the pre-clone instructions and in setup.sh, a URL names its user by reference to the engine's own user variable, which is set just before it:
ENV PGUSER="app"
ENV DATABASE_URL="postgresql://${PGUSER}@127.0.0.1:5432/app"
MariaDB uses MYSQL_USER the same way. Docker and the shell expand the reference, so the app receives exactly the URL above. Keep that form if you edit these lines or add the recipe by hand: Check repositories for changes and publishing setup changes treat any URL that has a user name written in it as a credential and refuse to show or publish it.
The recipe maps recognized detected names such as DB_HOST, DB_PORT, DB_USER, DB_NAME, PGHOST or MYSQL_DATABASE, and sets canonical engine defaults. DB_CONNECTION, when detected, becomes pgsql or mysql.
Password variables are left unset; their values are never copied from an example file. The local account needs no password. Remove any stale password override or configure the app to accept an unset/empty password. Settings with an ambiguous format, such as CONNECTION_STRING, and settings for another engine need manual review. A .NET connection string, for example, must be formatted for its actual provider. The recipe never guesses that every connection string is a URL.
The local connection defaults are included in the environment image and are available to tasks, the App Server and seed commands. Existing secrets, task overrides, app .env files or ~/.bash_env can override those defaults. Check for conflicting settings when switching to a local database.
Dependencies and seeding
Install application dependencies before running migrations or seed commands. Dependencies normally install during the environment image build; the database is initialized and seeded later in setup.sh, when the task starts. Do not seed during the image build, because that would include task data in the image.
For repositories that are not cloned into the image, or whose updated code changes dependencies, install dependencies when the task starts, before database setup. Run installation commands from the correct application folder. Migration tools such as dotnet-ef must also be installed and available on coder's PATH before seeding. Use the project's tool manifest or a version compatible with the project.
Seed order is:
- SQL schema dumps.
- Known migration commands, in the proposed order.
- SQL files named
seed.sql. - Prisma's explicit
db seedcommand.
SQL files use the selected engine's client. Recognized migration commands cover Prisma, Knex, Sequelize, Drizzle, EF Core, Django, Alembic and Laravel. Unrecognized tools or commands are left for manual review and are not run automatically. TypeORM, Flyway or Liquibase projects without a known migration command also need review; migrations that run when the app starts are not marked complete by database setup. Review mixtures of dumps and migrations: a dump may already include the schema a migration would create. SQL dumps needing superuser roles or another database are not imported with elevated rights.
Each successful automatic step gets a marker outside the repository. A repeated setup skips completed steps. A failed step gets no marker; the next setup retries it, which can require manual repair if it partly applied nontransactional SQL. Each automatic step has a five-minute timeout. Bounded commands receive a kill signal after a further one-second grace if they ignore termination. Before retrying initialization, the recipe removes only the never-promoted initializing staging directory, so partial initialization cannot block every later start. It never removes the promoted data directory. If that directory exists but its initialization marker is missing, setup reports failed:initialize for review rather than overwriting data. For a fresh disposable database, recreate the container.
Markers belong to the generated recipe and are not content hashes. Updating the repository does not automatically rerun migrations or seeds in an existing container. If you change the recipe or schema, apply a reviewed migration explicitly or recreate the container. Unknown/manual steps retain a review reminder even after all known steps succeed.
Switching to a connection
Remove the local recipe's database install and ENV instructions from pre-clone, remove its fragment from setup.sh (the lines from # BEGIN CoderFlow local database to # END CoderFlow local database), remove the Local database section from AGENTS.md, and remove conflicting local settings. Add and test a Database connection to an existing development database on the environment's Connections tab and supply the app's connection settings through the supported connection/secret configuration. Rebuild the image and start a new task container so stale image variables and the old local daemon cannot remain active.