Build Cal.com Teams

KINDAreplaces $16/mosaves $192/yrback to the verdict

0%0 of 21 items done

Saved on this device only. Tick prerequisites first, then work the phases in order · do not start one until the checks above it pass.

Cal.com self-hosted on a server you own, connected to real calendar accounts so availability reflects actual commitments, with team event types, round robin assignment, buffers and notice periods configured deliberately, confirmation and reminder email that arrives, and a restore you have performed.

estimated effort multi-daythe files for this build are in the project pack

ApplicationCal.com, self-hostedDatabasePostgreSQLCalendarsReal provider connections through their APIsEmailA transactional provider with a verified domainEdgeA reverse proxy with automatic certificates

Before step 1

Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.

  1. have readyroughly 10 to 25 USD a month

    Why Both run here, and an undersized server produces slow booking pages, which people abandon.

    Get it Any virtual server provider. Check the project's current requirements before choosing.

    Verify The application starts and the booking page loads promptly

  2. have readya few USD if deleted the same day

    Why Restoring onto the machine that already has the configuration proves nothing, and the missing configuration is exactly what causes a broken restore here.

    Get it The same provider, smallest suitable plan, destroyed after the drill.

    Verify You can SSH to it

  3. have readyroughly 10 to 15 USD a year

    Why Needed for certificates, for the public URL, and for the email sending domain records.

    Get it Any registrar. You will add an A record and several email verification records.

    Verify The hostname resolves and email records validate

  4. API keyfree for the API itself; some providers require a verification review before an application can request calendar access broadly

    Why Without them availability is computed from nothing, which is worse than having no booking page.

    Get it Create an application in your calendar provider's developer console, enable the calendar scope, and add the redirect URL matching your public URL. Approval steps vary by provider and can take time.

    Verify An account connects and its events appear as busy time

  5. API keyfree tiers usually cover a small team's volume

    Why Confirmations, invitations, reminders and cancellations all depend on delivery. A booking system whose email fails is worse than a shared inbox.

    Get it Create an account, add the DNS records, and take the credentials. Domain verification is the slow step, so start it in Phase 1.

    Verify A confirmation arrives at three different providers and is not in spam

  6. have readyfree

    Why Phase 4's concurrency test and Phase 5's delivery test both need someone other than you.

    Get it Ask a colleague. Ten minutes of their time covers both.

    Verify A simultaneous booking test and a cross-provider delivery test are both completed

Data model

Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.

- The application owns its schema. What you own is the configuration, and it is worth recording.
- instance · version, host, database, and the deploy method
- calendar connections · which account, which calendars are read for busy time, and which one receives new events
- event types · duration, buffers before and after, minimum notice, booking window, and which team members
- routing · round robin, collective or a single host, and the fairness rule
- backups · database and configuration, off the machine, with a restore date

Environment variables

These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.

VariableNeededExampleWhere the value comes from
NEXT_PUBLIC_WEBAPP_URLrequiredhttps://cal.example.comThe public URL. Appears in every link and email, so it must be correct before the first booking.
DATABASE_URLsecretrequiredpostgres://user:pass@db:5432/calYour PostgreSQL connection string.
NEXTAUTH_SECRETsecretrequiredgenerate-a-long-random-valueSigns sessions. Part of a working restore, so back it up.
CALENDSO_ENCRYPTION_KEYsecretrequiredgenerate-a-long-random-valueEncrypts stored calendar credentials. Losing it means reconnecting every calendar, which is why it belongs in the backup.
GOOGLE_API_CREDENTIALSsecretrequired{"web":{"client_id":"..."}}From your calendar provider's developer console, with the redirect URL matching the public URL.
EMAIL_SERVERsecretrequiredsmtp://user:pass@smtp.provider.com:587Transactional provider credentials.
EMAIL_FROMrequiredbookings@example.comMust be on the verified sending domain or delivery will suffer.

The build, in order

  1. Running, on your own domain

    The application is up, reachable over HTTPS, and an account exists.

    1. Non-root user, key-only SSH, firewall allowing SSH and web ports.

      terminal
      ufw allow OpenSSH && ufw allow 80,443/tcp && ufw enable
    2. Following the project's own deployment guidance for the version you are using.

      Files docker-compose.yml

    3. Before the first booking. Verify by generating a link and reading it.

      Files Caddyfile.env

    done when · tick each as it passes
    watch out
    • The configured public URL appears in every booking link, confirmation email and calendar invitation. Setting it wrong and correcting it later leaves broken links in emails already sent, and those are the links customers click. Set it correctly before the first booking.
  2. Real calendars, so availability is true

    Your actual commitments block your availability.

    1. Create the credentials the application expects and complete the authorisation flow for each person.

    2. Every calendar representing a real commitment. This is the setting that decides whether availability is honest.

    3. Create one, check the public page. Do not trust the configuration screen.

    done when · tick each as it passes
    watch out
    • Reading only the destination calendar for busy time is the common misconfiguration, and it means a meeting in your personal calendar does not block a slot. Select every calendar that represents a real commitment, including the ones you would rather not think about.
  3. Event types with buffers that reflect reality

    Slots offered are slots you could actually take.

    1. All three before publishing. The defaults allow a booking in five minutes with no gap.

    2. Per person, with the zone explicit. Zones are where scheduling tools embarrass people.

    3. A private window with a different browser zone. Check the times are what you expect.

    done when · tick each as it passes
    watch out
    • Zero buffer and zero notice means someone can book you for the next five minutes, back to back with the previous meeting, and the first time it happens you will disable the whole thing. Set both before publishing the link, because the defaults are permissive.
  4. Team routing without double booking

    A team event assigns fairly and never books two people into the same slot by mistake.

    1. Round robin for one-of, collective for all-of. The distinction matters for availability computation.

    2. Book several times and check distribution. Fairness rules vary and the default may not be what you assumed.

    3. Two requests for the last slot at the same moment. Exactly one must succeed.

      terminal
      ab -n 2 -c 2 -p booking.json -T application/json https://cal.example.com/api/book
    done when · tick each as it passes
    watch out
    • Two bookers submitting the same slot within the same second is the case that produces a double booking, and it will not appear in casual testing. Exercise it deliberately with two simultaneous requests, and confirm exactly one succeeds and the other is told the slot has gone.
  5. Email that arrives

    Confirmations, reminders and cancellations reach inboxes rather than spam folders.

    1. The DNS records your provider specifies. Propagation takes hours, so start early.

    2. Three different ones, checking the spam folder in each.

    3. In two different mail clients. Cancellation is the one people forget to test.

    done when · tick each as it passes
    watch out
    • An invitation that does not carry a correctly formed calendar attachment leaves the booker with an email and no event, and they will not notice until they miss it. Test the invitation in at least two different mail clients, because handling differs.
what this build does not replace
after v1, if you want it

Need the files? The project pack on the verdict page hands your agent the whole brief · more scheduling.