Skip to main content

Database configuration

Page summary:

/config/database defines connections, clients, and pooling. Strapi supports SQLite, MySQL, MariaDB, and PostgreSQL. MongoDB and other NoSQL databases are not supported. NoSQL support existed in Strapi v3 only.

The /config/database.js|ts file is used to define database connections that will be used to store the application content.

Supported databases

The following databases are supported by Strapi:

DatabaseRecommendedMinimum
MySQL8.48.0
MariaDB11.410.3
PostgreSQL17.014.0
SQLite33

Strapi does not support MongoDB (or any NoSQL databases), nor does it support any "Cloud Native" databases (e.g., Amazon Aurora, Google Cloud SQL, etc.).

Warning

Strapi applications are not meant to be connected to a pre-existing database, not created by a Strapi application, nor connected to a Strapi v3 database. The Strapi team will not support such attempts. Attempting to connect to an unsupported database may, and most likely will, result in lost data.

Configuration structure​

The /config/database.js|ts file accepts 2 main configuration objects:

connection configuration object​

ParameterDescriptionTypeDefault
clientDatabase client to create the connection.
Accepts the following values:
  • sqlite for SQLite databases
  • postgres for PostgreSQL databases
  • mysql for MySQL databases
String-
connectionDatabase connection informationObject-
debugShow database exchanges and errors.Booleanfalse
useNullAsDefault

Optional, only for SQLite
Use NULL as a default valueBooleantrue
pool

Optional
Database pooling optionsObject-
acquireConnectionTimeout

Optional
How long knex will wait before throwing a timeout error when acquiring a connection (in milliseconds)Integer60000
Note

Strapi only supports the following client values, and will automatically rewrite the client value to the following options before passing the configuration to Knex:

client valueActual package used
sqlitebetter-sqlite3
mysqlmysql2
postgrespg

Connection parameters​

The connection.connection object found in ./config/database.js (or ./config/database.ts for TypeScript) is used to pass database connection information and accepts the following parameters:

ParameterDescriptionType
connectionStringDatabase connection string. When set, it overrides the other connection.connection properties. To disable use an empty string: ''.String
hostDatabase host name. Default value: localhost.String
portDatabase portInteger
database or filenameDatabase name or filename.
  • For MySQL or PostgreSQL, use the database key (with host, port, etc.).
  • For SQLite, only provide filename which points to the database file.
String
userUsername used to establish the connectionString
passwordPassword used to establish the connectionString
timezoneSet the default behavior for local time. Default value: utc Timezone optionsString
schemaSet the default database schema. Used only for Postgres DB.String
sslFor SSL database connection.
Use an object to pass certificate files as strings.
Boolean or Object
Note

Depending on the database client used, more parameters can be set (e.g., charset and collation for mysql). Check the database client documentation to know what parameters are available, for instance the pg, mysql, and better-sqlite3 documentations.

Tip

The connection object is passed to Knex. If Knex supports additional options that are not listed on this page (for example expirationChecker for short-lived credentials), you can add them here.

Database pooling options​

The connection.pool object optionally found in ./config/database.js (or ./config/database.ts for TypeScript) is used to pass Tarn.js database pooling options and accepts the following parameters:

Caution

When using Docker, change the pool min value to 0 as Docker will kill any idle connections, making it impossible to keep any open connections to the database (see the Tarn.js pool settings used by Knex.js for more information).

ParameterDescriptionTypeDefault
minMinimum number of database connections to keepaliveInteger2
maxMaximum number of database connections to keepaliveInteger10
acquireTimeoutMillisTime in milliseconds before timing out a database connection attemptInteger60000
createTimeoutMillisTime in milliseconds before timing out a create query attemptInteger30000
destroyTimeoutMillisTime in milliseconds before timing out a destroy query attemptInteger5000
idleTimeoutMillisTime in milliseconds before free database connections are destroyedInteger30000
reapIntervalMillisTime in milliseconds to check for idle database connections to destroyInteger1000
createRetryIntervalMillisTime in milliseconds to idle before retrying failed create actionsInteger200
afterCreateCallback function to execute custom logic when the pool acquires a new connection.

See the Knex.js documentation for more information
Function-

settings configuration object​

The settings object found in ./config/database.js (or ./config/database.ts for TypeScript) is used to configure Strapi-specific database settings and accepts the following parameters:

ParameterDescriptionTypeDefault
forceMigrationAllow the schema sync to drop tables, columns, indexes, and foreign keys that are no longer part of the content-types schemas. Set to false to skip every drop operation.Booleantrue
runMigrationsRun the migration files found in /database/migrations on startup. Strapi's own internal migrations and the schema sync run regardless of this setting.Booleantrue
useTypescriptMigrationsLook for migration files in the build directory instead of the source directory, so that TypeScript migrations are executed. See handling migrations with TypeScript code.Booleanfalse
poolTimeoutDiagnosticsAdd pool state, event loop delay, and phase information to connection pool acquire timeout errors, and log a periodic warning while the pool keeps timing out. Set to false to leave Knex's original error and message untouched. See connection pool timeouts.Booleantrue
Caution

Despite its name, forceMigration does not control whether migrations run, which is what runMigrations does. It controls whether the schema sync is allowed to drop database objects. Setting it to false prevents data loss, but the schema sync still records the new schema as the reference, so a table whose deletion was skipped is no longer tracked by Strapi and is not dropped if you later set the parameter back to true. See database migrations.

Connection pool timeouts​

When Knex (through Tarn.js) cannot acquire a database connection before acquireConnectionTimeout or pool.acquireTimeoutMillis elapses, whichever is smaller, it throws a KnexTimeoutError. Unless settings.poolTimeoutDiagnostics is false, Strapi appends a link to this section to the error's message. It also attaches an error.details object describing the pool at that moment. The error's class, name, and underlying stack stay unchanged.

Strapi also logs a warning block, at most once every 30 seconds while the pool keeps timing out. Neither error.details nor the warning includes SQL statements or query parameters.

[database] connection pool timeout: waited 1523 ms for a connection (acquire timeout 1500 ms)
- connections: 2 in use, 0 free, max 2, min 0
- waiting for a connection: 37, connections being opened: 0
- reason reported by the pool: operation timed out for an unknown reason
- event loop delay over the last 11 s: max 323 ms, p99 41 ms
- phase runtime, host api-7f9c, pid 981242
See https://docs.strapi.io/cms/configurations/database#connection-pool-timeouts.

error.details structure​

KeyDescriptionType
codeAlways DB_POOL_ACQUIRE_TIMEOUT.String
waitedMsHow long the caller waited before Knex gave up.Number
acquireTimeoutMsThe effective timeout Knex applied.Number
acquireConnectionTimeoutThe configured connection.acquireConnectionTimeout, when set.Number
poolAcquireTimeoutMillisThe configured connection.pool.acquireTimeoutMillis, when set.Number
poolPool counts at the moment of the timeout: used, free, max, min, pendingAcquires, pendingCreates.Object
poolReasonThe reason reported by the pool for the failed attempt, for example a driver error, or "operation timed out for an unknown reason" when the pool cannot say why.String
eventLoopDelayEvent loop delay observed over the previous 30 to 60 seconds: maxMs, p99Ms, windowMs.Object
phaseLifecycle phase during which the timeout occurred: boot, runtime, schema sync, or migration <name>.String
hostnameHost name of the process that saw the timeout.String
pidProcess ID of the process that saw the timeout.Number

Interpreting the diagnostics​

The diagnostics state facts and never name a cause. Common patterns:

ScenarioWhat the diagnostics show
Saturated pool (burst of requests, or a long-running query)pool.used at or near max, and a high pendingAcquires count.
Database refusing or unable to accept connectionspoolReason contains a driver error, for example a connection refusal or an authentication failure.
Starved Node.js processeventLoopDelay.maxMs and eventLoopDelay.p99Ms are high even though the pool has free connections.
Timeout at boot or during a migrationphase is boot, schema sync, or migration <name> instead of runtime.

GraphQL responses​

An unexpected error returned by the GraphQL API no longer copies the internal error's name, message, and details (including the pool timeout diagnostics above) into the response. It now returns the same generic Internal Server Error as the REST API. The full error, diagnostics included, is still logged server-side. A custom resolver that throws a plain Error to return a message to clients should throw an ApplicationError from @strapi/utils instead.

Configuration examples​

./config/database.js
module.exports = ({ env }) => ({
connection: {
client: 'postgres',
connection: {
connectionString: env('DATABASE_URL'),
host: env('DATABASE_HOST', '127.0.0.1'),
port: env.int('DATABASE_PORT', 5432),
database: env('DATABASE_NAME', 'strapi'),
user: env('DATABASE_USERNAME', 'strapi'),
password: env('DATABASE_PASSWORD', 'strapi'),
schema: env('DATABASE_SCHEMA', 'public'), // Not required
ssl: env.bool('DATABASE_SSL', false) && {
rejectUnauthorized: env.bool('DATABASE_SSL_SELF', false), // For self-signed certificates
},
},
pool: { min: env.int('DATABASE_POOL_MIN', 2), max: env.int('DATABASE_POOL_MAX', 10) },
debug: false,
},
});
Caution

Strapi is aware that there is an issue regarding SSL support for the server. In order to fix it, you have to set the ssl:{} object as a boolean in order to disable it. See below for example:

module.exports = ({ env }) => ({
connection: {
client: 'postgres',
connection: {
...
ssl: env('DATABASE_SSL', false)
},
},
});

Please note that if you need client side SSL CA verification you will need to use the ssl:{} object with the fs module to convert your CA certificate to a string. You can see an example below:

const fs = require('fs');
module.exports = ({ env }) => ({
connection: {
client: 'postgres',
connection: {
...
ssl: {
ca: fs.readFileSync(`${__dirname}/path/to/your/ca-certificate.crt`).toString(),
},
},
},
});

Example: PostgreSQL with AWS RDS IAM authentication (dynamic password token)

Knex supports short-lived credentials by letting you provide connection.connection as a function and by checking whether a token needs renewal via expirationChecker.

./config/database.ts
import { Signer } from '@aws-sdk/rds-signer';
import type { Core } from '@strapi/strapi';

export default ({ env }: Core.Config.Shared.ConfigParams): Core.Config.Database => {
const signer = new Signer({
hostname: env('DATABASE_HOST', 'localhost'),
port: env.int('DATABASE_PORT', 5432),
username: env('DATABASE_USERNAME', 'strapi'),
});

return {
connection: {
client: 'postgres',
connection: async () => {
const token = await signer.getAuthToken();
const expiresAt = Date.now() + 15 * 60 * 1000;

return {
host: env('DATABASE_HOST', 'localhost'),
port: env.int('DATABASE_PORT', 5432),
database: env('DATABASE_NAME', 'strapi'),
user: env('DATABASE_USERNAME', 'strapi'),
password: token,
schema: env('DATABASE_SCHEMA', 'public'),
ssl: true,
expirationChecker: () => expiresAt - Date.now() <= 5 * 60 * 1000,
};
},
},
};
};
Note

AWS RDS IAM authentication requires SSL. Refer to the AWS documentation for the required certificate bundle and SSL parameters.

Configuration in database​

Configuration files are not multi-server friendly. To update configurations in production you can use a data store to get and set settings.

Get settings​

  • environment (string): Sets the environment you want to store the data in. By default it's current environment (can be an empty string if your configuration is environment agnostic).
  • type (string): Sets if your configuration is for an api, plugin or core. By default it's core.
  • name (string): You have to set the plugin or api name if type is api or plugin.
  • key (string, required): The name of the key you want to store.
// strapi.store(object).get(object);
// create reusable plugin store variable
const pluginStore = strapi.store({
environment: strapi.config.environment,
type: 'plugin',
name: 'users-permissions',
});
await pluginStore.get({ key: 'grant' });

Set settings​

  • value (any, required): The value you want to store.
// strapi.store(object).set(object);
// create reusable plugin store variable
const pluginStore = strapi.store({
environment: strapi.config.environment,
type: 'plugin',
name: 'users-permissions'
});
await pluginStore.set({
key: 'grant',
value: {
...
}
});

Dynamic database credentials​

Some cloud providers issue short-lived database credentials that expire after a fixed period. For example, AWS RDS IAM tokens are valid for 15 minutes; GCP Cloud SQL IAM tokens expire similarly; HashiCorp Vault can rotate secrets on a schedule. Using a static password in .env causes intermittent connection failures when the connection pool recycles idle connections after the token has expired.

Strapi supports dynamic credentials through Knex's connection function pattern. Instead of a plain connection object, you can pass a synchronous or asynchronous function to connection.connection. Knex calls this function whenever it needs to open a new database connection, ensuring the pool always receives fresh credentials. This pattern works for PostgreSQL and MySQL/MariaDB. TypeScript types officially accept sync and async functions for connection.connection as of Strapi v5.51.0.

Tip

Include an expirationChecker property in the object returned by your function. Knex calls this before reusing a connection from the pool; if it returns true, Knex discards the existing connection and calls your function again to obtain fresh credentials.

MySQL/MariaDB with AWS RDS IAM authentication​

The following example mints a fresh IAM auth token from the AWS SDK each time the pool opens a new MySQL connection. For a PostgreSQL equivalent, see the PostgreSQL + AWS RDS IAM authentication example in the configuration examples above.

./config/database.js
const { Signer } = require('@aws-sdk/rds-signer');

module.exports = ({ env }) => {
const signer = new Signer({
hostname: env('DATABASE_HOST', '127.0.0.1'),
port: env.int('DATABASE_PORT', 3306),
username: env('DATABASE_USERNAME', 'strapi'),
});

return {
connection: {
client: 'mysql',
connection: async () => {
const token = await signer.getAuthToken();
const expiresAt = Date.now() + 15 * 60 * 1000;

return {
host: env('DATABASE_HOST', '127.0.0.1'),
port: env.int('DATABASE_PORT', 3306),
database: env('DATABASE_NAME', 'strapi'),
user: env('DATABASE_USERNAME', 'strapi'),
password: token,
ssl: true,
expirationChecker: () => expiresAt - Date.now() <= 5 * 60 * 1000,
};
},
},
};
};
Note

AWS RDS IAM authentication requires SSL and IAM database authentication to be enabled on the RDS instance. Refer to the AWS documentation for the required certificate bundle and setup instructions.

Environment variables in database configurations​

Strapi includes the database configuration options in the ./config/database.js or ./config/database.ts file. When a new project is created the environment variable DATABASE_CLIENT with the value mysql, postgres, or sqlite is automatically added to the .env file depending on which database you choose during project creation. Additionally, all of the environment variables necessary to connect to your local development database are also added to the .env file. The following is an example of the generated configuration file:

const path = require('path');

module.exports = ({ env }) => {
const client = env('DATABASE_CLIENT', 'sqlite');

const connections = {
mysql: {
connection: {
connectionString: env('DATABASE_URL'),
host: env('DATABASE_HOST', 'localhost'),
port: env.int('DATABASE_PORT', 3306),
database: env('DATABASE_NAME', 'strapi'),
user: env('DATABASE_USERNAME', 'strapi'),
password: env('DATABASE_PASSWORD', 'strapi'),
ssl: env.bool('DATABASE_SSL', false) && {
key: env('DATABASE_SSL_KEY', undefined),
cert: env('DATABASE_SSL_CERT', undefined),
ca: env('DATABASE_SSL_CA', undefined),
capath: env('DATABASE_SSL_CAPATH', undefined),
cipher: env('DATABASE_SSL_CIPHER', undefined),
rejectUnauthorized: env.bool(
'DATABASE_SSL_REJECT_UNAUTHORIZED',
true
),
},
},
pool: { min: env.int('DATABASE_POOL_MIN', 2), max: env.int('DATABASE_POOL_MAX', 10) },
},
postgres: {
connection: {
connectionString: env('DATABASE_URL'),
host: env('DATABASE_HOST', 'localhost'),
port: env.int('DATABASE_PORT', 5432),
database: env('DATABASE_NAME', 'strapi'),
user: env('DATABASE_USERNAME', 'strapi'),
password: env('DATABASE_PASSWORD', 'strapi'),
ssl: env.bool('DATABASE_SSL', false) && {
key: env('DATABASE_SSL_KEY', undefined),
cert: env('DATABASE_SSL_CERT', undefined),
ca: env('DATABASE_SSL_CA', undefined),
capath: env('DATABASE_SSL_CAPATH', undefined),
cipher: env('DATABASE_SSL_CIPHER', undefined),
rejectUnauthorized: env.bool(
'DATABASE_SSL_REJECT_UNAUTHORIZED',
true
),
},
schema: env('DATABASE_SCHEMA', 'public'),
},
pool: { min: env.int('DATABASE_POOL_MIN', 2), max: env.int('DATABASE_POOL_MAX', 10) },
},
sqlite: {
connection: {
filename: path.join(
__dirname,
'..',
env('DATABASE_FILENAME', 'data.db')
),
},
useNullAsDefault: true,
},
};

return {
connection: {
client,
...connections[client],
acquireConnectionTimeout: env.int('DATABASE_CONNECTION_TIMEOUT', 60000),
},
};
};

The following are examples of the corresponding .env file database-related keys for each of the possible databases:

terminal

# Database
DATABASE_CLIENT=mysql
DATABASE_HOST=127.0.0.1
DATABASE_PORT=3306
DATABASE_NAME=strapi
DATABASE_USERNAME=strapi
DATABASE_PASSWORD=strap1
DATABASE_SSL=false

Database connections using connectionString​

Many managed database solutions use the property connectionString to connect a database to an application. Strapi includes the connectionString property. The connectionString is a concatenation of all the database properties in the connection.connection object. The connectionString:

  • overrides the other connection.connection properties such as host and port,
  • can be disabled by setting the property to an empty string: ''.

Database management by environment​

Development of a Strapi application commonly includes customization in the local development environment with a local development database, such as SQLite. When the application is ready for another environment such as production or staging the application is deployed with a different database instance, usually MySQL, MariaDB, or PostgreSQL. Database environment variables allow you to switch the attached database. To switch the database connection:

  • set a minimum of the DATABASE_CLIENT and DATABASE_URL for MySQL, MariaDB, and PostgreSQL,
  • or set a minimum of DATABASE_CLIENT and DATABASE_FILENAME for SQLite.

For deployed versions of your application the database environment variables should be stored wherever your other secrets are stored. The following table gives examples of where the database environment variables should be stored:

Hosting optionenvironment variable storage
Virtual private server/virtual machine (e.g. AWS EC2)ecosystem.config.js or .env
DigitalOcean App PlatformEnvironment Variables table
HerokuConfig vars table

Databases installation​

Strapi gives you the option to choose the most appropriate database for your project. Strapi supports MySQL, MariaDB, PostgreSQL, and SQLite.

SQLite​

SQLite is the default (see Quick Start Guide) and recommended database to quickly create an application locally.

Install SQLite during application creation​

Use one of the following commands:

terminal
yarn create strapi my-project --non-interactive

This creates a new project that uses SQLite, without asking any questions. To start it, run yarn develop or npm run develop from the project folder.

Install SQLite manually​

In a terminal, run the following command:

terminal
yarn add better-sqlite3

Add the following code to your /config/database.ts|js file:

./config/database.js
module.exports = ({ env }) => ({
connection: {
client: 'sqlite',
connection: {
filename: path.join(__dirname, '..', env('DATABASE_FILENAME', '.tmp/data.db')),
},
useNullAsDefault: true,
},
});

PostgreSQL​

When connecting Strapi to a PostgreSQL database, the database user requires SCHEMA permissions. While the database admin has this permission by default, a new database user explicitly created for the Strapi application will not. This would result in a 500 error when trying to load the admin console.

To create a new PostgreSQL user with the SCHEMA permission, use the following steps:

terminal
# Create a new database user with a secure password
$ CREATE USER my_strapi_db_user WITH PASSWORD 'password';
# Connect to the database as the PostgreSQL admin
$ \c my_strapi_db_name admin_user
# Grant schema privileges to the user
$ GRANT ALL ON SCHEMA public TO my_strapi_db_user;
Was this page helpful?