Skip to main content
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

# 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

# Using Homebrew
brew install postgresql@15
brew services start postgresql@15

Windows

Download from postgresql.org and follow the installer.

Create Database

# 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:
# Local database
DATABASE_URL="postgresql://calcom_user:password@localhost:5432/calendso"

# Remote with SSL
DATABASE_URL="postgresql://user:[email protected]:5432/calendso?sslmode=require"

# With connection pooling
DATABASE_URL="postgresql://user:[email protected]: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:
# 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:
# Development (includes seeding)
yarn workspace @calcom/prisma db-migrate

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

Migration Commands

# 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

Migration Best Practices

  • 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)
The Docker start.sh script automatically runs migrations on container startup:
npx prisma migrate deploy --schema /calcom/packages/prisma/schema.prisma

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

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:
# 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"
DATABASE_DIRECT_URL must point to the actual database (not pooler) for migrations to work correctly.

Managed Pooling Services

Many providers offer built-in connection pooling:

Supabase

# Pooled connection (port 6543)
DATABASE_URL="postgresql://postgres:[email protected]:6543/postgres"

# Direct connection (port 5432)
DATABASE_DIRECT_URL="postgresql://postgres:[email protected]:5432/postgres"

Railway

Railway automatically provides both pooled and direct connections.

Neon

# Pooled endpoint
DATABASE_URL="postgresql://user:[email protected]/db?sslmode=require"

# Direct endpoint
DATABASE_DIRECT_URL="postgresql://user:[email protected]/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:
DATABASE_URL="postgresql://postgres:[email protected]:5432/railway"
DATABASE_DIRECT_URL="postgresql://postgres:[email protected]:5432/railway"
Railway PostgreSQL Guide

Render

  1. Create a new PostgreSQL database in Render
  2. Get external connection string
  3. Configure:
DATABASE_URL="postgresql://user:[email protected]/db_xxx"
DATABASE_DIRECT_URL="postgresql://user:[email protected]/db_xxx"
Render PostgreSQL Docs

Supabase

  1. Create a new Supabase project
  2. Get connection strings from Settings > Database
  3. Use pooled connection for app, direct for migrations:
DATABASE_URL="postgresql://postgres:[email protected]:6543/postgres?pgbouncer=true"
DATABASE_DIRECT_URL="postgresql://postgres:[email protected]:5432/postgres"

Neon

  1. Create a Neon project
  2. Get pooled and direct connection strings
  3. Configure:
DATABASE_URL="postgresql://user:[email protected]/neondb"
DATABASE_DIRECT_URL="postgresql://user:[email protected]/neondb"
Neon Quickstart

Amazon RDS

  1. Create RDS PostgreSQL instance
  2. Configure security groups for access
  3. Get endpoint from RDS console:
DATABASE_URL="postgresql://postgres:[email protected]:5432/calendso"
DATABASE_DIRECT_URL="postgresql://postgres:[email protected]:5432/calendso"

SSL Configuration

Requiring SSL

DATABASE_URL="postgresql://user:pass@host:5432/db?sslmode=require"

Self-Signed Certificates

For platforms like Heroku with self-signed certs:
PGSSLMODE="no-verify"
DATABASE_URL="postgresql://user:pass@host:5432/db"
Only use sslmode=no-verify when you trust the network and database provider.

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:
-- 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:
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:
-- Check auto-vacuum settings
SHOW autovacuum;

-- Manual vacuum analyze
VACUUM ANALYZE;

Backup Strategies

pg_dump Backup

# 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:
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:
# Start Prisma Studio
yarn db-studio

# Access at http://localhost:5555
Prisma Studio is available in the Docker Compose setup at port 5555. Remove this service in production.

pgAdmin

Full-featured PostgreSQL management:
docker run -p 5050:80 \
  -e [email protected] \
  -e PGADMIN_DEFAULT_PASSWORD=admin \
  dpage/pgadmin4

psql CLI

Connect directly to your database:
# 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:
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:
Change default credentials in production:
environment:
  - POSTGRES_USER=calcom_prod
  - POSTGRES_PASSWORD=${DB_PASSWORD}  # Use env var
  - POSTGRES_DB=calendso

Separate Databases

Insights Database

For analytics workload isolation:
INSIGHTS_DATABASE_URL="postgresql://user:pass@analytics-db:5432/insights"

SAML Database (Enterprise)

For SAML SSO data:
SAML_DATABASE_URL="postgresql://user:pass@host:5432/calendso_saml"
Create the SAML database:
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+):
\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:
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

-- 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 for database-related environment variables
  • See Docker Setup for containerized database deployment
  • Check Deployment for platform-specific database configurations

Build docs developers (and LLMs) love