Manual Database

Database

elements man database Read as markdown

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.

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).

config.jsoc
{ database: { name: "my_app", host: env("DB_HOST", "127.0.0.1"), port: env<number>("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.

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.

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:

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 <app> and <app>_test, derived from your project name. Override the app database name under database.name in config.jsoc; the test database always tracks <name>_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.