Databases

Open Beta

Managed databases are available to every account. Create one from the Databases tab in the console.

This page covers creating a database, connecting it to a service, and running it day to day. For what a managed database is and how to pick a tier, see Databases.

How managed databases work

A database belongs to a Project. Every Service in that Project can connect to it; services in other projects can't. Separate environments are separate projects, so each environment gets its own isolated database.

On the shared tier your database runs on a cluster Forte operates. The storage is yours alone. The compute is shared with other databases on the same cluster.

Your code reaches the database over a TLS-encrypted endpoint — sslmode=require for PostgreSQL, tls=true for MongoDB-compatible databases. Most drivers handle this on their own when the connection string asks for it.

Create a database

Use the console or the CLI. The SDKs don't support database management yet.

From the console:

  1. Open the Databases tab in your project.
  2. Choose New database.
  3. Pick PostgreSQL or MongoDB as the engine and Shared as the tier (the MongoDB tile requests alpha access until it is granted).
  4. Name the database. Names take letters, numbers, hyphens, and underscores, and run 3 to 30 characters.
  5. Create it. The database is ready in under 30 seconds.

From the CLI:

bash
forte databases create --name orders-db
forte databases create --name events-db --type mongodb

Omit any argument and the CLI prompts for it. forte databases list and forte databases get show status and storage.

Storage

Shared-tier storage is customizable from 1 to 10 GB, and defaults to 2 GB if you don't choose one. Compute is shared across the cluster, so there's no CPU or memory setting — only storage.

For more than 10 GB, you need a dedicated cluster. Request one from the Dedicated tier tile when you create a database, or email support@forteplatforms.com.

Storage bills per GB-month: see Pricing and quotas.

Connect a database to a service

Open the database, choose Connect to a service, and pick the service. You then choose the environment variable names your code already reads: either a single connection string (DATABASE_URL by default) or discrete values for host, port, database, username and password. Forte suggests the names your framework expects.

When you map a connection string, you can also pick its format so the value matches the driver your service uses. Each format is tuned for the managed pooler (prepared statements disabled, the right SSL-mode spelling):

FormatForValue
URI (default)psql, node-postgres, Rails, Django, Gopostgresql://…?sslmode=require
JDBCJava / JVM (HikariCP, Spring Data JPA)jdbc:postgresql://…&prepareThreshold=0
R2DBCreactive JVM (Spring Data R2DBC)r2dbc:postgresql://…&preparedStatementCacheQueries=0
SQLAlchemyPython (psycopg2)postgresql+psycopg2://…?sslmode=require
Npgsql.NET / C#Host=…;SSL Mode=Require;No Reset On Close=true;Max Auto Prepare=0
PrismaPrisma ORMpostgresql://…?sslmode=require&pgbouncer=true
Customanything elseyour template with {host} {port} {database} {username} {password} {sslmode}

MongoDB-compatible databases use the URI format (mongodb://…?tls=true&authSource=admin) or a custom template; the driver-specific PostgreSQL formats don't apply.

Forte picks the format automatically when it detects your stack; override it in the connect dialog or with the CLI's --connection-string-format (and --connection-string-template for custom).

The CLI does the same thing:

bash
# Prompts for the variable name, pre-filled with DATABASE_URL
forte databases connections add
 
# Or name the variables outright
forte databases connections add --service <serviceId> \
  --host-var PGHOST --port-var PGPORT --database-var PGDATABASE \
  --username-var PGUSER --password-var PGPASSWORD

On confirm, Forte:

  1. Creates a database role dedicated to that service, with access to this database only.
  2. Sets the variables you mapped on the service.
  3. Redeploys the service so they reach the running container.

The password is generated by Forte and never shown: not in the console, not in the API response, and not in the service's own environment variable list. Your code reads it from the environment like any other variable:

ts
import { Pool } from "pg";
 
const pool = new Pool({ connectionString: process.env.DATABASE_URL, max: 2 });

For a MongoDB-compatible database, the mapped variable holds a mongodb:// URI instead:

ts
import { MongoClient } from "mongodb";
 
const client = new MongoClient(process.env.MONGODB_URI!, { maxPoolSize: 4 });

Changing the mapped names redeploys with the new names. Until that deployment is live, the connection shows Waiting for deploy and the running service keeps the old names.

Disconnecting removes the variables and redeploys the service without them. The current deployment keeps working until the new one is live; only then is the connection's role dropped. While that's in progress the connection shows Disconnecting. If the deployment fails, the service keeps running with its existing access, and the next successful deployment finishes the disconnect.

A service's own environment variables and secrets can't use a name a connected database owns, and vice versa: whichever you try second is rejected rather than silently overriding the other.

For a complete working app, see the Next.js + Node example, which stores per-user records in a connected Postgres database.

Manage database users

Connecting a service creates its role for you. Create a database user yourself when something outside Forte needs access: a migration tool, a SQL client, or an analytics job.

On the Users tab you can:

  • Create a user. Forte generates the password and shows it once, together with a ready-made connection string. Copy them before closing the dialog; Forte can't show the password again.
  • Rotate a password. The old password stops working immediately, so anything still using it fails until you update it. The new password and connection string are shown once.
  • Delete a user. Existing connections for that user are closed.

Each database holds up to 25 users.

Connect a SQL client

The database's overview page shows the host, port, database name and SSL mode. The username is the user's connection role — the full value shown on the Users tab, not the short name you typed when creating the user. The password is shown once, when you create the user or rotate its password, together with a complete connection string you can paste straight into your client.

Point any Postgres client at those values:

postgresql://CONNECTION_ROLE:PASSWORD@HOST:PORT/DATABASE?sslmode=require

Four things to get right:

  • Sign in as the connection role. Copy it from the Users tab's Connection role column.
  • Use the port shown in the console, not 5432. Connections arrive at a pooler, not at Postgres directly.
  • TLS is required. Set SSL mode to require (or stricter). A plaintext connection is refused.
  • Turn off server-side prepared statements. The endpoint pools per transaction, so prepared statements don't survive between queries. In DBeaver and DataGrip this is a driver property: prepareThreshold=0 on the JDBC URL does it. psql needs nothing.

Session state doesn't carry across queries either, so a client that sets a search path on connect won't keep it. Qualify your table names, or set the search path on the role itself with ALTER ROLE ... SET search_path.

Connect from a MongoDB client

The database's overview page shows the host, port, and database name. The username is the user's connection role — the full value shown on the Users tab. The password is shown once, when you create the user or rotate its password, together with a complete connection string you can paste straight into your client.

Point mongosh, Compass, or any MongoDB driver at those values:

mongosh "mongodb://<role>:<password>@<host>:27017/<database>?tls=true&authSource=admin"

TLS is required: a plaintext connection is refused.

Connection limits

Each database user gets 5 concurrent connections. That budget covers every client using that user at once, including every running instance of a service, since all instances share one role.

Size your connection pool accordingly. A service on 2 provisioned instances should cap its pool at 2 connections per instance, not 5.

Connections pass through a transaction-mode pooler, which reuses a server connection per transaction rather than per session. Session-scoped features don't survive that: avoid LISTEN/NOTIFY, session-level advisory locks, SET statements meant to persist across queries, and server-side prepared statements. Most drivers have a flag to turn off prepared statements: set it if your queries fail with prepared-statement errors.

Query limits

Forte applies limits to every database user so one query can't take the cluster down.

SettingValueWhat it does
statement_timeout30 seconds (PostgreSQL)Cancels a single query that runs longer.
Statement timeout120 seconds (MongoDB-compatible)Cancels a single command that runs longer.
transaction_timeout5 minutesCancels a transaction open longer than this.
idle_in_transaction_session_timeout60 secondsCancels a transaction left idle, releasing its locks.
work_mem16 MBMemory per sort or hash step before it spills to disk.
temp_file_limit256 MBThe most temporary file space one query can use.
Parallel queryOffQueries run on a single worker.

Batch jobs and large migrations need to work within these. Split long-running work into smaller transactions.

Monitor a database

The Monitoring tab charts queries, average query time, rows read and written, connections, and active time, over the last hour, 24 hours, or 7 days. The connections chart draws the 5-connection limit as a reference line.

The slow-query list shows the queries taking the longest, so you can find the missing index before it costs you a timeout. For MongoDB-compatible databases each entry shows the command as your driver sent it (find, aggregate, update, ...) with its collection, rendered as extended JSON; very large commands are recorded by operation and collection only.

When storage runs out

Forte tracks how much of your provisioned storage the database uses and warns you before it fills:

  • 80% full: Forte emails you.
  • 95% full: Forte emails you again.
  • 100% full: the database goes read-only. Queries still read; writes are rejected.

Deleting data is the fix, but deleting needs writes, so a full database can't clean itself up: contact support for a temporary write window, or grow the database's storage (up to the shared tier's 10 GB).

Database states

StateWhat it means
CreatingForte is provisioning the database.
ActiveThe database accepts reads and writes.
Read onlyWrites are rejected, reads still work. Storage is full, or support set it.
SuspendedConnections are closed and refused.
DeletingForte is removing the database.
FailedProvisioning failed. Contact support.

Backups

Forte backs up every database once a day. To restore from a backup, contact support.

Delete a database

Disconnect every service first: Forte blocks the delete while any connection remains. A connection shown as Disconnecting doesn't block it. Then open the database's settings, choose Delete, and type the database name to confirm.

Deleting a database destroys its data.

Limits

LimitValue
Databases per account5 (1 without a verified billing method)
Storage per database1–10 GB (2 GB default)
Users per database25
Services connected per database20
Concurrent connections per user5

To raise a limit, email support@forteplatforms.com.

Next steps

Search

Search your resources, console pages, and documentation