Meeting scheduling
This summary page explains what the meeting scheduling stack is, how the series fits together, and how to get started.
Meeting scheduling series
- Meeting scheduling - You are here
- Architecture
- Manifests
- Flux integration
- 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
| Component | Version | Purpose |
|---|---|---|
| Cal.com | v6.1.3 | Scheduling engine (patched for CVE-2026-23478, CVE-2025-66489) |
| PostgreSQL | 16.4-alpine | Data storage |
| email-relay | - | Shared SMTP relay with MX validation (see Email relay) |
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