# How to self-host Cal.com on Ownkube

> Deploy Cal.com's official image on Ownkube with a managed Postgres database, the required auth and encryption keys, and a custom domain for scheduling.

- **Published:** 2026-08-23
- **Author:** Ownkube team
- **Category:** How-To
- **Tags:** cal-com, self-hosting, scheduling, deploy, tutorial
- **Canonical URL:** https://ownkube.io/blog/deploy-cal-com
- **Cover:** https://ownkube.io/blog/deploy-cal-com.png

---
Cal.com is an open source scheduling tool built to be self-hosted, and running it yourself avoids the per-seat pricing of the hosted version once a team grows past a couple of people. This walks through deploying Cal.com's official [Docker image](https://docs.docker.com/) on Ownkube with Postgres, the auth secrets it requires, and a working custom domain.

**TL;DR**

- Cal.com needs Postgres as its primary datastore. There is no SQLite option, so provision a managed database before deploying the app.
- Deploy the official Cal.com image, set `DATABASE_URL`, `NEXTAUTH_SECRET`, and `CALENDSO_ENCRYPTION_KEY`, and give it your real domain via `NEXT_PUBLIC_WEBAPP_URL` before creating any bookings.
- `NEXTAUTH_SECRET` and `CALENDSO_ENCRYPTION_KEY` should be generated once and treated as permanent. Rotating them invalidates sessions and encrypted calendar credentials.
- Ownkube issues TLS automatically once a [custom domain](/blog/automatic-https-custom-domain) is attached, which Cal.com's calendar OAuth flows (Google, Outlook) require to complete correctly.
- A Postgres database box starts at $4/month plus storage, and the app runs comfortably on a small reserved box given its steady, low-idle traffic pattern.

## What you need before you start

- The image reference `calcom/cal.com:latest`, or a pinned release tag for reproducible deploys.
- A domain or subdomain for the app. Calendar OAuth (Google, Microsoft) redirects back to this URL and needs HTTPS to complete.
- Two secrets generated ahead of time: a `NEXTAUTH_SECRET` and a `CALENDSO_ENCRYPTION_KEY`, both random strings you generate once and keep.

```bash
# Generate both secrets locally before you deploy
openssl rand -base64 32   # NEXTAUTH_SECRET
openssl rand -base64 32   # CALENDSO_ENCRYPTION_KEY
```

## 1. Create a managed Postgres database

Cal.com's Prisma schema targets Postgres specifically; there is no supported SQLite path. Create a database box on Ownkube (from $4/month plus storage at $0.15/GB/month) and note the connection details.

Cal.com expects a single connection string rather than split host and port variables, so assemble it in the [standard Postgres URL format](https://www.postgresql.org/docs/):

```bash
postgresql://<user>:<password>@<host>:5432/<database>?schema=public
```

## 2. Deploy the Cal.com image

Create the app from the [official image](https://cal.com/docs/introduction/quick-start/self-hosting/installation):

```dockerfile
FROM calcom/cal.com:v4.6.6
```

Pinning a version tag rather than `latest` is worth doing here, since Cal.com ships frequently and a pinned tag keeps a database migration from running unexpectedly on a redeploy. The image listens on port `3000` by default.

## 3. Set the environment variables

```bash
# Database
DATABASE_URL=postgresql://<user>:<password>@<host>:5432/<database>?schema=public

# Auth and encryption (generate once, keep permanently)
NEXTAUTH_SECRET=<generated-secret>
CALENDSO_ENCRYPTION_KEY=<generated-key>

# App URL
NEXT_PUBLIC_WEBAPP_URL=https://scheduling.example.com
NEXTAUTH_URL=https://scheduling.example.com

# Email (optional but needed for booking confirmations and invites)
EMAIL_FROM=notifications@example.com
EMAIL_SERVER_HOST=smtp.example.com
EMAIL_SERVER_PORT=587
EMAIL_SERVER_USER=<smtp-user>
EMAIL_SERVER_PASSWORD=<smtp-password>
```

Set `NEXT_PUBLIC_WEBAPP_URL` and `NEXTAUTH_URL` to the real domain before you connect any calendars. Calendar OAuth providers validate the redirect URL against what the app reports, and a mismatch after the fact means reconnecting every calendar integration rather than a quick edit.

## 4. Run the database migration

Cal.com's Prisma schema needs its migrations applied on first boot (and after upgrades that include schema changes). The official image runs this automatically on startup in most versions, but if you deploy from a custom build, run it explicitly as a one-off command against the app:

```bash
npx prisma migrate deploy
```

## 5. Attach your domain and connect a calendar

Point your domain's DNS at the app's Ownkube hostname and attach the custom domain in the dashboard. TLS is issued automatically once DNS resolves, which is a requirement for Cal.com's Google and Microsoft calendar OAuth flows, both refuse to complete a redirect back to a plain HTTP URL.

```bash
curl -I https://scheduling.example.com
```

Log in, create your first event type, and connect a calendar from Settings to confirm the OAuth round trip works end to end.

## Sizing the deployment

Cal.com is a Next.js app with a fairly steady request pattern once a team is booking through it regularly (page loads, availability checks, webhook callbacks from calendar providers), which makes a flat Spark box (from $5/month) a reasonable default rather than metered pricing, since the load rarely drops to fully idle during business hours. A personal scheduling page with light traffic is fine on metered pricing instead, where CPU and memory bill on actual per-minute use.

If self-hosted scheduling eventually needs to move onto infrastructure your company controls directly, the same Cal.com image and environment variables run unchanged in [your own AWS account](/blog/move-app-to-your-own-aws) under Ownkube's own-cloud option, with the same database migration path and the same logs and metrics attached. That is a redeploy target, not a rewrite. For a wider look at how self-hosted platforms compare on cost and control, see our [comparison of Railway, Render, and Northflank](/blog/railway-vs-render-vs-northflank-2026).

## FAQ

### Does Cal.com support SQLite for a small deployment?

No. Cal.com's schema is built for Postgres and there is no supported SQLite backend. A managed Postgres database box is required even for a single-user instance.

### What happens if I rotate `CALENDSO_ENCRYPTION_KEY`?

Rotating it breaks decryption of previously stored calendar credentials and tokens, forcing every connected calendar to be reconnected. Generate it once before first deploy and keep it out of version control but permanently saved somewhere safe.

### Do I need to run database migrations manually?

The official image typically applies Prisma migrations automatically on boot. If you build a custom image or skip that step, run `npx prisma migrate deploy` against the app once before serving traffic.

### Why does calendar sync fail after I change domains?

Google and Microsoft OAuth validate the redirect URL against what's registered for the app. Changing `NEXTAUTH_URL` or `NEXT_PUBLIC_WEBAPP_URL` after connecting calendars means reconnecting each one so the provider accepts the new redirect.

### What does this cost to run?

A Postgres database box starts at $4/month plus storage. The app itself fits well on a Spark box from $5/month, or metered pricing for lighter, more occasional use.

## Where Ownkube fits

Cal.com needs Postgres, two permanent secrets, and a real HTTPS domain to work correctly with calendar providers, and Ownkube Compute gives you all three without provisioning a cloud account first. Deploy the image, attach [managed Postgres](/blog/add-managed-postgres-database), set the auth keys once, and point a domain at it. [Deploy your first app](https://app.ownkube.io/login).