Skip to main content

Self-Hosting Guide: Deploy Cal.com for Scheduling

·OSSAlt Team
calcomschedulingself-hostingdockerguide

Self-Hosting Guide: Deploy Cal.com for Scheduling

Cal.com is the open source Calendly alternative. Self-hosting gives you unlimited booking pages, unlimited event types, and team scheduling — all without per-user pricing.

Requirements

  • VPS with 2 GB RAM minimum
  • Docker and Docker Compose
  • Domain name (e.g., cal.yourdomain.com)
  • 10+ GB disk
  • SMTP service for booking notifications

Step 1: Clone and Configure

git clone https://github.com/calcom/cal.com.git
cd cal.com

# Copy environment
cp .env.example .env

Step 2: Configure Environment

Edit .env:

# App
NEXT_PUBLIC_WEBAPP_URL=https://cal.yourdomain.com
NEXTAUTH_SECRET=your-random-secret-min-32-chars
CALENDSO_ENCRYPTION_KEY=your-random-encryption-key-32-chars

# Database
DATABASE_URL=postgresql://calcom:your-strong-password@db:5432/calcom
DATABASE_DIRECT_URL=postgresql://calcom:your-strong-password@db:5432/calcom

# Email
EMAIL_FROM=cal@yourdomain.com
EMAIL_SERVER_HOST=smtp.resend.com
EMAIL_SERVER_PORT=587
EMAIL_SERVER_USER=resend
EMAIL_SERVER_PASSWORD=re_your_api_key

# Calendar integrations (get from Google Cloud Console)
GOOGLE_API_CREDENTIALS={"client_id":"...","client_secret":"..."}

# Microsoft Calendar (get from Azure Portal)
# MS_GRAPH_CLIENT_ID=your-client-id
# MS_GRAPH_CLIENT_SECRET=your-client-secret

Generate secrets:

openssl rand -hex 32  # NEXTAUTH_SECRET
openssl rand -hex 16  # CALENDSO_ENCRYPTION_KEY (must be 32 chars)

Step 3: Docker Compose Setup

# docker-compose.yml
services:
  calcom:
    image: calcom/cal.com:latest
    container_name: calcom
    restart: unless-stopped
    ports:
      - "3000:3000"
    env_file: .env
    depends_on:
      - db

  db:
    image: postgres:16-alpine
    container_name: calcom-db
    restart: unless-stopped
    volumes:
      - postgres_data:/var/lib/postgresql/data
    environment:
      - POSTGRES_DB=calcom
      - POSTGRES_USER=calcom
      - POSTGRES_PASSWORD=your-strong-password

volumes:
  postgres_data:

Step 4: Start Cal.com

docker compose up -d

Step 5: Reverse Proxy (Caddy)

# /etc/caddy/Caddyfile
cal.yourdomain.com {
    reverse_proxy localhost:3000
}
sudo systemctl restart caddy

Step 6: Initial Setup

  1. Open https://cal.yourdomain.com
  2. Create your admin account
  3. Set your timezone and availability

Step 7: Connect Calendars

Google Calendar:

  1. Go to Google Cloud Console
  2. Create a project and enable Calendar API
  3. Create OAuth 2.0 credentials
  4. Add https://cal.yourdomain.com/api/integrations/googlecalendar/callback as redirect URI
  5. Add credentials to .env

Microsoft Outlook:

  1. Go to Azure Portal
  2. Register an application
  3. Add Calendar.ReadWrite permissions
  4. Add https://cal.yourdomain.com/api/integrations/office365calendar/callback as redirect URI

Apple Calendar:

  • Connect via CalDAV in Cal.com settings

Step 8: Create Event Types

Event TypeDurationUse Case
Quick Chat15 minInitial calls, quick questions
Discovery Call30 minSales conversations
Team Meeting30 minInternal sync
Deep Dive60 minTechnical discussions
Pair Programming90 minCollaborative coding

For each event type, configure:

  • Duration and buffer time
  • Availability windows (e.g., Mon-Fri 9am-5pm)
  • Minimum notice (e.g., 4 hours)
  • Questions for invitees
  • Confirmation email template
  • Meeting location (Zoom, Google Meet, phone)

Step 9: Team Scheduling (Optional)

Set up team features:

  1. SettingsTeams → create a team
  2. Invite team members
  3. Create team event types:
    • Round Robin — distributes bookings evenly across team
    • Collective — requires all team members to be available
    • Managed Event Types — admin controls event type settings for all members

Step 10: Embed on Your Website

<!-- Inline embed -->
<iframe
  src="https://cal.yourdomain.com/your-username/30min"
  width="100%"
  height="700"
  frameborder="0"
></iframe>

<!-- Or use Cal.com embed snippet -->
<script>
  (function (C, A, L) {
    let p = function (a, ar) { a.q.push(ar); };
    let d = C.document;
    C.Cal = C.Cal || function () { let cal = C.Cal; let ar = arguments;
      if (!cal.loaded) { cal.ns = {}; cal.q = cal.q || []; d.head.appendChild(d.createElement("script")).src = A;
        cal.loaded = true; }
      if (ar[0] === L) { const api = function () { p(api, arguments); };
        const namespace = ar[1]; api.q = api.q || [];
        typeof namespace === "string" ? (cal.ns[namespace] = api) && p(api, ar) : p(cal, ar);
        return; }
      p(cal, ar);
    };
  })(window, "https://cal.yourdomain.com/embed/embed.js", "init");
  Cal("init");
  Cal("ui", {"styles":{"branding":{"brandColor":"#000000"}}});
</script>

Production Hardening

Backups:

# Database backup (daily cron)
docker exec calcom-db pg_dump -U calcom calcom > /backups/calcom-$(date +%Y%m%d).sql

Updates:

docker compose pull
docker compose up -d

Monitoring:

  • Monitor port 3000 with Uptime Kuma
  • Set up alerts for failed email delivery
  • Monitor PostgreSQL disk usage

Resource Usage

UsersRAMCPUDisk
1-102 GB2 cores10 GB
10-504 GB2 cores20 GB
50+8 GB4 cores30 GB

VPS Recommendations

ProviderSpecPrice
Hetzner2 vCPU, 4 GB RAM€4.50/month
DigitalOcean2 vCPU, 2 GB RAM$12/month
Linode1 vCPU, 2 GB RAM$12/month

Compare scheduling tools on OSSAlt — features, integrations, and pricing side by side.