Self-Hosting Cal.com with Docker Compose
What Makes Cal.com Worth Self-Hosting?
Calendly charges $12–16/user/month for features Cal.com gives you for free. Cal.com is an open-source scheduling platform — booking pages, round-robin assignments, team scheduling, Google/Outlook calendar integration, payment collection, and webhook automations. Self-hosting means your scheduling data stays on your infrastructure with no per-seat licensing.
Cal.com is built on Next.js with Prisma ORM and PostgreSQL. The Docker image includes everything needed to run the full application.
Prerequisites
- A Linux server (Ubuntu 22.04+ recommended)
- Docker and Docker Compose installed (guide)
- 2 GB RAM minimum
- 20 GB free disk space
- A domain name (required for OAuth calendar integrations)
| Resource | Minimum | Recommended |
|---|---|---|
| RAM | 2 GB (total stack) | 4 GB |
| CPU | 1 core | 2 cores |
| Disk | 10 GB | 20 GB |
| App idle RAM | ~300 MB | ~400 MB |
Cal.com is lightweight compared to other scheduling tools. PostgreSQL is the biggest memory consumer.
Docker Compose Configuration
Create a project directory and docker-compose.yml:
services:
calcom:
image: calcom/cal.com:v6.2.0
container_name: calcom
depends_on:
calcom-db:
condition: service_healthy
calcom-redis:
condition: service_started
environment:
- DATABASE_URL=postgresql://calcom:${DB_PASSWORD}@calcom-db:5432/calendso
- REDIS_URL=redis://calcom-redis:6379
- NEXTAUTH_SECRET=${NEXTAUTH_SECRET}
- CALENDSO_ENCRYPTION_KEY=${ENCRYPTION_KEY}
- NEXTAUTH_URL=${BASE_URL}/api/auth
- NEXT_PUBLIC_WEBAPP_URL=${BASE_URL}
ports:
- "3000:3000"
restart: unless-stopped
calcom-db:
image: postgres:15
container_name: calcom-db
environment:
- POSTGRES_DB=calendso
- POSTGRES_USER=calcom
- POSTGRES_PASSWORD=${DB_PASSWORD}
volumes:
- calcom_db:/var/lib/postgresql/data
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U calcom -d calendso"]
interval: 10s
timeout: 5s
retries: 5
calcom-redis:
image: redis:7-alpine
container_name: calcom-redis
volumes:
- calcom_redis:/data
restart: unless-stopped
volumes:
calcom_db:
calcom_redis:
Create a .env file:
# Your public URL — used for OAuth callbacks and booking links
BASE_URL=https://cal.example.com
# Database password — change this
DB_PASSWORD=change_me_to_a_strong_password
# Authentication secret — generate with: openssl rand -base64 32
NEXTAUTH_SECRET=generate_a_random_string_here
# Encryption key for sensitive data — generate with: openssl rand -base64 32
ENCRYPTION_KEY=generate_another_random_string_here
Generate the secrets:
echo "NEXTAUTH_SECRET=$(openssl rand -base64 32)" >> .env
echo "ENCRYPTION_KEY=$(openssl rand -base64 32)" >> .env
Start the stack:
docker compose up -d
Initial Setup
- Open
http://your-server:3000(or your domain if reverse proxied) - Click Create Account and register the first user — this becomes the admin
- Complete the onboarding wizard: timezone, availability, booking page URL slug
- Your booking page is immediately live at
https://cal.example.com/your-username
Connecting Google Calendar
Cal.com syncs with Google Calendar to check availability and create events:
- Create a project in Google Cloud Console
- Enable the Google Calendar API
- Create OAuth 2.0 credentials with redirect URI:
https://cal.example.com/api/auth/callback/google - Add
GOOGLE_API_CREDENTIALSto your Cal.com environment with the client ID and secret
Connecting Outlook/Office 365
Similar process through Azure AD app registration. Redirect URI: https://cal.example.com/api/auth/callback/office365
Key Features
| Feature | Free (Self-Hosted) | Calendly Free | Calendly Pro ($12/mo) |
|---|---|---|---|
| Booking pages | Unlimited | 1 event type | Unlimited |
| Team scheduling | Yes | No | Yes |
| Round-robin | Yes | No | Yes |
| Calendar integrations | Google, Outlook, CalDAV | Google, Outlook | Google, Outlook |
| Custom branding | Full control | Calendly branding | Remove branding |
| Webhooks | Yes | No | Yes |
| Payment collection | Stripe, PayPal | No | Stripe |
| API access | Full API | No | Yes |
| Recurring events | Yes | No | Yes |
Reverse Proxy
Cal.com runs on port 3000. Put it behind a reverse proxy for SSL.
Caddy:
cal.example.com {
reverse_proxy calcom:3000
}
The NEXTAUTH_URL and NEXT_PUBLIC_WEBAPP_URL environment variables must match your public URL exactly, including the protocol (https://). Mismatches cause authentication loops.
See Reverse Proxy Setup for Nginx Proxy Manager and Traefik configurations.
Backup
Cal.com stores everything in PostgreSQL. Back up the database:
docker exec calcom-db pg_dump -U calcom calendso > calcom-backup.sql
Redis data is ephemeral cache — losing it doesn’t lose bookings.
For automated backup strategies, see Backup Strategy.
Troubleshooting
CLIENT_FETCH_ERROR on login
Symptom: Login page shows “CLIENT_FETCH_ERROR” or infinite redirect loop.
Fix: NEXTAUTH_URL must be reachable from inside the container. If you’re behind a reverse proxy, use the internal URL for NEXTAUTH_URL:
NEXTAUTH_URL=http://localhost:3000/api/auth
And set the public URL separately:
NEXT_PUBLIC_WEBAPP_URL=https://cal.example.com
Booking page shows 404
Symptom: https://cal.example.com/username returns 404.
Fix: The username must match exactly what was set during onboarding. Check in Settings → Profile. Also verify NEXT_PUBLIC_WEBAPP_URL matches your domain.
Calendar sync not working
Symptom: Google Calendar events don’t show as busy times.
Fix: OAuth credentials must have the correct redirect URI. Check Google Cloud Console — the redirect must be https://your-domain/api/auth/callback/google (HTTPS, exact domain match). Re-authorize the calendar connection after fixing.
ARM64 compatibility
Symptom: Container crashes on Raspberry Pi or ARM-based server.
Fix: Use the ARM-specific image tag:
image: calcom/cal.com:v6.2.0-arm
Verdict
Cal.com is the clear winner for self-hosted scheduling. It replaces Calendly with zero compromises on features — booking pages, team scheduling, calendar sync, payments, and webhooks are all included. The Docker setup is straightforward, resource usage is modest, and the community is active.
Use Cal.com if: You want a Calendly replacement without per-seat costs, need team scheduling or round-robin routing, or want full API access to your scheduling data.
Look elsewhere if: You only need simple appointment booking without calendar sync — Easy Appointments is lighter. Or if you need event ticketing rather than 1:1 scheduling, see Hi.Events or Alf.io.
Related
- Cal.com vs Calendly: Self-Hosted Scheduling Wins
- Cal.com vs Rallly: Which Scheduling Tool to Self-Host?
- Best Self-Hosted Booking & Scheduling Tools
- Self-Hosted Alternatives to Calendly
- How to Self-Host Radicale (CalDAV)
- Best Self-Hosted Calendar & Contacts
- Docker Compose Basics
- Reverse Proxy Setup
- Backup Strategy
Get self-hosting tips in your inbox
Get the Docker Compose configs, hardware picks, and setup shortcuts we don't put in articles. Weekly. No spam.
Comments