<!-- OSSAlt AI-readable guide source -->
<!-- Canonical: https://ossalt.com/guides/self-hosting-guide-calcom-2026 -->
<!-- Raw Markdown: https://ossalt.com/guides/self-hosting-guide-calcom-2026/raw.md -->
<!-- Source path: content/guides/self-hosting-guide-calcom-2026.mdx -->

---
og_image: "/images/guides/self-hosting-guide-calcom-2026.webp"
title: "Self-Hosting Cal.com: Open Source Scheduling Tool 2026"
description: "Deploy Cal.com on your own VPS with Docker — calendar integration, booking pages, team scheduling, and production tips for self-hosted scheduling in 2026."
date: "2026-03-08"
author: "OSSAlt Team"
tags: ["calcom", "scheduling", "self-hosting", "docker", "guide"]
tier: 1
---

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.

## Why Self-Host Cal.com

Calendly's pricing creates a frustrating ceiling for growing teams. The Standard plan at $10/user/month covers basic booking, but team scheduling requires the Teams plan at $16/user/month. For a 20-person team, that's $3,840/year just for scheduling links. Add required features like Salesforce integration or routing forms, and you're looking at custom enterprise pricing.

Self-hosting Cal.com on a €4.50/month Hetzner server gives you unlimited users, unlimited event types, team round-robin scheduling, and collective booking for approximately $54/year. No seat-based pricing, no feature gating.

**Booking data privacy.** Every time someone books a call with you through Calendly, their name, email, and meeting preferences go to Calendly's servers. For sales teams tracking prospect engagement, healthcare providers with HIPAA concerns, or anyone with client confidentiality obligations, self-hosting keeps that relationship data on your own infrastructure.

**White-label branding.** Self-hosted Cal.com can be deployed on your own domain with your own branding — `cal.yourcompany.com` rather than `calendly.com/yourname`. For customer-facing scheduling, this presents a more professional and consistent brand experience without paying for Calendly's premium branding removal.

**Custom booking flows.** Cal.com's open source nature means you can modify the booking confirmation flow, add custom questions, integrate with internal CRM systems via webhooks, or build completely custom booking experiences using Cal.com's embed API.

**When NOT to self-host Cal.com:** Cal.com is a complex Next.js application with a large dependency surface and multiple configuration requirements (OAuth credentials for each calendar provider). If you're an individual who just needs basic scheduling links, Calendly's free tier covers simple use cases without server management. Also, Cal.com's Google and Microsoft calendar OAuth setup requires creating Google Cloud Console and Azure applications — this is a non-trivial prerequisite that trips up some self-hosters.

## Prerequisites

Cal.com has more setup complexity than simpler self-hosted tools because it integrates directly with external calendar services via OAuth. Plan time for the calendar integration setup steps. Choosing a reliable [VPS provider for self-hosting](/guides/self-hosting-vps-comparison-2026) matters since scheduling reliability directly affects your ability to take meetings.

**Server specs:** 2 GB RAM minimum is accurate for a small team (under 20 users). For a larger organization with high booking volume, 4 GB RAM provides comfortable headroom for the Next.js server-side rendering and database queries. The PostgreSQL database is the primary RAM consumer beyond the Node.js process.

**Two secrets to generate before starting:** NEXTAUTH_SECRET (used for session encryption) and CALENDSO_ENCRYPTION_KEY (used for calendar credential encryption). Both must be generated before first start and must never change after initial setup — changing them invalidates all stored calendar credentials and active sessions.

**Google OAuth setup:** To enable Google Calendar sync (the most requested integration), you need to create a Google Cloud Console project, enable the Calendar API and People API, configure the OAuth consent screen, and create OAuth 2.0 credentials with the correct redirect URI. Budget 30-45 minutes for this setup. The credentials go in `GOOGLE_API_CREDENTIALS` as a JSON string.

**SMTP configuration:** Booking confirmations, reminder emails, and cancellation notices all go through SMTP. Configure a transactional email service (Resend, Postmark, or Mailgun) before inviting users — their account verification emails depend on it.

**Operating system:** Ubuntu 22.04 LTS. Cal.com's Docker image is maintained by the Cal.com team and targets standard Ubuntu/Debian environments.

**Skills required:** Comfortable with multi-step environment variable configuration, OAuth application setup in external developer consoles (Google Cloud, Azure), and basic Docker Compose operations.

## 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

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

# Copy environment
cp .env.example .env
```

## Step 2: Configure Environment

Edit `.env`:
```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:
```bash
openssl rand -hex 32  # NEXTAUTH_SECRET
openssl rand -hex 16  # CALENDSO_ENCRYPTION_KEY (must be 32 chars)
```

## Step 3: Docker Compose Setup

```yaml
# 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

```bash
docker compose up -d
```

## Step 5: Reverse Proxy (Caddy)

```
# /etc/caddy/Caddyfile
cal.yourdomain.com {
    reverse_proxy localhost:3000
}
```

```bash
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](https://console.cloud.google.com)
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](https://portal.azure.com)
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 Type | Duration | Use Case |
|-----------|----------|----------|
| Quick Chat | 15 min | Initial calls, quick questions |
| Discovery Call | 30 min | Sales conversations |
| Team Meeting | 30 min | Internal sync |
| Deep Dive | 60 min | Technical discussions |
| Pair Programming | 90 min | Collaborative 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. **Settings** → **Teams** → 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

```html
<!-- 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:**
```bash
# Database backup (daily cron)
docker exec calcom-db pg_dump -U calcom calcom > /backups/calcom-$(date +%Y%m%d).sql
```

Set up offsite backups with [automated server backups using restic](/guides/automated-server-backups-restic-rclone-2026). The PostgreSQL database contains all your users' booking preferences, calendar credentials, and event type configurations — losing this data means your users need to reconnect their calendars from scratch.

**Updates:**
```bash
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

| Users | RAM | CPU | Disk |
|-------|-----|-----|------|
| 1-10 | 2 GB | 2 cores | 10 GB |
| 10-50 | 4 GB | 2 cores | 20 GB |
| 50+ | 8 GB | 4 cores | 30 GB |

## VPS Recommendations

| Provider | Spec | Price |
|----------|------|-------|
| Hetzner | 2 vCPU, 4 GB RAM | €4.50/month |
| DigitalOcean | 2 vCPU, 2 GB RAM | $12/month |
| Linode | 1 vCPU, 2 GB RAM | $12/month |

## Production Security Hardening

Cal.com stores OAuth tokens for your users' Google and Microsoft calendars — this is sensitive credential data that grants read/write access to people's schedules. The [self-hosting security checklist](/guides/self-hosting-security-checklist-2026) applies in full; here are Cal.com-specific priorities.

**UFW firewall:**

```bash
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw deny 3000/tcp   # Block direct Cal.com access
sudo ufw enable
```

**Fail2ban for SSH:**

```bash
sudo apt install fail2ban -y
```

`/etc/fail2ban/jail.local`:

```ini
[sshd]
enabled = true
maxretry = 5
bantime = 3600
findtime = 600
```

**Protect the .env file:** The `.env` contains NEXTAUTH_SECRET, CALENDSO_ENCRYPTION_KEY, and OAuth credentials. These are the most sensitive values in the deployment.

```bash
chmod 600 .env
echo ".env" >> .gitignore
```

**Never change the encryption key after first run.** The CALENDSO_ENCRYPTION_KEY encrypts stored calendar OAuth tokens. If you rotate this key, every user's calendar connection becomes invalid and must be reconnected manually.

**Disable SSH password auth:**

```
PasswordAuthentication no
PermitRootLogin no
```

Restart: `sudo systemctl restart sshd`

**Automatic security updates:**

```bash
sudo apt install unattended-upgrades -y
sudo dpkg-reconfigure --priority=low unattended-upgrades
```

**OAuth redirect URI security:** In your Google Cloud Console and Azure Portal, only register the exact redirect URIs your Cal.com installation uses. Do not use wildcard URIs — this prevents an attacker from redirecting OAuth flows to their own server if they find an open redirect in your application.

## Troubleshooting Common Issues

**Google Calendar not showing availability**

The most common cause is incomplete OAuth scope configuration. Your Google Cloud Console OAuth application needs both `calendar.readonly` (to check availability) and `calendar.events` (to create booking events). Verify the scopes on your OAuth consent screen. Also check that the redirect URI `https://cal.yourdomain.com/api/integrations/googlecalendar/callback` is exactly listed in your Google Cloud credentials — extra slashes or wrong protocol cause OAuth failures.

**Booking confirmations not sending**

Cal.com sends emails through the `EMAIL_SERVER_*` variables via NextAuth's email provider. Test SMTP connectivity: `docker exec calcom node -e "require('nodemailer').createTransport({host:'smtp.resend.com',port:587,auth:{user:'resend',pass:'YOUR_KEY'}}).verify(console.log)"`. If verification fails, check that port 587 isn't blocked by your VPS provider and that your API key is correct.

**"Invalid encryption key" errors after update**

If you see credential decryption errors after pulling a new Cal.com image, a database migration may have changed the encryption format. Check `docker logs calcom` for the specific error. In most cases, the CALENDSO_ENCRYPTION_KEY in your `.env` has changed accidentally. Restore the original key from your backup — never generate a new one to "fix" this error, as that permanently corrupts all stored credentials.

**Booking page shows wrong timezone**

Cal.com respects the `TZ` environment variable in the container and the user's browser timezone for display. If availability slots appear wrong, verify the `NEXT_PUBLIC_WEBAPP_URL` is correct (used for time calculations) and that your server's system timezone is set correctly: `timedatectl set-timezone America/New_York`.

**Round-robin scheduling not distributing evenly**

Round-robin requires all team members to have connected calendars and set their availability windows. If one team member's calendar is disconnected, Cal.com may always assign to others. Check each team member's calendar connection status in their profile settings. Also verify that all team members have overlapping availability windows for the event type's duration.

## Ongoing Maintenance and Operations

Cal.com is a scheduling-critical application — booking links in your email signature, on your website, and in marketing materials all depend on it being available. Here's what long-term operation looks like.

**Calendar sync reliability.** The most common support request for self-hosted Cal.com is "my availability shows wrong." This happens when a user's Google or Microsoft OAuth token expires and calendar sync silently stops. OAuth tokens expire after a period of inactivity or when the user's password changes. Build a process to periodically verify calendar connections: in your Cal.com admin, check **Settings** → **Connected Calendars** monthly and notify users whose connections appear disconnected.

**Booking link updates.** When team members change their availability, leave the company, or join a new team, booking links need to be updated. Establish a process: new hires configure their Cal.com profile on day one, departing team members' profiles are deactivated before their last day. Orphaned booking links that still appear active but lead to 404 pages or "no availability" screens create a poor experience for prospective customers.

**Event type maintenance.** As your product and team evolve, event types need updating. Old event types (e.g., a discontinued product demo) should be archived rather than deleted — deletion breaks existing booking links. Cal.com supports disabling event types so they're hidden from your booking page but existing links redirect gracefully.

**Webhook integrations for CRM sync.** Cal.com fires webhooks on booking created, cancelled, and rescheduled events. Connect these to your CRM (via Zapier, n8n, or a custom webhook consumer) to automatically create contact records and activities when someone books a call. This is one of the highest-value integrations for sales teams — booked demos automatically populate the CRM without manual data entry.

**Database backup before updates.** Cal.com's database contains user profiles, event types, booking history, and calendar credentials. The encrypted calendar credentials are particularly important — losing them means every user must reconnect their calendars. Back up before every update and verify restores work.

**Team scheduling optimization.** For teams using round-robin scheduling, periodically review distribution metrics to ensure bookings are distributed as expected. If one team member consistently receives disproportionately fewer bookings, check their availability windows — they may have inadvertently restricted their availability or have calendar conflicts blocking slots.

**Embedding updates.** If you update your Cal.com URL or event types, update all embedded booking widgets on your website. Embedded iframes using hardcoded event type URLs continue pointing to old configuration until updated. Audit your website for embedded Cal.com widgets quarterly and verify they still point to active event types.

**Integrating bookings with your CRM and sales stack.** Cal.com fires webhook events for every booking action — created, rescheduled, cancelled, and completed. Connect these events to your CRM via a simple webhook consumer or n8n workflow to automatically create contact records when someone books a call, log call activities against existing contacts, and trigger automated follow-up sequences after meetings end. This integration transforms Cal.com from a standalone scheduling tool into a full-funnel sales process component. Configure the webhook in **Settings** → **Developer** → **Webhooks**, and map the payload fields (name, email, event type, start time) to your CRM's API. The self-hosted nature means you control exactly what data is forwarded and can enrich it with internal context before sending.

---

*Compare scheduling tools on [OSSAlt](https://www.ossalt.com) — features, integrations, and pricing side by side.*
