Skip to main content

Overview

Bitwarden Server requires a SQL database to store user data, vault items, organizations, and system configuration. The database is shared across all services.

Supported Databases

SQL Server

Recommended - SQL Server 2017+Microsoft’s enterprise database. Best performance and full feature support.

PostgreSQL

PostgreSQL 12+Open-source alternative with excellent performance.

MySQL / MariaDB

MySQL 8.0+ / MariaDB 10.5+Widely available open-source databases.
Production Recommendation: Use SQL Server 2022 or PostgreSQL 14+ for best performance and reliability.

Quick Start with Docker

SQL Server

# Start SQL Server container
docker run -d \
  --name bitwarden-mssql \
  -e "ACCEPT_EULA=Y" \
  -e "MSSQL_SA_PASSWORD=YourStrongPassword123!" \
  -e "MSSQL_PID=Developer" \
  -p 1433:1433 \
  -v mssql_data:/var/opt/mssql \
  mcr.microsoft.com/mssql/server:2022-latest

# Wait for SQL Server to start
sleep 30

# Create database
docker exec bitwarden-mssql /opt/mssql-tools/bin/sqlcmd \
  -S localhost -U sa -P "YourStrongPassword123!" \
  -Q "CREATE DATABASE vault; ALTER DATABASE vault SET RECOVERY SIMPLE;"

PostgreSQL

# Start PostgreSQL container
docker run -d \
  --name bitwarden-postgres \
  -e "POSTGRES_DB=vault" \
  -e "POSTGRES_USER=postgres" \
  -e "POSTGRES_PASSWORD=YourStrongPassword123!" \
  -p 5432:5432 \
  -v postgres_data:/var/lib/postgresql/data \
  postgres:14

MySQL

# Start MySQL container
docker run -d \
  --name bitwarden-mysql \
  -e "MYSQL_ROOT_PASSWORD=YourStrongPassword123!" \
  -e "MYSQL_DATABASE=vault" \
  -p 3306:3306 \
  -v mysql_data:/var/lib/mysql \
  mysql:8.0 \
  --default-authentication-plugin=mysql_native_password

Connection Strings

SQL Server

"globalSettings": {
  "sqlServer": {
    "connectionString": "Server=localhost;Database=vault;User Id=sa;Password=YourPassword;TrustServerCertificate=True;Encrypt=True;"
  }
}
Connection String Parameters:
  • Server - Hostname or IP address
  • Database - Database name (default: vault)
  • User Id - Database user
  • Password - Database password
  • TrustServerCertificate=True - Accept self-signed certificates (dev only)
  • Encrypt=True - Enable encryption (recommended)
  • MultipleActiveResultSets=True - Enable MARS (optional)

PostgreSQL

"globalSettings": {
  "sqlServer": {
    "connectionString": "Host=localhost;Port=5432;Database=vault;Username=postgres;Password=YourPassword;SSL Mode=Prefer;"
  }
}
Connection String Parameters:
  • Host - Hostname or IP address
  • Port - Port number (default: 5432)
  • Database - Database name
  • Username - Database user
  • Password - Database password
  • SSL Mode - Disable, Prefer, or Require

MySQL

"globalSettings": {
  "sqlServer": {
    "connectionString": "Server=localhost;Port=3306;Database=vault;Uid=root;Pwd=YourPassword;SslMode=Preferred;"
  }
}
Connection String Parameters:
  • Server - Hostname or IP address
  • Port - Port number (default: 3306)
  • Database - Database name
  • Uid - Database user
  • Pwd - Database password
  • SslMode - None, Preferred, or Required
Password Requirements: SQL Server requires passwords with:
  • At least 8 characters
  • At least one uppercase letter
  • At least one lowercase letter
  • At least one number
  • At least one special character

Database Migrations

Bitwarden uses database migrations to create and update the schema. Migrations must be run before starting services.

Using Migrator Utility

The recommended way to run migrations:
# SQL Server
docker run --rm \
  -e "globalSettings__sqlServer__connectionString=Server=mssql;Database=vault;User Id=sa;Password=YourPassword;TrustServerCertificate=True;" \
  --network bitwarden_default \
  ghcr.io/bitwarden/mssqlmigratorutility:latest

# PostgreSQL
docker run --rm \
  -e "globalSettings__sqlServer__connectionString=Host=postgres;Database=vault;Username=postgres;Password=YourPassword;" \
  -e "globalSettings__databaseProvider=postgres" \
  --network bitwarden_default \
  ghcr.io/bitwarden/postgresmigratorutility:latest

# MySQL
docker run --rm \
  -e "globalSettings__sqlServer__connectionString=Server=mysql;Database=vault;Uid=root;Pwd=YourPassword;" \
  -e "globalSettings__databaseProvider=mysql" \
  --network bitwarden_default \
  ghcr.io/bitwarden/mysqlmigratorutility:latest
The --network flag ensures the migrator can reach the database container. Adjust the network name based on your Docker Compose project.

Manual Migrations

For non-Docker deployments, run the migrator utility directly:
# Download migrator utility
cd /opt/bitwarden
git clone https://github.com/bitwarden/server.git
cd server/util/MsSqlMigratorUtility

# Build and run
dotnet run -- \
  --connection "Server=localhost;Database=vault;User Id=sa;Password=YourPassword;"

Hosted Service Migrations

Services can automatically run migrations on startup:
appsettings.json
{
  "globalSettings": {
    "runDatabaseMigrationsOnStartup": true
  }
}
Production Warning: Automatic migrations on startup can cause race conditions when multiple instances start simultaneously. Use the migrator utility instead.

Database Schema

The Bitwarden database contains these primary tables:
  • User - User accounts and profiles
  • Device - Registered devices per user
  • AuthRequest - Passwordless authentication requests
  • SsoUser - SSO user mappings
  • U2f - FIDO2/WebAuthn credentials
  • Cipher - Vault items (logins, cards, notes, etc.)
  • Folder - User folders
  • Collection - Organization collections
  • CollectionCipher - Collection-cipher relationships
  • Send - Temporary secret sharing
  • Organization - Organization accounts
  • OrganizationUser - User-organization memberships
  • Group - Organization groups
  • GroupUser - Group memberships
  • Policy - Organization policies
  • Event - Audit log events
  • EventSystemUser - System-generated events
  • SsoConfig - SSO configuration
  • OrganizationApiKey - API keys
  • Installation - Installation identifiers
  • Grant - OAuth 2.0 grants (IdentityServer)
  • Transaction - Payment transactions

Performance Optimization

Indexes

Bitwarden migrations create optimal indexes automatically. Key indexes:
  • Cipher.UserId - User vault queries
  • Cipher.OrganizationId - Organization vault queries
  • Event.Date - Event log queries
  • CollectionCipher.CipherId and CollectionCipher.CollectionId - Collection relationships

SQL Server Recommendations

-- Set recovery model to SIMPLE for smaller logs (non-production)
ALTER DATABASE vault SET RECOVERY SIMPLE;

-- Update statistics
EXEC sp_updatestats;

-- Check index fragmentation
SELECT 
    OBJECT_NAME(ips.object_id) AS TableName,
    ips.index_id,
    avg_fragmentation_in_percent
FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, 'LIMITED') ips
WHERE avg_fragmentation_in_percent > 30;

PostgreSQL Recommendations

-- Vacuum and analyze
VACUUM ANALYZE;

-- Update statistics
ANALYZE;

-- Check table sizes
SELECT 
    schemaname,
    tablename,
    pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) AS size
FROM pg_tables
WHERE schemaname = 'public'
ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC;

Connection Pooling

Enable connection pooling for better performance: SQL Server:
Server=localhost;Database=vault;User Id=sa;Password=pass;Pooling=true;Min Pool Size=5;Max Pool Size=100;
PostgreSQL:
Host=localhost;Database=vault;Username=postgres;Password=pass;Pooling=true;Minimum Pool Size=5;Maximum Pool Size=100;

Backup and Restore

SQL Server Backup

# Create backup
docker exec bitwarden-mssql /opt/mssql-tools/bin/sqlcmd \
  -S localhost -U sa -P "YourPassword" \
  -Q "BACKUP DATABASE vault TO DISK='/var/opt/mssql/backup/vault_$(date +%Y%m%d).bak' WITH COMPRESSION;"

# Copy backup from container
docker cp bitwarden-mssql:/var/opt/mssql/backup/vault_20240101.bak ./

# Restore backup
docker exec bitwarden-mssql /opt/mssql-tools/bin/sqlcmd \
  -S localhost -U sa -P "YourPassword" \
  -Q "RESTORE DATABASE vault FROM DISK='/var/opt/mssql/backup/vault_20240101.bak' WITH REPLACE;"

PostgreSQL Backup

# Create backup
docker exec bitwarden-postgres pg_dump -U postgres vault > vault_backup.sql

# Restore backup
docker exec -i bitwarden-postgres psql -U postgres vault < vault_backup.sql

MySQL Backup

# Create backup
docker exec bitwarden-mysql mysqldump -u root -p"YourPassword" vault > vault_backup.sql

# Restore backup
docker exec -i bitwarden-mysql mysql -u root -p"YourPassword" vault < vault_backup.sql
Automation: Set up automated daily backups using cron jobs or container orchestration tools.

Read Replicas

For high-traffic deployments, configure read replicas:
appsettings.json
{
  "globalSettings": {
    "sqlServer": {
      "connectionString": "Server=primary.db.local;Database=vault;User Id=sa;Password=pass;",
      "readOnlyConnectionString": "Server=replica.db.local;Database=vault;User Id=sa;Password=pass;ApplicationIntent=ReadOnly;"
    }
  }
}
Read operations (vault sync, searches) will use the replica automatically.

Troubleshooting

Symptoms: Services fail to start with connection errorsSolutions:
  • Verify database is running: docker ps
  • Check connection string credentials
  • Ensure database port is accessible
  • Test connection: telnet localhost 1433
  • Check firewall rules
Symptoms: Password validation failedSolutions:
  • Use a strong password with uppercase, lowercase, numbers, and symbols
  • Minimum 8 characters
  • Example: MyStr0ng!Pass
Symptoms: Migration errors during startupSolutions:
  • Run migrations manually using migrator utility
  • Check database user has CREATE TABLE permissions
  • Verify connection string is correct
  • Review migration logs for specific errors
Symptoms: Slow queries, timeoutsSolutions:
  • Check database resource usage (CPU, memory, disk)
  • Review slow query logs
  • Update statistics: EXEC sp_updatestats
  • Consider adding read replicas
  • Increase connection pool size

Security Best Practices

1

Use Strong Passwords

Generate random passwords with at least 20 characters for database users.
2

Restrict Network Access

Only allow connections from application servers. Use firewall rules or security groups.
3

Enable Encryption

Use TLS/SSL for database connections in production.
4

Regular Backups

Automate daily backups and test restore procedures regularly.
5

Monitor Access

Enable audit logging for database access and review logs regularly.
6

Principle of Least Privilege

Grant minimal required permissions to application database users.

Next Steps

Configuration

Configure connection strings in appsettings.json

Docker Deployment

Deploy services with Docker Compose

Backup & Restore

Set up automated backups

Monitoring

Monitor database performance

Build docs developers (and LLMs) love