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.
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
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: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:
# 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
Development
Production
Docker
# 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: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
# 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
Create a new PostgreSQL database in Railway
Copy the connection string from Railway dashboard
Add to .env:
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
Render
Create a new PostgreSQL database in Render
Get external connection string
Configure:
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
Supabase
Create a new Supabase project
Get connection strings from Settings > Database
Use pooled connection for app, direct for migrations:
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
Create a Neon project
Get pooled and direct connection strings
Configure:
DATABASE_URL = "postgresql://user:pass@ep-xxx.pooler.neon.tech/neondb"
DATABASE_DIRECT_URL = "postgresql://user:pass@ep-xxx.neon.tech/neondb"
Neon Quickstart
Amazon RDS
Create RDS PostgreSQL instance
Configure security groups for access
Get endpoint from RDS console:
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
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
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 PGADMIN_DEFAULT_EMAIL=admin@example.com \
-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)
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
Next Steps
Review Configuration for database-related environment variables
See Docker Setup for containerized database deployment
Check Deployment for platform-specific database configurations