Back to Blog
Tutorial11 min read2026-06-29

How to Deploy Cal.com Scheduling App

Cal.com is the open-source Calendly alternative. This guide deploys the Next.js app with managed PostgreSQL, configures calendar integrations, and handles the env-heavy setup correctly.

Ajay Kumar
Ajay Kumar
Founder & DevOps, PandaStack

Cal.com is the leading open-source scheduling platform — a self-hostable Calendly alternative built on Next.js and Prisma. Self-hosting gives you data ownership, custom branding, and no per-seat fees. The catch is that Cal.com is famously environment-variable heavy and requires database migrations, so a careful setup pays off.

This guide deploys Cal.com with a managed PostgreSQL database.

What Cal.com needs

ComponentRole
Next.js appWeb UI + API
PostgreSQLBookings, users, event types, availability
Calendar integrationsGoogle/Office365 for two-way sync
Email (SMTP)Booking confirmations and notifications

Cal.com is primarily one web application plus a database. Most of the work is configuration.

Step 1: Provision managed PostgreSQL

On [PandaStack](https://dashboard.pandastack.io), create a managed PostgreSQL (14.x or 16.x). When linked, DATABASE_URL is injected — Cal.com (via Prisma) reads DATABASE_URL directly, so this maps cleanly with no manual string-building.

Step 2: Generate required secrets

Cal.com needs an encryption key and a NextAuth secret:

openssl rand -base64 32   # NEXTAUTH_SECRET
openssl rand -base64 24   # CALENDSO_ENCRYPTION_KEY (used to encrypt stored credentials)

CALENDSO_ENCRYPTION_KEY encrypts third-party calendar tokens at rest — keep it stable and secret, or stored integrations become unreadable.

Step 3: Assemble the core environment

Cal.com has many variables; here are the essentials:

NEXT_PUBLIC_WEBAPP_URL=https://cal.example.com
NEXTAUTH_URL=https://cal.example.com
NEXTAUTH_SECRET=<generated>
CALENDSO_ENCRYPTION_KEY=<generated>

DATABASE_URL=${DATABASE_URL}              # injected by linking managed PostgreSQL
DATABASE_DIRECT_URL=${DATABASE_URL}        # Prisma direct connection for migrations

# Email
EMAIL_FROM=notifications@example.com
EMAIL_SERVER_HOST=<smtp-host>
EMAIL_SERVER_PORT=587
EMAIL_SERVER_USER=<user>
EMAIL_SERVER_PASSWORD=<password>

The URL variables must all match your real HTTPS domain — mismatches break authentication and booking links.

Step 4: Run Prisma migrations

Cal.com's schema is managed by Prisma. Run migrations before first boot as a pre-deploy step using the Cal.com image:

npx prisma migrate deploy

This applies all pending migrations to your managed PostgreSQL. Run it on every deploy that ships schema changes.

Step 5: Deploy the container

Use the official self-hosting image (or build from the repo's Dockerfile):

FROM calcom/cal.com:latest
# Serves on port 3000

Pin a version in production.

  1. 1Push the repo (or reference the image) to GitHub.
  2. 2Create a container app on PandaStack.
  3. 3Link the managed PostgreSQL so DATABASE_URL is injected.
  4. 4Set all env vars (secrets as secrets).
  5. 5Set npx prisma migrate deploy as the pre-deploy hook.
  6. 6Expose port 3000.
  7. 7Add a custom domain matching your URL vars; SSL is automatic.

Build note: Cal.com bakes some NEXT_PUBLIC_* values at build time. If you build from source rather than the prebuilt image, ensure NEXT_PUBLIC_WEBAPP_URL is set during the build, not just at runtime.

Step 6: Connect calendar integrations

The whole point of a scheduler is two-way calendar sync. For Google Calendar:

  1. 1Create OAuth credentials in Google Cloud Console.
  2. 2Add the redirect URI https://cal.example.com/api/integrations/googlecalendar/callback.
  3. 3Provide the credentials to Cal.com (via the GOOGLE_API_CREDENTIALS env var as a JSON blob).

For Office 365 / Outlook, register an app in Azure and supply the client ID/secret. Each integration has its own variables — add only the ones you need.

Step 7: First-run setup

Visit your domain and complete onboarding:

  1. 1Create the first admin user.
  2. 2Set availability and timezone.
  3. 3Connect your calendar (using the integration configured above).
  4. 4Create event types (e.g. "30-min intro call").
  5. 5Share your booking link.

Cal.com vs. Calendly, fairly

Calendly is polished, zero-ops, and has a deep integration marketplace — for many teams it's the path of least resistance. Cal.com's advantages are self-hosting (data residency, GDPR control), open-source extensibility, custom theming, and no per-seat pricing at scale. The tradeoff is operational ownership and a config-heavy setup. Both are strong; self-host when control and cost-at-scale matter.

Operating tips

  • Back up PostgreSQL — it holds bookings and encrypted integration tokens. Scheduled backups cover it.
  • Keep CALENDSO_ENCRYPTION_KEY stable — losing it means re-connecting all calendars.
  • Match all URL env vars to the real domain.
  • Pin image versions, run prisma migrate deploy on upgrades, and read release notes.

References

  • [Cal.com documentation](https://cal.com/docs)
  • [Cal.com self-hosting guide](https://cal.com/docs/introduction/quick-start/self-hosting)
  • [Cal.com environment variables (.env.example)](https://github.com/calcom/cal.com/blob/main/.env.example)
  • [Prisma Migrate deploy](https://www.prisma.io/docs/orm/prisma-migrate/workflows/development-and-production)

---

Cal.com is mostly a careful env-and-migrations exercise on top of a Next.js app and a database — and PandaStack streamlines the hard parts: managed PostgreSQL with an injected DATABASE_URL, pre-deploy migration hooks, and automatic SSL on your domain. Self-host your scheduling at [dashboard.pandastack.io](https://dashboard.pandastack.io).

Ready to deploy?

Start free on PandaStack.

Start free on PandaStack

More in Tutorial

Browse all Tutorial articles →

See also