import SupportedDatabases from '/docs/snippets/supported-databases.md'
Database configuration#
`/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.
:::strapi Supported databases
The following databases are supported by Strapi:
:::
Configuration structure#
The /config/database.js|ts file accepts 2 main configuration objects:
connectionfor database configuration options passed tosettingsfor Strapi-specific database settings
connection configuration object#
| Parameter | Description | Type | Default |
|---|---|---|---|
client | Database client to create the connection. Accepts the following values:
| String | - |
connection | Database connection information | Object | - |
debug | Show database exchanges and errors. | Boolean | false |
useNullAsDefaultOptional, only for SQLite | Use NULL as a default value | Boolean | true |
poolOptional | Database pooling options | Object | - |
acquireConnectionTimeoutOptional | How long knex will wait before throwing a timeout error when acquiring a connection (in milliseconds) | Integer | 60000 |
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:
| Parameter | Description | Type |
|---|---|---|
connectionString | Database connection string. When set, it overrides the other connection.connection properties. To disable use an empty string: ''. | String |
host | Database host name. Default value: localhost. | String |
port | Database port | Integer |
database or filename | Database name or filename.
| String |
user | Username used to establish the connection | String |
password | Password used to establish the connection | String |
timezone | Set the default behavior for local time. Default value: utc | String |
schema | Set the default database schema. Used only for Postgres DB. | String |
ssl | For SSL database connection. Use an object to pass certificate files as strings. | Boolean or Object |
Database pooling options#
The connection.pool object optionally found in ./config/database.js (or ./config/database.ts for TypeScript) is used to pass database pooling options and accepts the following parameters:
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 settings used by Knex.js for more information).
| Parameter | Description | Type | Default |
|---|---|---|---|
min | Minimum number of database connections to keepalive | Integer | 2 |
max | Maximum number of database connections to keepalive | Integer | 10 |
acquireTimeoutMillis | Time in milliseconds before timing out a database connection attempt | Integer | 60000 |
createTimeoutMillis | Time in milliseconds before timing out a create query attempt | Integer | 30000 |
destroyTimeoutMillis | Time in milliseconds before timing out a destroy query attempt | Integer | 5000 |
idleTimeoutMillis | Time in milliseconds before free database connections are destroyed | Integer | 30000 |
reapIntervalMillis | Time in milliseconds to check for idle database connections to destroy | Integer | 1000 |
createRetryIntervalMillis | Time in milliseconds to idle before retrying failed create actions | Integer | 200 |
afterCreate | Callback function to execute custom logic when the pool acquires a new connection. See the 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:
| Parameter | Description | Type | Default |
|---|---|---|---|
forceMigration | Allow 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. | Boolean | true |
runMigrations | Run the migration files found in /database/migrations on startup. Strapi's own internal migrations and the schema sync run regardless of this setting. | Boolean | true |
useTypescriptMigrations | Look for migration files in the build directory instead of the source directory, so that TypeScript migrations are executed. See handling migrations with TypeScript code. | Boolean | false |
poolTimeoutDiagnostics | Add 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. | Boolean | true |
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#
| Key | Description | Type |
|---|---|---|
code | Always DB_POOL_ACQUIRE_TIMEOUT. | String |
waitedMs | How long the caller waited before Knex gave up. | Number |
acquireTimeoutMs | The effective timeout Knex applied. | Number |
acquireConnectionTimeout | The configured connection.acquireConnectionTimeout, when set. | Number |
poolAcquireTimeoutMillis | The configured connection.pool.acquireTimeoutMillis, when set. | Number |
pool | Pool counts at the moment of the timeout: used, free, max, min, pendingAcquires, pendingCreates. | Object |
poolReason | The 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 |
eventLoopDelay | Event loop delay observed over the previous 30 to 60 seconds: maxMs, p99Ms, windowMs. | Object |
phase | Lifecycle phase during which the timeout occurred: boot, runtime, schema sync, or migration <name>. | String |
hostname | Host name of the process that saw the timeout. | String |
pid | Process ID of the process that saw the timeout. | Number |
Interpreting the diagnostics#
The diagnostics state facts and never name a cause. Common patterns:
| Scenario | What 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 connections | poolReason contains a driver error, for example a connection refusal or an authentication failure. |
| Starved Node.js process | eventLoopDelay.maxMs and eventLoopDelay.p99Ms are high even though the pool has free connections. |
| Timeout at boot or during a migration | phase 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#
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,
},
});
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.
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,
};
},
},
};
};
module.exports = ({ env }) => ({
connection: {
client: 'mysql',
connection: {
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: env('DATABASE_PASSWORD', 'strapi'),
ssl: {
rejectUnauthorized: env.bool('DATABASE_SSL_SELF', false), // For self-signed certificates
},
},
debug: false,
},
});
module.exports = ({ env }) => ({
connection: {
client: 'sqlite',
connection: {
filename: env('DATABASE_FILENAME', '.tmp/data.db'),
},
useNullAsDefault: true,
debug: false,
},
});
import path from 'path';
import type { Core } from '@strapi/strapi';
export default ({ env }: Core.Config.Shared.ConfigParams): Core.Config.Database => ({
connection: {
client: 'sqlite',
connection: {
filename: path.join(
__dirname,
'..',
'..',
env('DATABASE_FILENAME', path.join('.tmp', 'data.db'))
),
},
useNullAsDefault: true,
},
});
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 anapi,pluginorcore. By default it'score.name(string): You have to set the plugin or api name iftypeisapiorplugin.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.
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.
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,
};
},
},
};
};
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', '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,
};
},
},
};
};
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),
},
};
};
import path from 'path';
import type { Core } from '@strapi/strapi';
export default ({ env }: Core.Config.Shared.ConfigParams): Core.Config.Database => {
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:
# 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
DATABASE_CLIENT=postgres
DATABASE_HOST=127.0.0.1
DATABASE_PORT=5432
DATABASE_NAME=strapi
DATABASE_USERNAME=strapi
DATABASE_PASSWORD=strapi
DATABASE_SSL=false
# Database
DATABASE_CLIENT=sqlite
DATABASE_FILENAME=.tmp/data.db
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.connectionproperties such ashostandport, - 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_CLIENTandDATABASE_URLforMySQL,MariaDB, andPostgreSQL, - or set a minimum of
DATABASE_CLIENTandDATABASE_FILENAMEforSQLite.
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 option | environment variable storage |
|---|---|
| Virtual private server/virtual machine (e.g. AWS EC2) | ecosystem.config.js or .env |
| DigitalOcean App Platform | Environment Variables table |
| Heroku | Config 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:
yarn create strapi my-project --non-interactive
npx create-strapi@latest 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:
yarn add better-sqlite3
npm install better-sqlite3
Add the following code to your /config/database.ts|js file:
module.exports = ({ env }) => ({
connection: {
client: 'sqlite',
connection: {
filename: path.join(__dirname, '..', env('DATABASE_FILENAME', '.tmp/data.db')),
},
useNullAsDefault: true,
},
});
import path from 'path';
import type { Core } from '@strapi/strapi';
export default ({ env }: Core.Config.Shared.ConfigParams): Core.Config.Database => ({
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:
# 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;