# Deploy Cal.com with the calcom-scheduling template

Source: https://docs.quake.ai/resources/deployments/deploy-calcom-scheduling-template
Markdown: https://docs.quake.ai/resources/deployments/deploy-calcom-scheduling-template.md

---

# Deploy Cal.com with the calcom-scheduling template

Stand up [Cal.com](https://cal.com), an open-source scheduling and booking platform, on a single Quake AI instance using the [validated OpenTofu template](/docs/platform/validation#how-infrastructure-templates-are-checked) `calcom-scheduling`. You apply the template, serve it over HTTPS through Caddy, set the public app URL, build the app from source, sign up the first account, connect a calendar, set your availability, create an event type, share the resulting booking page, and confirm a test booking.

Cal.com keeps your scheduling data on infrastructure you own. You run it yourself; this is a self-hosted tool you operate, not a managed multi-tenant service.



Cal.com's published Docker Hub image compiles `NEXT_PUBLIC_WEBAPP_URL` into the Next.js bundle at build time, with no supported runtime override. This deployment clones Cal.com's source and builds the app locally once you know your domain, instead of pulling a prebuilt image. The build needs real CPU and disk and takes a while on first run; the default sizing (8 vCPU, 8 GiB RAM) covers it.



<Figure size="md" caption="What you'll build: a Cal.com host built from source on a single instance with a bundled PostgreSQL, served over HTTPS through a Caddy reverse proxy">

```d2
direction: right

user: Invitee {shape: person}
fip: Floating IP
instance: Ubuntu instance {
  caddy: Caddy\nreverse proxy
  calcom: Cal.com\napp (built from source)
  db: PostgreSQL
  caddy -> calcom: proxies 443 to 3000
  calcom -> db: users, event types, bookings
}

user -> fip: HTTPS
fip -> instance.caddy
```

</Figure>

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

## Prerequisites

You need:

- OpenTofu 1.6.0 or later (or Terraform 1.6.0 or later) installed locally.
- Your OpenStack credentials sourced into the shell (`source openrc.sh`). See [the OpenStack CLI guide](/docs/tools/openstack-cli).
- An SSH keypair that already exists in your project. Record its name for the `key_name` variable.
- A copy of the `calcom-scheduling` template directory from [the template reference page](/resources/iac-templates/calcom-scheduling).
- A domain you can point at the instance.

## Step 1: Apply the template

Copy the template's example variables file and set `key_name`:

```bash
cp terraform.tfvars.example terraform.tfvars
```

```hcl
key_name = "YOUR_KEY_NAME"
```

Initialize, preview, and apply:

```bash
tofu init
tofu plan
tofu apply
```

OpenTofu provisions a private network, a router, a security group, a block volume mounted at `/var/lib/docker`, an instance, and a floating IP. On first boot, cloud-init installs Docker Engine, clones Cal.com's source to `/opt/calcom/src`, generates `NEXTAUTH_SECRET`, `CALENDSO_ENCRYPTION_KEY`, and the PostgreSQL password into `/opt/calcom/.env`, and starts only `database`. The app image isn't built yet: `NEXT_PUBLIC_WEBAPP_URL` has to be correct before the build starts, since it's compiled into the app rather than read at startup.

Read the outputs and record `floating_ip`:

```bash
tofu output
```

## Step 2: Point a domain at the host and serve HTTPS with Caddy

1. Create a DNS **A record** for your domain (for example `cal.example.com`) pointing at `YOUR_FLOATING_IP`. Follow [How to point a domain at a Quake AI resource](/docs/network/how-to/point-domain-to-quake-ai). Wait until it resolves:

```bash
dig +short cal.example.com
```

2. SSH to the instance and create `/opt/calcom/Caddyfile`:

```text
cal.example.com {
  reverse_proxy 127.0.0.1:3000
}
```

3. Add Caddy to `/opt/calcom/docker-compose.yml`:

```yaml
services:
  caddy:
    image: caddy:2
    restart: unless-stopped
    network_mode: host
    volumes:
      - /opt/calcom/Caddyfile:/etc/caddy/Caddyfile
      - caddy_data:/data
volumes:
  caddy_data:
```

For background on certificates, see [How to issue and auto-renew a TLS certificate with Let's Encrypt](/docs/network/how-to/lets-encrypt-certificate).

## Step 3: Set the app URL and build Cal.com

Edit `/opt/calcom/.env`, uncomment, and set:

```text
NEXT_PUBLIC_WEBAPP_URL=https://cal.example.com
NEXTAUTH_URL=https://cal.example.com
```

Build the app image, then start the full stack:

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

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

## Step 4: Sign up the first account

Open `https://cal.example.com`. The setup wizard walks you through creating the first account on first visit.

## Step 5: Connect a calendar and set your availability

1. From the dashboard, go to **Settings** > **My Account** > **Calendars**, and connect a calendar provider (for example Google Calendar or a CalDAV-compatible calendar).
2. Go to **Availability**, and adjust your working hours and time zone, or accept the default schedule.

## Step 6: Create an event type and share it

1. Go to **Event Types** > **New**, name it (for example `30 Minute Meeting`), and set its duration.
2. Save it, then copy the booking link Cal.com generates for that event type.
3. Open the link in a private browser window (as an invitee would) and book a slot to confirm the flow works end to end.

## What you built

- **Applied the `calcom-scheduling` template** to provision a network, security group, data volume, instance, and floating IP, with PostgreSQL started automatically by cloud-init
- **Served Cal.com over HTTPS** by pointing a domain at the floating IP and routing it through a Caddy reverse proxy
- **Built the app from source** with the correct public URL compiled in, then started it
- **Signed up the first account**
- **Connected a calendar and set your availability**
- **Created an event type and confirmed a test booking**

## Scope of this deployment

This template runs a single-VM Cal.com host, not a managed multi-tenant scheduling service. The instance is CPU-only and runs in one region, and it bundles PostgreSQL as a container on the same host. You operate the instance, Docker, the build, Cal.com, the database, and the data volume yourself: back them up, patch them, and rebuild when you upgrade to a newer release.

## Next steps

- [Cal.com scheduling template](/resources/iac-templates/calcom-scheduling): the template reference, parameters, and resource map
- [Self-managed PostgreSQL template](/resources/iac-templates/self-managed-postgres): the database to point at when you outgrow the bundled one
- [Security hardening checklist](/docs/security/hardening-checklist): tighten SSH access and exposure before you connect production traffic

## Clean up

When you no longer need the deployment, destroy everything the template created:

```bash
tofu destroy
```

Then remove the DNS A record you created in step 2. Because Cal.com, its build cache, and its database all live on the instance and its attached volume, `tofu destroy` removes them along with the infrastructure.
