How to Deploy Cal.com – Open-Source Calendly Alternative

Updated on 16 April, 2026
Deploy Cal.com as a self-hosted alternative to Calendly with Docker, PostgreSQL, and HTTPS support.
How to Deploy Cal.com – Open-Source Calendly Alternative header image

Cal.com is an open-source scheduling platform for creating and sharing booking links for meetings, appointments, and events. It serves as a self-hosted alternative to Calendly, giving full control over user data and customization. Cal.com supports individual scheduling, team workflows, and organization-level management.

This article explains how to deploy Cal.com using Docker Compose with PostgreSQL for persistent data storage and Traefik as a reverse proxy. It covers Hypertext Transfer Protocol Secure (HTTPS) configuration with automatic certificate provisioning through Let's Encrypt using a custom domain.

Prerequisites

Before you begin, you need to:

Set Up the Directory Structure, Configuration, and Environment Variables

Cal.com runs as a web application container connected to a PostgreSQL database for persistent storage. Traefik operates as the reverse proxy, handling Transport Layer Security (TLS) termination and automatic certificate provisioning through Let's Encrypt.

  1. Create a project directory for Cal.com.

    console
    $ mkdir calcom
    
  2. Navigate into the directory.

    console
    $ cd calcom
    

    This directory stores the Docker Compose configuration, environment variables, and application data.

    Cal.com requires separate secure values for authentication and data encryption.

  3. Generate a random secret for authentication.

    console
    $ openssl rand -base64 32
    
  4. Generate a second random secret for data encryption.

    console
    $ openssl rand -base64 32
    

    Use the first output for NEXTAUTH_SECRET and the second for CALENDSO_ENCRYPTION_KEY when creating the .env file in the next step.

  5. Create a .env file to store environment variables.

    console
    $ nano .env
    

    Add the following configuration:

    ini
    NODE_ENV=production
    
    DOMAIN=cal.example.com
    LETSENCRYPT_EMAIL=admin@example.com
    
    NEXT_PUBLIC_WEBAPP_URL=https://cal.example.com
    NEXTAUTH_URL=https://cal.example.com
    ALLOWED_HOSTNAMES=["cal.example.com"]
    NEXTAUTH_URL_INTERNAL=http://calcom:3000
    AUTH_TRUST_HOST=true
    
    NEXTAUTH_SECRET=REPLACE_WITH_GENERATED_SECRET
    CALENDSO_ENCRYPTION_KEY=REPLACE_WITH_GENERATED_ENCRYPTION_KEY
    
    DATABASE_URL=postgresql://calcom:calcompass@postgres:5432/calcom
    DATABASE_DIRECT_URL=postgresql://calcom:calcompass@postgres:5432/calcom
    
    CALCOM_TELEMETRY_DISABLED=1
    CAL_SIGNATURE_TOKEN=self-hosted
    

    Replace:

    • cal.example.com with your registered domain name (in both DOMAIN and the URL variables)
    • admin@example.com with your email address for Let's Encrypt certificate notifications
    • REPLACE_WITH_GENERATED_SECRET with the first generated secret
    • REPLACE_WITH_GENERATED_ENCRYPTION_KEY with the second generated secret
    Note
    NEXT_PUBLIC_WEBAPP_URL is a build-time variable. It is baked into the Next.js build when the container starts for the first time. If you change it later, recreate the container with docker compose up -d --force-recreate rather than restarting it, otherwise the change has no effect.

    Save and close the file.

  6. (Optional) Configure SMTP variables for outgoing email. Cal.com starts and runs without email configured, but booking confirmations, password resets, and team invitations are disabled. Refer to the .env.example file in the Cal.com repository and add the required SMTP variables to the .env file before starting the containers.

Deploy with Docker Compose

Docker Compose coordinates the application, database, and reverse proxy layers. Traefik discovers the Cal.com container via Docker labels, applies domain-based routing rules, and manages TLS termination using certificates provisioned automatically through Let's Encrypt.

  1. Add your user to the Docker group so that Docker commands run without sudo.

    console
    $ sudo usermod -aG docker $USER
    
  2. Apply the new group membership to the current shell session.

    console
    $ newgrp docker
    
  3. Create the Docker Compose file.

    console
    $ nano docker-compose.yaml
    

    Add the following manifest:

    yaml
    services:
      traefik:
        image: traefik:v3.6
        container_name: traefik
        restart: unless-stopped
        command:
          - "--providers.docker=true"
          - "--providers.docker.exposedbydefault=false"
          - "--entrypoints.web.address=:80"
          - "--entrypoints.websecure.address=:443"
          - "--entrypoints.web.http.redirections.entrypoint.to=websecure"
          - "--entrypoints.web.http.redirections.entrypoint.scheme=https"
          - "--certificatesresolvers.le.acme.httpchallenge=true"
          - "--certificatesresolvers.le.acme.httpchallenge.entrypoint=web"
          - "--certificatesresolvers.le.acme.email=${LETSENCRYPT_EMAIL}"
          - "--certificatesresolvers.le.acme.storage=/letsencrypt/acme.json"
        ports:
          - "80:80"
          - "443:443"
        volumes:
          - /var/run/docker.sock:/var/run/docker.sock:ro
          - ./letsencrypt:/letsencrypt
    
      postgres:
        image: postgres:15
        container_name: postgres
        restart: unless-stopped
        environment:
          POSTGRES_USER: calcom
          POSTGRES_PASSWORD: calcompass
          POSTGRES_DB: calcom
        volumes:
          - ./postgres-data:/var/lib/postgresql/data
    
      calcom:
        image: calcom/cal.com:v6.2.0
        container_name: calcom
        env_file:
          - .env
        depends_on:
          - postgres
        expose:
          - "3000"
        labels:
          - "traefik.enable=true"
          - "traefik.http.routers.calcom.rule=Host(`${DOMAIN}`)"
          - "traefik.http.routers.calcom.entrypoints=websecure"
          - "traefik.http.routers.calcom.tls=true"
          - "traefik.http.routers.calcom.tls.certresolver=le"
          - "traefik.http.services.calcom.loadbalancer.server.port=3000"
        volumes:
          - ./calcom-data:/data/app/.data
        restart: unless-stopped
    

    Save and close the file.

    In the above manifest:

    • services: Defines three containers managed by Docker Compose.
      • traefik: Functions as the reverse proxy and TLS termination point, handling incoming HTTP and HTTPS traffic and forwarding requests to the Cal.com container.
      • postgres: Provides the PostgreSQL database for storing user accounts, scheduling configurations, and application metadata.
      • calcom: Hosts the Cal.com web application and connects to the PostgreSQL service.
    • image: Specifies the container image for each service.
    • container_name: Assigns predictable names to containers, simplifying log inspection and service management.
    • command (Traefik): Configures Traefik with Docker provider integration, HTTP and HTTPS entry points, automatic HTTP-to-HTTPS redirection, and Let's Encrypt certificate acquisition.
    • ports (Traefik): Maps host ports 80 and 443 to Traefik to receive incoming web traffic.
    • env_file (Cal.com): Loads environment variables from the .env file, including authentication secrets, encryption keys, application URLs, and database connection strings.
    • expose (Cal.com): Exposes port 3000 to other containers on the shared Docker network without publishing it to the host, allowing Traefik to reach Cal.com internally.
    • labels (Cal.com): Instructs Traefik to route HTTPS requests matching the configured domain to the Cal.com container on port 3000.
    • volumes:
      • The ./postgres-data bind mount stores PostgreSQL data on the host between container restarts.
      • The ./calcom-data bind mount stores application-level configuration and state for Cal.com on the host.
      • The Docker socket mount (/var/run/docker.sock) enables Traefik to detect and configure routes for running containers automatically.
      • The ./letsencrypt bind mount stores TLS certificates across container restarts.
    • restart: unless-stopped: Configures all containers to restart automatically after crashes or server reboots, unless explicitly stopped.
  4. Start the containers in detached mode.

    console
    $ docker compose up -d
    
  5. Verify that all containers are running.

    console
    $ docker compose ps
    

    The output displays three running containers with Traefik listening on ports 80 and 443.

  6. View the Cal.com logs to confirm the application started successfully.

    console
    $ docker compose logs calcom
    

    The output shows Next.js startup messages ending with Ready in — confirming the application is serving traffic on port 3000.

    For more information on managing Docker Compose stacks, see the How to Use Docker Compose article.

Access Cal.com

  1. Open your web browser and navigate to https://cal.example.com/auth/setup, replacing cal.example.com with your configured domain name.

  2. Complete the administrator setup wizard. Enter a username, full name, email address, and password, then click Next.

  3. Choose a license. Select AGPLv3 license for the free open-source license, or enter a commercial license key if you have one. Click Skip step to proceed with the AGPLv3 license.

  4. Enable apps. Toggle the integrations you want to make available to users, organized by category such as Calendar, Conferencing, and Payment. Click Finish.

    Cal.com redirects you to the user onboarding wizard.

  5. Set up your profile. Confirm your username and full name, and select your timezone. Click Connect your calendar.

  6. Connect a calendar. Select a calendar provider such as Apple Calendar or CalDav and follow the prompts to connect it. Click I'll connect my calendar later to skip.

  7. Connect a video app. Select a video conferencing app such as Campfire or Discord and follow the prompts. Click Set up later to skip.

  8. Set your availability. Adjust your available hours for each day of the week. The default is Monday through Friday, 9:00 a.m. to 5:00 p.m. Click Complete your profile.

  9. Complete your profile. Upload a profile photo and add a short bio. Click Finish setup and get started to access the dashboard.

Create and Schedule a Test Booking

A Cal.com deployment is fully functional when it accepts bookings, persists them to PostgreSQL, and surfaces them in the dashboard. Create a test booking to confirm that the scheduler, database, and UI are operating correctly.

  1. From the sidebar, navigate to Event Types and click + New.

  2. In the Add a new event type modal, enter a title, an optional description, and a duration in minutes. The URL slug is filled in automatically from the title. Click Continue.

  3. Review the event type settings in the editor and click Save.

  4. Copy the booking URL by clicking the link icon in the top-right toolbar of the event type editor.

  5. Open the booking URL in an incognito window or a different browser to simulate a visitor.

  6. Select an available date, choose a time slot, enter a name and email address, and confirm the booking.

  7. Return to the Cal.com dashboard and refresh it. Verify that the booking appears under Bookings.

Conclusion

You have successfully deployed Cal.com on your Linux server using Docker Compose with PostgreSQL for persistent data storage and Traefik providing automatic HTTPS through Let's Encrypt. Traefik handles all inbound traffic on ports 80 and 443 and forwards requests to the Cal.com container over the shared Docker network. For more information, refer to the official Cal.com documentation.

Comments