# Database The database is a core part of the Elements story. A web application is fundamentally a system for moving data between users and a database, and Elements treats the database accordingly. Migrations, real-time channels, cron, and background jobs are built into Elements directly on top of the database, not added as separate services. That deep integration is what makes building a web app quick. The database is Postgres. Postgres, formally PostgreSQL, is an open-source relational database. Data is organized into tables with typed columns, and queries are written in SQL. Standardizing on a single database lets Elements deliver a consistent set of examples and packages that work for every project. Postgres is great for getting started and will scale with your application indefinitely. ## Postgres Is Bundled You do not install Postgres. Elements bundles Postgres 18.4 and manages it for you. A per-machine background daemon (`elements-machine`) runs a single Postgres cluster shared by every Elements project on the machine. The cluster is created automatically on first use, kept healthy (self-healing), and configured for loopback-only trust authentication. It listens on port **5433** (not the usual 5432), so it never collides with a Postgres you may already run. In development its data directory is `~/elements/db`. If something else on the machine already uses 5433, the install stops and says so. Choose another port by setting `ELEMENTS_POSTGRES_PORT` for the install. Take your install command from https://elements.dev/install and put the variable in front of `sh`: ```bash Terminal curl -fsSL '' | ELEMENTS_POSTGRES_PORT=5434 sh ``` The cluster keeps the port in its own `postgresql.conf`, so later commands use it without the variable, and `elements create` writes it into the new app's `config/env/development.env` as `DB_PORT`. An app created before the change keeps the port it was created with; edit `DB_PORT` there to move it. On the first build, your project connects to the daemon, the cluster is ensured ready, and two databases are created for the project automatically: the app database and a test database. Their names derive from the project directory name, normalized (for example `MyApp` becomes `my_app` and `my_app_test`), or from `database.name` in `config.jsoc`. There is nothing to start, nothing to install, and no connection string to paste. A freshly created app builds and runs with a working database out of the box. ## Configure The `database` block in `config.jsoc` supplies connection values. Every field is optional; empty or omitted fields fall back to the bundled cluster defaults (host `127.0.0.1`, port `5433`, user `postgres`, no password). ```jsoc // config.jsoc { database: { name: "my_app", host: env("DB_HOST", "127.0.0.1"), port: env("DB_PORT", 5433), user: env("DB_USER", "postgres"), password: env("DB_PASSWORD", ""), default: env("DB_DEFAULT", ""), ssl: env("DB_SSL", ""), sslRootCert: env("DB_SSL_ROOT_CERT", ""), }, } ``` ``` # config/env/development.env DB_HOST=localhost ``` A wrong-typed value fails the build before the project server tries to connect. `default` is the database Elements connects to in order to create or drop the app database. Neither statement can run over a connection to the database it names, so an existing one is needed first. Blank means auto-detect: `postgres`, then `defaultdb`. Set `default`, or the `DB_DEFAULT` variable the scaffolded config reads, when your provider's bootstrap database has another name. See [Remote and Hosted Postgres](cli). `ssl` and `sslRootCert` set TLS for every connection to the database: the build's migrations, `elements db`, the job server, and the app. The TLS section below covers both. To use a database other than the bundled cluster, a remote or hosted Postgres, set `DB_HOST` (and the other `DB_*` values) to that server. When `host` points somewhere other than the local cluster, Elements connects there and leaves the bundled cluster out of the picture entirely. Several app servers share one cluster this way, and every feature works across all of them. See [Remote and Hosted Postgres](cli). ## TLS Every connection to the database uses the same TLS settings, so the build, `elements db`, and the app connect the same way. With nothing set, Elements picks by host: no TLS for the bundled cluster on loopback (`127.0.0.1`, `localhost`, `::1`), and an encrypted connection for every other host. A hosted Postgres is encrypted with nothing configured. To verify the server as well, give Elements your provider's CA certificate. Save the file in the project, for example `config/certs/db-ca.pem`, and point `DB_SSL_ROOT_CERT` at it in the environment's env file: ```text config/env/production.env DB_HOST=mydb.abc123.us-east-1.rds.amazonaws.com DB_SSL_ROOT_CERT=config/certs/db-ca.pem ``` The variable reaches the config through `sslRootCert: env("DB_SSL_ROOT_CERT", "")` in the `database` block shown above. With a CA file set, every connection is `verify-full`: the server's certificate must chain to that CA and name the host in `DB_HOST`. This is libpq's `sslmode=verify-full`, the setting HIPAA and similar compliance reviews ask for. A relative path is relative to the project root, so the certificate deploys with the project. Providers publish the file: AWS RDS has its regional `rds-ca` bundles, DigitalOcean's control panel has a CA certificate download, and Google Cloud SQL and Supabase offer the server CA in their dashboards. `ssl` (the `DB_SSL` variable in the scaffolded config) picks a mode by its libpq name: | Mode | Encrypted | Certificate checked | | ------------- | --------- | --------------------------------------------------------- | | `disable` | no | no | | `require` | yes | no, or the chain when a CA file is set (as in libpq) | | `verify-ca` | yes | the chain, against the CA file this mode requires | | `verify-full` | yes | the chain and the host name | `verify-full` with no CA file checks against the trusted public CAs, which is the right setting for a provider whose certificates are publicly trusted, such as Neon. `true` and `false` also work, as `require` and `disable`. A certificate that does not verify stops the build at its database step, with the host, the reason, and the setting to change, before any release goes live. ## App and Test Databases Elements provisions two databases per environment: an app database and a test database. The app database holds the application's data. Every build's tests run against the test database, leaving the app database untouched. Tests automatically route to the test database. Route handlers, `@rpc` functions, jobs, and cron all run against the app database. Elements picks the right database based on the calling context, so there is no flag to flip per query. Tests are not a separate environment. They run as part of the build in every environment (development, production, staging), against that environment's test database. If you want a dedicated environment for pre-production verification, configure a `staging` environment. The default database names are `` and `_test`, derived from your project name. Override the app database name under `database.name` in `config.jsoc`; the test database always tracks `_test`. Elements maintains two schemas in every database. Application tables live on the `public` schema. Elements-internal tables, including the migrations table and the background jobs queue, live on a separate `elements` schema. This keeps Elements' bookkeeping out of the way of your own tables and reuses Postgres's own namespacing instead of inventing a prefix convention.