# How to Use n8n with Self-Hosted PostgreSQL or MySQL Databases

- Tool: n8n
- Difficulty: Beginner
- Time required: 20-30 minutes
- Compatibility: n8n 1.0+, npm and Docker installations
- Last updated: March 2026

## TL;DR

n8n supports PostgreSQL as its recommended backend database and MySQL as a deprecated alternative. Set the DB_TYPE environment variable to postgresdb and provide connection details via DB_POSTGRESDB_HOST, DB_POSTGRESDB_PORT, DB_POSTGRESDB_DATABASE, DB_POSTGRESDB_USER, and DB_POSTGRESDB_PASSWORD. This replaces the default SQLite and enables production-grade reliability with proper backup and scaling options.

## How to Use n8n with Self-Hosted Databases

By default, n8n uses SQLite to store workflows, credentials, and execution data. While SQLite works well for development and small deployments, production environments benefit from using PostgreSQL or MySQL as the backend database. An external database provides better concurrency, easier backups, replication support, and the ability to run multiple n8n instances against the same database. PostgreSQL is the recommended choice as MySQL support is deprecated.

## Before you start

- A running PostgreSQL 13+ server accessible from your n8n host
- A dedicated database and user created for n8n
- n8n installed via npm or Docker
- Basic knowledge of environment variables and database connection strings
- Terminal or SSH access to your n8n server

## Step-by-step guide

### 1. Create a PostgreSQL database and user for n8n

Log in to your PostgreSQL server and create a dedicated database and user for n8n. Using a separate user with limited permissions follows security best practices and prevents n8n from affecting other databases on the same server. Grant the user full access to the n8n database only.

```
-- Connect to PostgreSQL as the admin user
-- psql -U postgres

-- Create a dedicated user for n8n
CREATE USER n8n_user WITH PASSWORD 'your_secure_password_here';

-- Create the n8n database
CREATE DATABASE n8n_db OWNER n8n_user;

-- Grant all privileges on the database to the n8n user
GRANT ALL PRIVILEGES ON DATABASE n8n_db TO n8n_user;

-- Verify the database was created
\l
```

**Expected result:** A new database called n8n_db and a user called n8n_user are created in PostgreSQL. The user has full ownership of the database.

### 2. Set environment variables for npm installations

For npm installations, set the database connection environment variables before starting n8n. You can set them in your shell profile, a .env file, or your systemd service file. The required variables are DB_TYPE, DB_POSTGRESDB_HOST, DB_POSTGRESDB_PORT, DB_POSTGRESDB_DATABASE, DB_POSTGRESDB_USER, and DB_POSTGRESDB_PASSWORD.

```
# Set environment variables in your shell or .env file
export DB_TYPE=postgresdb
export DB_POSTGRESDB_HOST=localhost
export DB_POSTGRESDB_PORT=5432
export DB_POSTGRESDB_DATABASE=n8n_db
export DB_POSTGRESDB_USER=n8n_user
export DB_POSTGRESDB_PASSWORD=your_secure_password_here

# Optional: Set the schema (default is 'public')
export DB_POSTGRESDB_SCHEMA=public

# Optional: Enable SSL for remote databases
export DB_POSTGRESDB_SSL_ENABLED=true

# Start n8n with the database configuration
n8n start
```

**Expected result:** n8n starts and connects to the PostgreSQL database. On first startup, it automatically creates all required tables and indexes.

### 3. Set environment variables for Docker installations

For Docker installations, pass the environment variables when creating the container using the -e flag or a docker-compose.yml file. The Docker approach is the most common for production deployments. Make sure the n8n container can reach the PostgreSQL server over the network.

```
# Docker run command with PostgreSQL configuration
docker run -d \
  --name n8n \
  -p 5678:5678 \
  -e DB_TYPE=postgresdb \
  -e DB_POSTGRESDB_HOST=your-postgres-host \
  -e DB_POSTGRESDB_PORT=5432 \
  -e DB_POSTGRESDB_DATABASE=n8n_db \
  -e DB_POSTGRESDB_USER=n8n_user \
  -e DB_POSTGRESDB_PASSWORD=your_secure_password_here \
  -v n8n_data:/home/node/.n8n \
  docker.n8n.io/n8nio/n8n

# Or use docker-compose.yml (see complete code below)
```

**Expected result:** The n8n Docker container starts and connects to PostgreSQL. The n8n logs show a successful database connection message.

### 4. Verify the database connection

After starting n8n with the PostgreSQL configuration, verify the connection is working. Check the n8n logs for any database connection errors. Then open the n8n editor in your browser and create a test workflow to confirm data is being stored in PostgreSQL. You can also query the PostgreSQL database directly to see the tables n8n created.

```
# Check n8n logs for database connection status
# npm installation
n8n start 2>&1 | head -20

# Docker installation
docker logs n8n

# Verify tables were created in PostgreSQL
psql -U n8n_user -d n8n_db -c "\dt"

# Expected tables include:
# workflow_entity
# credentials_entity
# execution_entity
# webhook_entity
# tag_entity
```

**Expected result:** n8n logs show no database errors. The PostgreSQL database contains n8n tables. Creating and saving a workflow in the editor writes data to the PostgreSQL database.

### 5. Migrate existing data from SQLite to PostgreSQL (optional)

If you have existing workflows and credentials in SQLite that you want to keep, export them before switching databases. Use the n8n CLI to export workflows and credentials to JSON files, then import them after configuring the PostgreSQL database. This approach is safer than trying to migrate the raw database files.

```
# Step 1: Export workflows from SQLite (before changing DB_TYPE)
n8n export:workflow --all --output=./workflows-backup.json

# Step 2: Export credentials
n8n export:credentials --all --output=./credentials-backup.json

# Step 3: Switch to PostgreSQL (set the DB_TYPE and connection vars)
export DB_TYPE=postgresdb
export DB_POSTGRESDB_HOST=localhost
export DB_POSTGRESDB_PORT=5432
export DB_POSTGRESDB_DATABASE=n8n_db
export DB_POSTGRESDB_USER=n8n_user
export DB_POSTGRESDB_PASSWORD=your_secure_password_here

# Step 4: Start n8n to create the PostgreSQL schema
n8n start &
sleep 10

# Step 5: Import workflows into PostgreSQL
n8n import:workflow --input=./workflows-backup.json

# Step 6: Import credentials
n8n import:credentials --input=./credentials-backup.json
```

**Expected result:** All workflows and credentials from your SQLite installation are now available in the PostgreSQL-backed n8n instance.

## Complete code example

File: `docker-compose.yml`

```yaml
version: '3.8'

services:
  n8n:
    image: docker.n8n.io/n8nio/n8n
    container_name: n8n
    restart: unless-stopped
    ports:
      - '5678:5678'
    environment:
      # Database configuration
      - DB_TYPE=postgresdb
      - DB_POSTGRESDB_HOST=postgres
      - DB_POSTGRESDB_PORT=5432
      - DB_POSTGRESDB_DATABASE=n8n_db
      - DB_POSTGRESDB_USER=n8n_user
      - DB_POSTGRESDB_PASSWORD=${N8N_DB_PASSWORD}
      - DB_POSTGRESDB_SCHEMA=public
      # n8n configuration
      - N8N_HOST=localhost
      - N8N_PORT=5678
      - N8N_PROTOCOL=http
      - WEBHOOK_URL=http://localhost:5678/
      # Execution data retention (days)
      - EXECUTIONS_DATA_MAX_AGE=168
      - EXECUTIONS_DATA_PRUNE=true
    volumes:
      - n8n_data:/home/node/.n8n
    depends_on:
      postgres:
        condition: service_healthy

  postgres:
    image: postgres:16-alpine
    container_name: n8n-postgres
    restart: unless-stopped
    environment:
      - POSTGRES_DB=n8n_db
      - POSTGRES_USER=n8n_user
      - POSTGRES_PASSWORD=${N8N_DB_PASSWORD}
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ['CMD-SHELL', 'pg_isready -U n8n_user -d n8n_db']
      interval: 10s
      timeout: 5s
      retries: 5

volumes:
  n8n_data:
  postgres_data:
```

## Common mistakes

- **Using localhost as the PostgreSQL host in Docker when PostgreSQL runs on the host machine** — undefined Fix: Use the Docker host IP (host.docker.internal on Mac/Windows, or the host network IP on Linux) instead of localhost. In docker-compose, use the service name as the hostname.
- **Forgetting to create the database before starting n8n** — undefined Fix: n8n creates tables and indexes automatically but does not create the database itself. Create the database and user in PostgreSQL before starting n8n with the database configuration.
- **Losing credential encryption keys when switching databases** — undefined Fix: The encryption key is stored in the .n8n directory (config file). Ensure the same .n8n directory is mounted or accessible after switching databases. Without the encryption key, imported credentials cannot be decrypted.
- **Not enabling execution data pruning, causing the database to grow without limit** — undefined Fix: Set EXECUTIONS_DATA_PRUNE=true and EXECUTIONS_DATA_MAX_AGE=168 (hours) to automatically delete old execution data. Without pruning, the execution_entity table can grow to gigabytes.

## Best practices

- Always use PostgreSQL for production deployments — MySQL support is deprecated and may be removed in future n8n versions
- Use a dedicated database user with access only to the n8n database, not a superuser account
- Enable SSL for database connections when PostgreSQL runs on a different server
- Set up automated PostgreSQL backups using pg_dump on a cron schedule
- Configure execution data pruning with EXECUTIONS_DATA_PRUNE and EXECUTIONS_DATA_MAX_AGE to prevent the database from growing unbounded
- Use Docker Compose with health checks to ensure PostgreSQL is ready before n8n starts
- Store database passwords in environment variables or secrets management, never in configuration files committed to version control
- Monitor PostgreSQL disk usage and performance, especially the execution_entity table which grows quickly

## Frequently asked questions

### Should I use PostgreSQL or MySQL for n8n?

Use PostgreSQL. MySQL support in n8n is deprecated and may be removed in future versions. PostgreSQL provides better performance, more reliable migrations, and is the officially recommended database for production n8n deployments.

### Can I use a managed PostgreSQL service like AWS RDS or DigitalOcean Managed Databases?

Yes. Set the DB_POSTGRESDB_HOST to your managed database endpoint, provide the port, credentials, and enable SSL with DB_POSTGRESDB_SSL_ENABLED=true. Managed databases handle backups and replication automatically.

### Will switching from SQLite to PostgreSQL require downtime?

Yes. You need to stop n8n, export data, configure the new database, start n8n against PostgreSQL, and then import the data. Plan for 15-30 minutes of downtime depending on the amount of data.

### How much disk space does n8n use in PostgreSQL?

Workflow and credential data is small, typically under 100MB. Execution history is the main space consumer. Without pruning, it can grow to several gigabytes. Enable EXECUTIONS_DATA_PRUNE to control this.

### Can multiple n8n instances share the same PostgreSQL database?

Yes, but only in queue mode. Running multiple n8n instances in regular mode against the same database causes execution conflicts. Enable queue mode with EXECUTIONS_MODE=queue for multi-instance deployments.

### What happens if the PostgreSQL connection is lost while n8n is running?

n8n attempts to reconnect automatically. Active workflow executions may fail with database connection errors. Once the database is back online, n8n resumes normal operation. Check n8n logs for connection status.

---

Source: https://www.rapidevelopers.com/n8n-tutorial/how-to-use-n8n-with-self-hosted-databases
© RapidDev — https://www.rapidevelopers.com/n8n-tutorial/how-to-use-n8n-with-self-hosted-databases
