Skip to main content

Meeting scheduling

info

This summary page explains what the meeting scheduling stack is, how the series fits together, and how to get started.

Meeting scheduling series​

  1. Meeting scheduling - You are here
  2. Architecture
  3. Manifests
  4. Flux integration
  5. Operations

What this is​

A GitOps-managed meeting scheduling system using Cal.com for self-hosted appointment booking. It replaces SaaS scheduling tools with a solution you control, integrating with Google Calendar, Zoom, and email.

The problem I solved​

I was paying hundreds per year for a WordPress scheduling plugin. The features were limited, the UI was clunky, and my data lived on someone else's infrastructure.

Self-hosting Cal.com eliminates recurring SaaS fees (typically $500-1000/year for comparable features), gives full control over the booking experience, and keeps all appointment data in my own database.

Components​

ComponentVersionPurpose
Cal.comv6.1.3Scheduling engine (patched for CVE-2026-23478, CVE-2025-66489)
PostgreSQL16.4-alpineData storage
email-relay-Shared SMTP relay with MX validation (see Email relay)
note

Cal.com Docker self-hosting is community-maintained ("use at your own risk"). The official SaaS version receives updates first.

Architecture overview​

Traffic flows from Cloudflare Tunnel through your ingress controller to Cal.com. The application connects to external APIs for calendar sync, video conferencing, and email via the shared email-relay namespace (MX validation, logging via Mailpit, delivery via Microsoft Graph API).

Repository structure​

The stack spans two repositories following the standard GitOps pattern.

App repo​

Contains the Kubernetes manifests for Cal.com and PostgreSQL.

your-org/calcom/
├── .sops.yaml # age public key for SOPS encryption
└── k8s/prod/
├── kustomization.yaml
├── 10-secret-db.enc.yaml # SOPS encrypted PostgreSQL creds
├── 11-secret-calcom.enc.yaml # SOPS encrypted app secrets
├── 20-db-statefulset.yaml # PostgreSQL with NFS patterns
├── 30-calcom-deployment.yaml # Cal.com with init containers
├── 40-ingress.yaml # Ingress for cal.example.com
├── 50-secret-cfapi-token.enc.yaml # Cloudflare API token
├── 60-originissuer.yaml # Cloudflare Origin CA issuer
└── 70-certificate.yaml # TLS certificate

Email relay is deployed separately in the email-relay namespace. See Email relay for details.

Flux config repo​

Contains the Flux objects that deploy the app repo with proper dependency ordering.

clusters/my-cluster/calcom/
├── kustomization.yaml
├── source.yaml # GitRepository
├── 00-kustomization-ns.yaml # Namespace (dependsOn: origin-ca-issuer)
├── 10-kustomization-app.yaml # App (dependsOn: calcom-ns)
└── ns/
├── kustomization.yaml
└── namespace.yaml # calcom namespace

Quick start​

After deploying via Flux:

# Check Flux Kustomizations
flux get kustomizations -n flux-system | grep calcom

# Check pods
kubectl get pods -n calcom

# View Cal.com logs
kubectl logs -n calcom -l app=calcom -f

# View migration logs (runs on startup)
kubectl logs -n calcom -l app=calcom -c migrate

# View email relay logs (shared namespace)
kubectl logs -n email-relay -l app=email-relay -c mx-validator -f
kubectl logs -n email-relay -l app=email-relay -c smtp2graph -f

# Force reconciliation
flux reconcile source git calcom-app -n flux-system
flux reconcile kustomization calcom-app -n flux-system --with-source

# Database access
kubectl exec -it -n calcom calcom-db-0 -- psql -U calcom -d calcom

What you will learn​

  • Architecture: how Cal.com connects to external services and the data flow through your cluster
  • Manifests: the app repo structure, PostgreSQL NFS patterns, and Cal.com deployment configuration
  • Flux integration: dependency control to ensure Origin CA issuer is ready before deployment
  • Operations: OAuth setup (Google Calendar, Zoom), email via Microsoft Graph API, security hardening, and first login