12 min read
Implemented in Booking Kit
Full-stack booking & appointment starter: timezone-correct availability engine, specialist picker, Stripe pay-on-booking, and admin — React + Go or Node + MongoDB
See Booking KitTwo reasons people self-host scheduling, and only one of them is money.
The bill. Per-seat pricing is fine at three people and irritating at fifty, because scheduling is not fifty times more valuable to you at fifty seats than at one.
The data. If your bookings are patient appointments, legal consultations or anything a customer signed a processing agreement about, "our scheduling data lives in a US SaaS" is a sentence you may not be allowed to say.
If neither applies, stay where you are. Calendly is good software and this page is not going to argue otherwise.
Per-seat scheduling tools sit in a similar band, and the arithmetic is the same whichever you use:
| Seats | Roughly per year |
|---|---|
| 5 | €600-900 |
| 20 | €2.400-3.600 |
| 50 | €6.000-9.000 |
Against a self-hosted deployment: a small VPS is €10-20 a month, so €150-250 a year, flat, at any number of seats.
The honest counterweight is that hosting is not the whole cost. Add your own time for upgrades, backups and the occasional 3am. Below roughly ten seats, the SaaS is usually cheaper once you price your hours. Somewhere past twenty, it stops being close.
Anyone selling you self-hosting without this list is selling you a weekend you did not plan.
Calendar sync is the real work. Google Calendar and Microsoft 365 two-way sync means OAuth apps you register and maintain, refresh tokens that expire, webhooks for external changes, and a reconciliation strategy for when the two sides disagree. This is the single largest piece and it is not close.
Deliverability. Confirmation and reminder emails have to arrive. That means SPF, DKIM, DMARC and a sending reputation. Send transactional mail from a fresh domain without that and it lands in spam — silently, which is worse.
Video links. Auto-generated Meet or Zoom rooms are another OAuth integration each.
The polish you stop noticing. Timezone detection from the browser, "add to calendar" files that work in Outlook, reschedule links that survive a forward, invitee-facing wording in eight languages.
Uptime is now yours. Nobody notices scheduling until it is down during a launch.
If you build rather than deploy something existing, three things will bite. They bite everyone.
Daylight saving time. Booking is not calendar arithmetic; it is timezone arithmetic, and twice a year the arithmetic breaks. On the spring-forward day a slot at 02:30 does not exist. On the fall-back day 01:30 happens twice. Get this wrong and you produce phantom slots nobody can attend and duplicate slots you double-book.
The rule that survives: store UTC, compute in the business timezone, render in the invitee's — and unit-test the engine against both DST boundaries, not just a normal Tuesday.
Double-booking under concurrency. Two people open the same slot and both click confirm. Checking availability and then writing the booking is two operations, and between them is a gap. The fix is a database constraint, not a check:
-- Postgres refuses the overlap for you. Application logic will not.
CREATE EXTENSION IF NOT EXISTS btree_gist;
ALTER TABLE bookings
ADD CONSTRAINT no_overlap
EXCLUDE USING gist (
staff_id WITH =,
tstzrange(starts_at, ends_at) WITH &&
) WHERE (status <> 'cancelled');Let the database say no. Anything you write in the application has a race in it.
Buffers, and what a slot really is. Fifteen minutes between appointments, travel time, a lunch break that is not a booking, someone available Tuesdays and Thursdays only, and a service only two of five staff can perform. Availability is not "opening hours minus bookings" — it is a computation over rules, exceptions and capabilities, and it is where the actual complexity of a booking system lives.
Deploy an existing open-source scheduler if what you need is Calendly's shape without the bill. Cal.com is the obvious one, it is genuinely good, and this is the right answer for "book a meeting with me". Take it if your problem is meetings.
Build on a starter if scheduling is part of your product rather than a tool beside it — a salon, a clinic, equipment rental, a service marketplace. That case is not meeting scheduling: it has services with durations and prices, staff with capabilities, payment at booking, and roles. Forking a meeting scheduler to become that is more work than starting from something shaped like it.
Build from scratch only if your availability rules are genuinely unusual, and knowing that DST, concurrency and buffers are the three walls you will hit.
If it is the second case, Booking Kit is that shape: a DST-correct availability engine unit-tested against both boundaries, conflict re-validated at confirmation, per-staff hours with exceptions, services with capable-staff sets, Stripe pay-on-booking, and four RBAC roles. Go or Node backend, full source, self-hosted — and the live demo runs the code you receive.