> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/calcom/cal.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Database Setup

> PostgreSQL configuration, migrations, and optimization for Cal.com

Cal.com uses PostgreSQL as its primary database. This guide covers database setup, migrations, connection pooling, and optimization.

## Requirements

* **PostgreSQL**: Version 13 or higher
* **Storage**: Minimum 20GB, recommended 100GB+ for production
* **Memory**: Minimum 2GB RAM allocated to PostgreSQL
* **Connection Limit**: Adjust based on your deployment scale

## Database Setup

### Local PostgreSQL Installation

#### Ubuntu/Debian

```bash theme={null}
# Install PostgreSQL
sudo apt-get update
sudo apt-get install postgresql postgresql-contrib

# Start PostgreSQL service
sudo systemctl start postgresql
sudo systemctl enable postgresql
```

#### macOS

```bash theme={null}
# Using Homebrew
brew install postgresql@15
brew services start postgresql@15
```

#### Windows

Download from [postgresql.org](https://www.postgresql.org/download/windows/) and follow the installer.

### Create Database

```bash theme={null}
# Connect to PostgreSQL
sudo -u postgres psql

# Create database and user
CREATE DATABASE calendso;
CREATE USER calcom_user WITH ENCRYPTED PASSWORD 'your_secure_password';
GRANT ALL PRIVILEGES ON DATABASE calendso TO calcom_user;

# Grant schema permissions (PostgreSQL 15+)
\c calendso
GRANT ALL ON SCHEMA public TO calcom_user;
\q
```

### Connection String Format

PostgreSQL connection strings follow this format:

```
postgresql://[user]:[password]@[host]:[port]/[database]?[parameters]
```

Examples:

```bash theme={null}
# Local database
DATABASE_URL="postgresql://calcom_user:password@localhost:5432/calendso"

# Remote with SSL
DATABASE_URL="postgresql://user:pass@db.example.com:5432/calendso?sslmode=require"

# With connection pooling
DATABASE_URL="postgresql://user:pass@pooler.example.com:6543/calendso?pgbouncer=true"

# Self-signed SSL (Heroku, etc.)
DATABASE_URL="postgresql://user:pass@host:5432/db?sslmode=no-verify"
```

### Environment Configuration

Add to your `.env` file:

```bash theme={null}
# Primary database connection
DATABASE_URL="postgresql://calcom_user:password@localhost:5432/calendso"

# Direct connection for migrations (same as DATABASE_URL if not using pooling)
DATABASE_DIRECT_URL="postgresql://calcom_user:password@localhost:5432/calendso"

# Optional: Separate analytics database
INSIGHTS_DATABASE_URL="postgresql://user:pass@host:5432/calendso_insights"

# Optional: SAML database (Enterprise)
SAML_DATABASE_URL="postgresql://user:pass@host:5432/calendso_saml"
```

## Migrations

Cal.com uses Prisma for database schema management and migrations.

### Initial Setup

For a new database, run migrations to create all tables:

```bash theme={null}
# Development (includes seeding)
yarn workspace @calcom/prisma db-migrate

# Production (no seeding)
yarn workspace @calcom/prisma db-deploy
```

### Migration Commands

<CodeGroup>
  ```bash Development theme={null}
  # Apply pending migrations and generate Prisma Client
  yarn workspace @calcom/prisma db-migrate

  # Create a new migration
  yarn workspace @calcom/prisma db-migrate-create "migration_name"

  # Reset database (⚠️ deletes all data)
  yarn workspace @calcom/prisma db-reset
  ```

  ```bash Production theme={null}
  # Apply migrations (safe for production)
  yarn workspace @calcom/prisma db-deploy

  # Check migration status
  yarn workspace @calcom/prisma db-migrate status

  # Generate Prisma Client after schema changes
  yarn prisma generate
  ```

  ```bash Docker theme={null}
  # Migrations run automatically in start.sh
  npx prisma migrate deploy --schema /calcom/packages/prisma/schema.prisma
  ```
</CodeGroup>

### Migration Best Practices

<Warning>
  * Always backup your database before running migrations in production
  * Test migrations in staging environment first
  * Use `db-deploy` (not `db-migrate`) in production
  * Never run `db-reset` in production (destroys all data)
</Warning>

<Note>
  The Docker `start.sh` script automatically runs migrations on container startup:

  ```bash theme={null}
  npx prisma migrate deploy --schema /calcom/packages/prisma/schema.prisma
  ```
</Note>

### Schema Location

The Prisma schema is located at:

```
packages/prisma/schema.prisma
```

### Migration Directory

Migration files are stored in:

```
packages/prisma/migrations/
```

## Connection Pooling

### Why Use Connection Pooling?

Connection poolers like PgBouncer help:

* Reduce database connection overhead
* Handle high-concurrency workloads
* Manage connection limits efficiently
* Improve application performance

### PgBouncer Setup

#### Using Docker

```yaml theme={null}
services:
  pgbouncer:
    image: pgbouncer/pgbouncer:latest
    environment:
      - DATABASES_HOST=database
      - DATABASES_PORT=5432
      - DATABASES_USER=calcom_user
      - DATABASES_PASSWORD=password
      - DATABASES_DBNAME=calendso
      - PGBOUNCER_POOL_MODE=transaction
      - PGBOUNCER_MAX_CLIENT_CONN=1000
      - PGBOUNCER_DEFAULT_POOL_SIZE=25
    ports:
      - "6432:6432"
    depends_on:
      - database
```

#### Configuration

Update your `.env` to use PgBouncer:

```bash theme={null}
# Application connects through PgBouncer
DATABASE_URL="postgresql://calcom_user:password@pgbouncer:6432/calendso?pgbouncer=true"

# Migrations bypass PgBouncer (direct connection required)
DATABASE_DIRECT_URL="postgresql://calcom_user:password@database:5432/calendso"
```

<Note>
  `DATABASE_DIRECT_URL` must point to the actual database (not pooler) for migrations to work correctly.
</Note>

### Managed Pooling Services

Many providers offer built-in connection pooling:

#### Supabase

```bash theme={null}
# Pooled connection (port 6543)
DATABASE_URL="postgresql://postgres:pass@db.xxx.supabase.co:6543/postgres"

# Direct connection (port 5432)
DATABASE_DIRECT_URL="postgresql://postgres:pass@db.xxx.supabase.co:5432/postgres"
```

#### Railway

Railway automatically provides both pooled and direct connections.

#### Neon

```bash theme={null}
# Pooled endpoint
DATABASE_URL="postgresql://user:pass@ep-xxx.pooler.neon.tech/db?sslmode=require"

# Direct endpoint
DATABASE_DIRECT_URL="postgresql://user:pass@ep-xxx.neon.tech/db?sslmode=require"
```

## Managed Database Providers

### Railway

1. Create a new PostgreSQL database in Railway
2. Copy the connection string from Railway dashboard
3. Add to `.env`:

```bash theme={null}
DATABASE_URL="postgresql://postgres:password@containers-xx.railway.app:5432/railway"
DATABASE_DIRECT_URL="postgresql://postgres:password@containers-xx.railway.app:5432/railway"
```

[Railway PostgreSQL Guide](https://docs.railway.app/guides/postgresql)

### Render

1. Create a new PostgreSQL database in Render
2. Get external connection string
3. Configure:

```bash theme={null}
DATABASE_URL="postgresql://user:pass@dpg-xxx.render.com/db_xxx"
DATABASE_DIRECT_URL="postgresql://user:pass@dpg-xxx.render.com/db_xxx"
```

[Render PostgreSQL Docs](https://render.com/docs/databases)

### Supabase

1. Create a new Supabase project
2. Get connection strings from Settings > Database
3. Use pooled connection for app, direct for migrations:

```bash theme={null}
DATABASE_URL="postgresql://postgres:pass@db.xxx.supabase.co:6543/postgres?pgbouncer=true"
DATABASE_DIRECT_URL="postgresql://postgres:pass@db.xxx.supabase.co:5432/postgres"
```

### Neon

1. Create a Neon project
2. Get pooled and direct connection strings
3. Configure:

```bash theme={null}
DATABASE_URL="postgresql://user:pass@ep-xxx.pooler.neon.tech/neondb"
DATABASE_DIRECT_URL="postgresql://user:pass@ep-xxx.neon.tech/neondb"
```

[Neon Quickstart](https://neon.tech/docs/get-started-with-neon/quickstart)

### Amazon RDS

1. Create RDS PostgreSQL instance
2. Configure security groups for access
3. Get endpoint from RDS console:

```bash theme={null}
DATABASE_URL="postgresql://postgres:pass@mydb.xxx.rds.amazonaws.com:5432/calendso"
DATABASE_DIRECT_URL="postgresql://postgres:pass@mydb.xxx.rds.amazonaws.com:5432/calendso"
```

## SSL Configuration

### Requiring SSL

```bash theme={null}
DATABASE_URL="postgresql://user:pass@host:5432/db?sslmode=require"
```

### Self-Signed Certificates

For platforms like Heroku with self-signed certs:

```bash theme={null}
PGSSLMODE="no-verify"
DATABASE_URL="postgresql://user:pass@host:5432/db"
```

<Warning>
  Only use `sslmode=no-verify` when you trust the network and database provider.
</Warning>

### SSL Modes

* `disable`: No SSL
* `prefer`: Try SSL, fallback to non-SSL
* `require`: Require SSL, but don't verify certificates
* `verify-ca`: Require SSL and verify certificate
* `verify-full`: Require SSL, verify certificate and hostname

## Database Optimization

### Indexes

Cal.com's schema includes optimized indexes. Monitor slow queries:

```sql theme={null}
-- Enable query logging
ALTER DATABASE calendso SET log_min_duration_statement = 1000;

-- Find slow queries
SELECT query, calls, total_time, mean_time
FROM pg_stat_statements
ORDER BY mean_time DESC
LIMIT 10;
```

### Connection Limits

Adjust PostgreSQL connection limits in `postgresql.conf`:

```conf theme={null}
max_connections = 100
shared_buffers = 256MB
effective_cache_size = 1GB
work_mem = 4MB
maintenance_work_mem = 64MB
```

### Vacuum and Analyze

Enable auto-vacuum for maintenance:

```sql theme={null}
-- Check auto-vacuum settings
SHOW autovacuum;

-- Manual vacuum analyze
VACUUM ANALYZE;
```

## Backup Strategies

### pg\_dump Backup

```bash theme={null}
# Backup database
pg_dump -h localhost -U calcom_user -d calendso -F c -f backup_$(date +%Y%m%d).dump

# Restore database
pg_restore -h localhost -U calcom_user -d calendso -c backup_20260304.dump
```

### Continuous Archiving (WAL)

For point-in-time recovery, enable WAL archiving:

```conf theme={null}
wal_level = replica
archive_mode = on
archive_command = 'cp %p /path/to/archive/%f'
```

### Managed Backups

Most managed providers offer automatic backups:

* **Supabase**: Daily backups with point-in-time recovery
* **Railway**: Automatic daily backups
* **Render**: Daily backups included
* **RDS**: Automated backups with configurable retention

## Database Management Tools

### Prisma Studio

Visual database browser included with Cal.com:

```bash theme={null}
# Start Prisma Studio
yarn db-studio

# Access at http://localhost:5555
```

<Note>
  Prisma Studio is available in the Docker Compose setup at port 5555. Remove this service in production.
</Note>

### pgAdmin

Full-featured PostgreSQL management:

```bash theme={null}
docker run -p 5050:80 \
  -e PGADMIN_DEFAULT_EMAIL=admin@example.com \
  -e PGADMIN_DEFAULT_PASSWORD=admin \
  dpage/pgadmin4
```

### psql CLI

Connect directly to your database:

```bash theme={null}
# Local connection
psql -U calcom_user -d calendso

# Remote connection
psql "postgresql://user:pass@host:5432/db?sslmode=require"

# Common commands
\dt          # List tables
\d+ table    # Describe table
\l           # List databases
\conninfo    # Connection info
```

## Docker Compose Database

The default `docker-compose.yml` includes PostgreSQL:

```yaml theme={null}
services:
  database:
    container_name: database
    image: postgres
    restart: always
    volumes:
      - database-data:/var/lib/postgresql
    environment:
      - POSTGRES_USER=unicorn_user
      - POSTGRES_PASSWORD=magical_password
      - POSTGRES_DB=calendso
    networks:
      - stack

volumes:
  database-data:
```

<Warning>
  Change default credentials in production:

  ```yaml theme={null}
  environment:
    - POSTGRES_USER=calcom_prod
    - POSTGRES_PASSWORD=${DB_PASSWORD}  # Use env var
    - POSTGRES_DB=calendso
  ```
</Warning>

## Separate Databases

### Insights Database

For analytics workload isolation:

```bash theme={null}
INSIGHTS_DATABASE_URL="postgresql://user:pass@analytics-db:5432/insights"
```

### SAML Database (Enterprise)

For SAML SSO data:

```bash theme={null}
SAML_DATABASE_URL="postgresql://user:pass@host:5432/calendso_saml"
```

Create the SAML database:

```sql theme={null}
CREATE DATABASE calendso_saml;
GRANT ALL PRIVILEGES ON DATABASE calendso_saml TO calcom_user;
```

## Troubleshooting

### Connection Refused

```
Error: connect ECONNREFUSED 127.0.0.1:5432
```

**Solutions:**

* Verify PostgreSQL is running: `sudo systemctl status postgresql`
* Check port: `sudo netstat -plnt | grep 5432`
* Review `pg_hba.conf` for connection permissions

### Too Many Connections

```
Error: sorry, too many clients already
```

**Solutions:**

* Implement connection pooling (PgBouncer)
* Increase `max_connections` in PostgreSQL
* Check for connection leaks in application

### Migration Failures

```
Error: Migration failed to apply
```

**Solutions:**

* Use `DATABASE_DIRECT_URL` (not pooled connection)
* Check database permissions
* Review migration logs: `yarn workspace @calcom/prisma db-migrate status`
* Manually resolve conflicts in failed migrations

### Permission Denied

```
Error: permission denied for schema public
```

**Solution (PostgreSQL 15+):**

```sql theme={null}
\c calendso
GRANT ALL ON SCHEMA public TO calcom_user;
GRANT ALL ON ALL TABLES IN SCHEMA public TO calcom_user;
GRANT ALL ON ALL SEQUENCES IN SCHEMA public TO calcom_user;
```

### Authentication Failed

```
Error: password authentication failed for user "calcom_user"
```

**Solutions:**

* Verify credentials in `DATABASE_URL`
* URL-encode special characters in password
* Check `pg_hba.conf` authentication method (md5/scram-sha-256)

## Performance Monitoring

### Query Statistics

Enable `pg_stat_statements` extension:

```sql theme={null}
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;

-- View slow queries
SELECT query, calls, total_time, mean_time, max_time
FROM pg_stat_statements
ORDER BY total_time DESC
LIMIT 20;
```

### Connection Monitoring

```sql theme={null}
-- Current connections
SELECT count(*) FROM pg_stat_activity;

-- Connections by state
SELECT state, count(*) 
FROM pg_stat_activity 
GROUP BY state;

-- Long-running queries
SELECT pid, now() - query_start AS duration, query
FROM pg_stat_activity
WHERE state = 'active'
ORDER BY duration DESC;
```

## Production Checklist

* [ ] Use PostgreSQL 13 or higher
* [ ] Configure strong database credentials
* [ ] Enable SSL connections (`sslmode=require`)
* [ ] Set up connection pooling (PgBouncer or managed)
* [ ] Configure `DATABASE_URL` and `DATABASE_DIRECT_URL`
* [ ] Run migrations with `db-deploy`
* [ ] Enable automated backups
* [ ] Configure backup retention policy
* [ ] Set up monitoring and alerts
* [ ] Optimize PostgreSQL configuration for your workload
* [ ] Plan for database scaling (read replicas, etc.)
* [ ] Document recovery procedures
* [ ] Test backup restoration process

## Next Steps

* Review [Configuration](/self-hosting/configuration) for database-related environment variables
* See [Docker Setup](/self-hosting/docker) for containerized database deployment
* Check [Deployment](/self-hosting/deployment) for platform-specific database configurations
