# Cal.com scheduling

Source: https://docs.quake.ai/resources/iac-templates/calcom-scheduling
Markdown: https://docs.quake.ai/resources/iac-templates/calcom-scheduling.md

---

# Cal.com scheduling

This pattern composes Compute, Network, and Block Storage into a self-hosted scheduling and booking platform, on infrastructure you control.

## What this template does

Provisions a single instance running [Cal.com](https://cal.com), an open-source scheduling and booking platform (a self-hosted alternative to Calendly). You configure availability, event types, and calendar sync, and share booking links, on infrastructure you own:

- Compute instance that clones Cal.com's source and builds the app image locally, alongside a bundled PostgreSQL (8 vCPU and 8 GiB RAM, sized for the Next.js build rather than the running app)
- Private network, subnet, router, port, and security group; a floating IP for public access
- A block volume mounted at `/var/lib/docker`, so the cloned source tree, the Next.js build cache, and the PostgreSQL data all live on a volume you can grow
- cloud-init installs Docker Engine, clones the pinned `v6.2.0` release tag, and brings up PostgreSQL; the app image is not built until you finish configuration

`NEXTAUTH_SECRET`, `CALENDSO_ENCRYPTION_KEY`, and the PostgreSQL password are generated on first boot and written to `/opt/calcom/.env`; no credential ships with this template.

## Why this template builds from source instead of pulling an image

Cal.com's published `calcom/cal.com` Docker Hub image compiles `NEXT_PUBLIC_WEBAPP_URL` into the Next.js bundle at build time. Cal.com's own documentation lists it as a build-time-only variable, alongside `NEXT_PUBLIC_LICENSE_CONSENT` and `NEXT_PUBLIC_TELEMETRY_KEY`: there is no supported runtime override, so pointing the prebuilt image at your own domain doesn't work. This template clones Cal.com's source instead and builds the app locally with your domain baked in correctly the first time.

That build needs real CPU and disk. The default `s1a.large` flavor (8 vCPU, 8 GiB RAM) and 30 GiB volume cover it; you can size the flavor down after the first successful build, and size it back up for any future rebuild.

## Parameters

| Parameter | Description | Default |
| --- | --- | --- |
| `key_name` | SSH keypair name (must already exist) | No default |
| `flavor_name` | Instance size (the Next.js build from source needs 8 vCPU / 8 GiB) | `s1a.large` |
| `image_name` | Operating system image | `Ubuntu-24.04` |
| `app_name` | Display name prefix for resources | `calcom` |
| `volume_size` | Block volume size in GiB, mounted at `/var/lib/docker` | `30` |
| `external_network` | External network for floating IP allocation | `PublicStatic` |
| `private_cidr` | CIDR for the private subnet | `10.56.0.0/24` |
| `app_allowed_cidr` | CIDR allowed to reach Cal.com on port 3000 | `10.56.0.0/24` |

## Finish setup after apply

cloud-init starts PostgreSQL and writes the generated secrets to `/opt/calcom/.env`. Complete the setup over SSH:

1. Point a domain's DNS A record at the floating IP and put a reverse proxy (Caddy or Nginx) in front for HTTPS on 443.
2. Edit `/opt/calcom/.env`: uncomment and set `NEXT_PUBLIC_WEBAPP_URL` and `NEXTAUTH_URL` to your public HTTPS address.
3. Build and start Cal.com:

```bash
cd /opt/calcom
sudo docker compose build calcom
sudo docker compose up -d calcom
```

The build compiles the entire Next.js monorepo, so expect it to take a while on first run. The container runs its database migrations automatically once it starts.

4. Open the site URL. The first visit walks you through creating the first admin account.

## Access and security

Cal.com listens on port 3000 over plain HTTP. The security group restricts 3000 to `app_allowed_cidr`, which defaults to the private network only. Because Cal.com needs a public URL for OAuth calendar-sync callbacks and booking-page links, the normal access path is a domain with HTTPS on 443 behind a reverse proxy. Ports 80 and 443 stay open for that proxy; they carry no traffic until you add one.

## When to use this pattern

Run scheduling and booking pages for a person or a team on a host you operate, rather than a managed multi-tenant service. This template bundles PostgreSQL as a container on the same host, which suits a single-team deployment. To run the database separately, point `DATABASE_URL` and `DATABASE_DIRECT_URL` in `/opt/calcom/.env` at a [self-managed PostgreSQL](/resources/iac-templates/self-managed-postgres) instance, remove the bundled `database` service from the compose file, and rebuild.

## Estimated cost

<PricingCompanion
  components={[
    { kind: "template", slug: "calcom-scheduling", required: true },
  ]}
/>

## Template source

<TemplateSource slug="calcom-scheduling" />

<TemplateResourceMap template="calcom-scheduling" format="opentofu" />

## Customize this pattern

- [Customize a template's image and flavor](/docs/automation/how-to/customize-template-image-flavor)
- [Add a block volume to a template](/docs/automation/how-to/add-volume-to-template)
- [Parameterize a template with a tfvars file](/docs/automation/how-to/parameterize-template-tfvars)

## See also

- [Self-managed PostgreSQL](/resources/iac-templates/self-managed-postgres)
- [Infisical secrets management](/resources/iac-templates/infisical-secrets)
