The short answer
For a booking page like Calendly's, where customers pick a free time with one of your staff, pick Easy!Appointments. It got every time right across the change to winter time, synced with our CalDAV calendars, put a Jitsi link and a calendar file in every email, speaks Swedish to customers, and used about 150 MB. But it has no buffers between meetings, no reminder emails, no team meetings with several hosts, and an interface that looks its age.
Pick Rallly only for the other half of Calendly: finding a time that suits a group, like Calendly's meeting polls. It did that well, in Swedish and across time zones, but it is not a booking page, and on your own server it has no API, no webhooks and no calendar sync.
Cal.com is not one of the two: as of 14 October 2026, nobody publishes a maintained image of it to run yourself.
Cal.com and Cal.diy in October 2026
Cal.com is the best-known open-source Calendly alternative, and the one most people look for first. This is what we found on 14 October 2026:
- Cal.com moved its open-source code to a community edition called Cal.diy, under the MIT licence, without teams, organisations, workflows, insights or single sign-on. The old
calcom/cal.comrepository on GitHub now leads tocalcom/cal.diy, which still gets commits; the last one we saw was from 26 September 2026. - The last release is v6.2.0, from 1 March 2026, and the last image is
calcom/cal.com:v6.2.0, published on 2 March 2026. Thecalcom/cal.diyrepository on Docker Hub has no images at all. The project's own build of the image failed the last three times it ran, from 20 to 26 September 2026. - An issue called "Docker Image Missing" has been open on GitHub since 24 April 2026. In September, a user reported that building the image from the source instead took about 36 minutes and 6.5 GB of memory.
- Cal.diy's README says it is "strictly recommended for personal, non-production use", and that for "commercial and enterprise-ready scheduling infrastructure", you should use Cal.com, hosted by the company or "on-prem enterprise access" through its sales team.
An image that has had no security updates for seven months is not something we would put a customer-facing booking page on, so we did not test Cal.com here. If an image of Cal.diy appears, we will look again.
What we tested
We installed both on the same fresh Melonslab VC-P Alloy (2 vCPU, 8 GB) with Debian 13, each under a user of its own, behind the same Caddy. We have no guides for either yet, so we ran each project's official image the way our guides do: Quadlet units, the database in the same pod, passwords as Podman secrets and Caddy in front. How we ran Easy!Appointments and How we ran Rallly show the files.
Then we set up the same business in both, Exempel AB:
- two staff, Anna Lind, who works Monday to Friday, 9:00 to 17:00, and Erik Lund, Monday to Thursday, 10:00 to 16:00, both with lunch from 12:00 to 13:00, Stockholm time;
- an Intro call of 30 minutes and a Consultation of 60 minutes, with four hours' notice;
- a calendar for each of them in Radicale, a CalDAV server on the same machine, with a dentist's appointment in Anna's;
- emails to a test mail catcher on the server.
Customers booked from headless browsers set to New York, Los Angeles and Stockholm time, on dates on both sides of 25 October 2026, when Sweden goes back to winter time, and 1 November 2026, when the United States does. They rescheduled, cancelled and deleted their own data. We embedded the booking page on a plain HTML page, switched both to Swedish, restarted the server, measured memory at rest and under use, and backed up and restored both.
Calendly itself was not tested. We did not sign up. What this page says about it comes from Calendly's own pages, linked where we use them and read on 14 October 2026.
The numbers
| Easy!Appointments 1.6.0 | Rallly 4.15.4 | |
|---|---|---|
| What it is | A booking page: customers book a time with a person | Group polls: people vote on times, you pick one |
| Containers | 2: Easy!Appointments with Apache and PHP 8.2, MariaDB 11.8 | 2: Rallly on Node.js, PostgreSQL 18 |
| Memory at rest after a reboot | 145 MB (MariaDB 128, the app 17) | 305 MB (Rallly 300, PostgreSQL 5) |
| Memory at the peak of use | 208 MB | 761 MB |
| Images on disk | 0.97 GB | 1.34 GB |
| Data after the test | 171 MB, nearly all MariaDB's own files | 69 MB |
From useradd to a page that answers | 58 seconds | 68 seconds |
| Times right across a daylight saving change | yes | yes |
| Buffers between meetings | no | not applicable |
| Minimum notice | yes, one setting for all | not applicable |
| Calendar file in emails | yes | yes, when you pick the final time |
| Reminder emails | no | no |
| Round robin | "Any provider": whoever has most free time that day | no |
| CalDAV sync | yes, both ways | no |
| Webhooks and API | yes, free | not on your own server |
| Embed on your website | yes, with a header changed in Caddy | no |
| Swedish | booking page, customer emails, staff interface | everything |
| Users | unlimited | one free; more need a licence |
| Licence | GPL 3.0 | AGPL 3.0, plus a paid licence for more users |
Memory is the sum of each app's containers, measured with podman stats five minutes after a reboot. The peak is the highest reading while we sent about six requests a second to Easy!Appointments, mostly looking up free times, and four a second to Rallly, mostly opening a poll, for three minutes. The times are how long the commands took to run, mostly downloading images, not the time you need to read and type them.
Setup
Easy!Appointments is one container with Apache and PHP, and a MariaDB database. The image is made by the project's author. Two things surprised us:
- The first visitor gets an installer that creates the admin account. Easy!Appointments also has a command for it, which we used before Caddy was set up, but that one creates the admin
administratorwith the passwordadministrator, plus a sample provider, customer and service. We changed the admin through the API before the site went online, and deleted the samples. - The image comes with Xdebug, a debugging tool for PHP, switched on. We turned it off with
XDEBUG_MODE=off.
Rallly is a Node.js container and a PostgreSQL database. Its own instructions use a script that also runs Traefik and Garage, for HTTPS and file storage; we needed neither behind Caddy. Rallly has no passwords at first: you sign in with a six-digit code sent by email, so without working email nobody gets in. Anyone with an email address can create an account unless you limit it; ALLOWED_EMAILS=*@example.com made Rallly answer "This email address is not allowed" to anyone else.
Booking pages, working hours and rules
In Easy!Appointments, a meeting type is a service with a length, and each staff member is a provider with a weekly working plan, breaks and exceptions such as holidays. A customer picks a service, a provider or Any Provider, a day and a time, enters a name, email and phone number, and confirms. The page is plain.
- Minimum notice is one setting for the whole site, in minutes. With 240 minutes, at 9:45 the earliest time offered that day was 14:00.
- How far ahead people can book is one setting too, 90 days by default.
- Buffers do not exist: two meetings can follow each other with no gap. The nearest thing is the slot interval, which only spaces out the start times on offer.
- Breaks worked: nobody could book Anna or Erik between 12:00 and 13:00.
Rallly has no booking page. You create a poll with a few possible times, share a link, people vote yes, if need be or no, and you pick the final time. Working hours, notice and buffers do not apply.
Time zones and daylight saving
This is where a booking tool is most often wrong, so we checked it carefully. Sweden left summer time on 25 October 2026 and the United States on 1 November, so for a week New York was five hours behind Stockholm instead of six.
Easy!Appointments showed a browser in New York its own time, with a time zone list to change it. On 20 October, Anna's 9:00 was offered as 03:00; on 26 October as 04:00; on 2 November as 03:00 again. Mary in New York booked Erik at 05:00 on 26 October, which landed in his calendar at 10:00, and the calendar file said 09:00 UTC, which is right. Her email showed 05:00, and Erik's 10:00. The only flaw: the summary and the emails label the zones with their winter offsets, "New_York (-5:00)" and "Stockholm (+1:00)", even in October, when they are four hours behind and two ahead. The times themselves were right.
Rallly showed each voter the poll in their own time zone: Anna's 12:00 on 23 October was 6:00 AM in New York and 3:00 AM in Los Angeles, and on 26 October 7:00 AM and 4:00 AM. The email to Mary when Anna picked the time said "7:00 – 8:00 AM EDT", and the calendar file 11:00 UTC. All right.
Emails, calendar files and reminders
Easy!Appointments emailed the customer, the provider and the admin on every booking, change and cancellation. Booking and change emails had a calendar file, invitation.ics, with the right time, the Jitsi link and a link to reschedule or cancel. The cancellation emails had no calendar file, so a customer who added the meeting to their calendar must remove it by hand. There are no reminder emails: the calendar file holds two reminders, but as alarms for the customer's calendar app to show, not emails Easy!Appointments sends.
Rallly emailed Anna each time someone voted, thanked each voter, and when she picked the time, sent everyone "Date booked for Planning meeting" with invite.ics. There are no reminders either.
Neither sends text messages.
Rescheduling and cancelling
Every Easy!Appointments email has a link where the customer can choose another time or cancel. Mary moved her meeting from 05:00 to 08:00 New York time; the provider and admin got an email, the event moved in Erik's CalDAV calendar, and the webhook fired. John cancelled with a reason, and the meeting disappeared from Anna's calendar. A cancelled booking is deleted, not kept as cancelled.
In Rallly, voters can change or delete their own votes from the same browser, from the menu next to their name. After you pick the final time, nothing can be changed: there is no way to move or cancel the meeting from Rallly.
Teams and round robin
Easy!Appointments has providers, secretaries who book for providers, and admins, with no limit on users. Any Provider on the booking page shows the free times of everyone who offers the service, and gives the booking to the provider with the most free time that day. That is load sharing, not a strict turn-by-turn round robin. A meeting with several staff at once is not possible; a service can take several customers per slot, for group classes.
Rallly is free for one user. When Erik signed in, he got a space of his own and a banner: "You have exceeded the limits of your license. Please upgrade." It still worked, as Rallly's licence page says it relies on an honour system, but from then on Anna saw the same banner. Anna could not invite him to her space: "No seats available".
Calendar sync
Easy!Appointments syncs with Google Calendar or any CalDAV calendar, chosen by each provider under Calendar > Enable Sync. New bookings went into Anna's and Erik's Radicale calendars, changes moved them and cancellations removed them. Anna's dentist's appointment was read back as busy time: her 10:00 to 11:30 on 20 October was no longer offered. Easy!Appointments reads the calendar when someone chooses Synchronize, or when its sync command runs; our files run it every 15 minutes.
Two things to know:
- Easy!Appointments refuses CalDAV addresses that point to the server itself or a private network: "Could not establish server connection." It makes one exception, for a server called
baikalover plain HTTP, which its developers use for testing; we used that name to reach Radicale on the same machine. A calendar on another server, at its normalhttpsaddress, is what it expects. We did not test one. - It stores the CalDAV password in its database as plain text. Use an app password if your calendar has them.
Google Calendar needs a Google Cloud project with its keys in the settings. Microsoft 365 and Outlook calendars have no built-in sync; CalDAV is the way if your provider offers it. We tested neither Google nor Microsoft.
Rallly on your own server has no calendar connections. Its "Add to calendar" button and the email's calendar file are how the meeting gets into a calendar.
Video links
Easy!Appointments has a Jitsi switch under Settings > Integrations. With it on, each booking got its own room, such as https://meet.jit.si/9422f4eb5d1830c0, in the email, the calendar file and the webhook. The address meet.jit.si is written into the code; to use your own Jitsi server, put a fixed room address in each service's Location instead. With Google Calendar sync, it can also make Google Meet links. There is no Zoom or Teams.
In Rallly, you put the video link in the poll's Location, and it goes into the final email and calendar file. Rallly's hosted service can create Zoom, Teams, Google Meet and Webex links; we found no such option on our server.
Webhooks and API
Easy!Appointments has both, free. A webhook for new, changed and deleted bookings, customers and more posted JSON to our test receiver within a second, with a fixed token in a header of our choosing, not a signature. The times in it, "start_datetime":"2026-10-26 10:00:00", are the provider's local time with no time zone given. The REST API, with an API key or an admin's password, listed bookings and free times; it is described in an OpenAPI file in the project.
Rallly has an API and webhooks on its hosted Pro plan, but its code only switches them on when it is not self-hosted. On our server, the pages for them answered 404.
Embedding on your website
Easy!Appointments sends X-Frame-Options: SAMEORIGIN, so an <iframe> on another site stays blank. With the header removed in Caddy and a frame-ancestors rule naming our page instead, a plain HTML page on another name in the same domain showed the booking page, and a booking made in it went through. Its cookies are SameSite=Lax, so on a site with a different domain we would expect booking to fail; we did not test that. A plain link to the booking page always works.
Rallly forbids being framed, with frame-ancestors 'none'. Link to the poll instead.
Swedish
Easy!Appointments showed its booking page in Swedish to a browser set to Swedish, from "Välj tjänst och utförare" to "Din bokning är sparad", and Karin got her email in Swedish: "Din tid är nu bokad". A few words stayed English: "Booked" and "Reschedule / Cancel Appointment" in the email, "Powered By" at the bottom. Staff choose Svenska in their account, and get the dashboard and their own emails in Swedish, such as "En ny bokning har lagts till i ditt schema". Your service names stay as you write them.
Rallly followed the browser too: Karin saw "Svarsalternativ", "Ja", "Om det behövs" and "Nej", and got "Tack för att du svarade på Planning meeting" and "Datum bokat för Planning meeting". Each email went out in the language of the person it was for.
Deleting an invitee's data
Under the GDPR, a customer can ask you to delete what you hold about them.
- Easy!Appointments has a setting, Delete Personal Information, that puts a red Delete button on the customer's reschedule page. When Mary used it, her customer record and booking were removed from the database, with no email and no webhook. But the event stayed in Erik's CalDAV calendar, with her name, email and phone number in its description, so delete it there by hand. You can also delete a customer under Customers, and the
cleanupcommand deletes customer data older than a number of days you set under Legal Contents. - Rallly let Mary delete her own vote, which removed her row. Deleting a whole poll only marks it as deleted; Rallly removes such polls after seven days, but only if something calls its housekeeping address, which its self-hosting documentation does not mention. Our files set that up. Even then, the final meeting and the names and email addresses of everyone invited to it stayed in the database. Removing those took SQL.
Resources
Both are small next to an 8 GB server. Easy!Appointments stayed near 150 MB, most of it MariaDB, and only reached 208 MB under use. Rallly used about 300 MB at rest and grew to 761 MB when four people a second opened a poll; three minutes later it was still at about 700 MB.
Updates and backups
Easy!Appointments releases once or twice a year; 1.6.0 came out on 27 May 2026, and 1.6.1 was in beta. The image is built once per release on PHP 8.2 and is not rebuilt in between, so system updates arrive only with new versions. We pinned the version and update by changing the tag; new versions update the database with the migrate command. Rallly releases every week or two; the 4.15 tag gets the patch releases through podman auto-update, and the database updates itself when the container starts.
Each backup is a database dump: 31 KB for Easy!Appointments and 104 KB for Rallly in our test. We restored both into empty volumes, and the bookings, polls and votes came back. Both came back by themselves after a reboot, and people stayed signed in.
How we ran Easy!Appointments
The project's Docker image takes its settings from environment variables and writes them into a PHP file when it starts. Because of that, a password containing ' breaks it; the ones below never do. As root:
useradd -m -s /bin/bash easyappointments
loginctl enable-linger easyappointments
machinectl shell easyappointments@
As easyappointments, create the secrets:
openssl rand -hex 24 | tr -d '\n' | podman secret create easyappointments-db-password -
printf %s 'your-smtp-password' | podman secret create easyappointments-smtp-password -
In ~/.config/containers/systemd, easyappointments.pod:
[Pod]
PodName=easyappointments
PublishPort=127.0.0.1:8151:80
[Install]
WantedBy=default.target
easyappointments-db.container:
[Container]
ContainerName=easyappointments-db
Image=docker.io/library/mariadb:11.8
Pod=easyappointments.pod
Volume=easyappointments-db:/var/lib/mysql
Environment=MARIADB_DATABASE=easyappointments MARIADB_USER=easyappointments MARIADB_RANDOM_ROOT_PASSWORD=1 MARIADB_AUTO_UPGRADE=1
Secret=easyappointments-db-password,type=env,target=MARIADB_PASSWORD
HealthCmd=healthcheck.sh --connect --innodb_initialized
HealthInterval=10s
Notify=healthy
AutoUpdate=registry
[Service]
Restart=always
easyappointments-app.container:
[Unit]
Requires=easyappointments-db.service
After=easyappointments-db.service
[Container]
ContainerName=easyappointments-app
Image=docker.io/alextselegidis/easyappointments:1.6.0
Pod=easyappointments.pod
Volume=easyappointments-storage:/var/www/html/storage
Environment=BASE_URL=https://book.example.com LANGUAGE=english DEBUG_MODE=FALSE XDEBUG_MODE=off
Environment=DB_HOST=127.0.0.1 DB_NAME=easyappointments DB_USERNAME=easyappointments
Environment=MAIL_PROTOCOL=smtp MAIL_SMTP_HOST=smtp.example.com MAIL_SMTP_PORT=587 MAIL_SMTP_CRYPTO=tls MAIL_SMTP_AUTH=TRUE
Environment=MAIL_SMTP_USER=book@example.com MAIL_FROM_ADDRESS=book@example.com MAIL_FROM_NAME=Bookings MAIL_REPLY_TO_ADDRESS=book@example.com
Secret=easyappointments-db-password,type=env,target=DB_PASSWORD
Secret=easyappointments-smtp-password,type=env,target=MAIL_SMTP_PASS
[Service]
Restart=always
The storage volume keeps sessions, so people stay signed in after a restart. In our test, email went to a test mail catcher on the server instead of a provider; we did not send through a real one. For the CalDAV test with Radicale on the same server, we also added AddHost=baikal:host-gateway to the pod file, as Calendar sync explains.
In ~/.config/systemd/user, easyappointments-sync.service reads the providers' calendars and runs the cleanup:
[Unit]
Description=Easy!Appointments calendar sync and cleanup
[Service]
Type=oneshot
ExecStart=podman exec -u www-data easyappointments-app php index.php console sync
ExecStart=podman exec -u www-data easyappointments-app php index.php console cleanup
And easyappointments-sync.timer:
[Timer]
OnCalendar=*:0/15
Persistent=true
[Install]
WantedBy=timers.target
Start it all, and install the database. console install empties the database first, so run it only once:
systemctl --user daemon-reload
systemctl --user start easyappointments-pod
systemctl --user enable --now podman-auto-update.timer easyappointments-sync.timer
podman exec -u www-data easyappointments-app php index.php console install
That creates the admin administrator with the password administrator. Before the site is online, replace both through the API, with your own details and a long password:
curl -s -u administrator:administrator -X PUT -H 'Content-Type: application/json' \
http://127.0.0.1:8151/index.php/api/v1/admins/1 \
-d '{"firstName":"Anna","lastName":"Andersson","email":"anna@example.com","phone":"+46700000000","timezone":"Europe/Stockholm","language":"swedish","settings":{"username":"anna","password":"a-long-password-of-your-own","notifications":true,"calendarView":"default"}}'
The block in Caddy's ~/Caddyfile. The two header lines are only needed to embed the booking page on www.example.com:
book.example.com {
reverse_proxy 127.0.0.1:8151 {
header_down -X-Frame-Options
}
header Content-Security-Policy "frame-ancestors 'self' https://www.example.com"
}
Then sign in at https://book.example.com/index.php/login, delete the sample provider Jane Doe, customer James Doe and service under Users, Customers and Services, and set your company's name, time zone and first weekday under Settings.
To back up, run podman exec easyappointments-db sh -c 'mariadb-dump -u easyappointments -p"$MARIADB_PASSWORD" --single-transaction easyappointments' > easyappointments.sql. Everything is in the database. To restore, stop the pod, remove the easyappointments-db volume, start easyappointments-db alone, load the dump with podman exec -i easyappointments-db sh -c 'mariadb -u easyappointments -p"$MARIADB_PASSWORD" easyappointments' < easyappointments.sql, and start the pod. To update, change 1.6.0 in Image=, restart easyappointments-app, and run podman exec -u www-data easyappointments-app php index.php console migrate.
How we ran Rallly
Rallly's own setup is a script with Docker Compose. We ran its lukevella/rallly image directly, with the settings it documents. Without a file store, uploading a profile picture or a space logo does not work; nothing else we tested needed one. As root, create the user rallly the same way as easyappointments above. As rallly, create the secrets:
pw=$(openssl rand -hex 24)
printf %s "$pw" | podman secret create rallly-db-password -
printf 'postgres://rallly:%s@127.0.0.1:5432/rallly' "$pw" | podman secret create rallly-database-url -
unset pw
openssl rand -hex 32 | tr -d '\n' | podman secret create rallly-secret -
openssl rand -hex 32 | tr -d '\n' | podman secret create rallly-cron-secret -
printf %s 'your-smtp-password' | podman secret create rallly-smtp-password -
In ~/.config/containers/systemd, rallly.pod:
[Pod]
PodName=rallly
PublishPort=127.0.0.1:8153:3000
[Install]
WantedBy=default.target
rallly-db.container:
[Container]
ContainerName=rallly-db
Image=docker.io/library/postgres:18-alpine
Pod=rallly.pod
Volume=rallly-db:/var/lib/postgresql
Environment=POSTGRES_USER=rallly POSTGRES_DB=rallly
Secret=rallly-db-password,type=env,target=POSTGRES_PASSWORD
HealthCmd=pg_isready -U rallly
HealthInterval=10s
Notify=healthy
AutoUpdate=registry
[Service]
Restart=always
rallly-app.container:
[Unit]
Requires=rallly-db.service
After=rallly-db.service
[Container]
ContainerName=rallly-app
Image=docker.io/lukevella/rallly:4.15
Pod=rallly.pod
Environment=NEXT_PUBLIC_BASE_URL=https://poll.example.com
Environment=SUPPORT_EMAIL=anna@example.com INITIAL_ADMIN_EMAIL=anna@example.com
Environment=SMTP_HOST=smtp.example.com SMTP_PORT=587 SMTP_SECURE=false SMTP_USER=poll@example.com NOREPLY_EMAIL=poll@example.com
Environment=ALLOWED_EMAILS=*@example.com NPM_CONFIG_UPDATE_NOTIFIER=false
Secret=rallly-database-url,type=env,target=DATABASE_URL
Secret=rallly-secret,type=env,target=SECRET_PASSWORD
Secret=rallly-cron-secret,type=env,target=CRON_SECRET
Secret=rallly-smtp-password,type=env,target=SMTP_PWD
AutoUpdate=registry
[Service]
Restart=always
TimeoutStartSec=600
ALLOWED_EMAILSlimits who can create an account and sign in. People who vote need no account.INITIAL_ADMIN_EMAILlets that person make themselves admin at/control-panel.NPM_CONFIG_UPDATE_NOTIFIER=falsestops the container from checking npm for updates each time it starts.CRON_SECRETprotects Rallly's housekeeping address, which the timer below calls.
In our test, email went to a test mail catcher on the server; we did not send through a real provider. In ~/.config/systemd/user, rallly-housekeeping.service removes deleted polls after seven days, and accounts whose deletion is due:
[Unit]
Description=Rallly housekeeping
[Service]
Type=oneshot
ExecStart=podman exec rallly-app sh -c 'curl -fsS -H "Authorization: Bearer $$CRON_SECRET" http://rallly:3000/api/house-keeping/remove-deleted-polls'
ExecStart=podman exec rallly-app sh -c 'curl -fsS -H "Authorization: Bearer $$CRON_SECRET" http://rallly:3000/api/house-keeping/remove-deleted-users'
And rallly-housekeeping.timer:
[Timer]
OnCalendar=daily
Persistent=true
[Install]
WantedBy=timers.target
Start it with systemctl --user daemon-reload, systemctl --user start rallly-pod and systemctl --user enable --now podman-auto-update.timer rallly-housekeeping.timer. The block in Caddy's ~/Caddyfile:
poll.example.com {
reverse_proxy 127.0.0.1:8153
}
Open https://poll.example.com, enter your email address and the code you get, and fill in your name, time zone and company. To back up, run podman exec rallly-db pg_dump -U rallly rallly > rallly.sql. To restore, stop the pod, remove the rallly-db volume, start rallly-db alone, load the dump with podman exec -i rallly-db psql -q -U rallly rallly < rallly.sql, and start the pod.
Licence and cost
We read the licence texts and pricing pages on 14 October 2026.
- Easy!Appointments is open source under the GPL 3.0, made by Alex Tselegidis, and free, with no paid edition and no limit on users. Its website sells services around it, such as installation, managed hosting, custom development and support contracts, without public prices.
- Rallly is made by Luke Vella and published under the AGPL 3.0. On top of that, its licensing page says it is "always free for personal use" for one user, and sells a licence for more users on your own server: 49 US dollars for up to 5 users, 299 for up to 50 as a "Limited Time Offer" (499 normally), and from 999 for more, each a one-off payment for all of Rallly 4. It "operates an honor system for user limits". Rallly's hosted service is free for up to 3 open polls, and Pro costs 6 US dollars per user a month billed yearly, which adds the API and webhooks.
Calendly today
What follows is from Calendly's own pages, read on 14 October 2026, not from our tests.
- Price. The pricing page lists Free, Standard at 10 US dollars and Teams at 16 US dollars per seat a month, with yearly billing saving "up to 20%", and Enterprise from 15,000 dollars a year. Free has one event type and one connected calendar.
- What costs extra. On that page, round robin and routing forms are in Teams; Stripe and PayPal payments, workflows with reminders, and webhooks start at Standard. Meeting polls are on all plans.
- Calendars. Google, Microsoft 365, Outlook.com and Exchange. Calendly no longer takes new iCloud connections since 20 August 2024, and we found no general CalDAV support.
- Video. Zoom, Google Meet, Microsoft Teams, Webex and GoTo Meeting, or a fixed link of your own.
- Languages. Booking pages and notifications come in English, French, Spanish, German, Dutch, Brazilian Portuguese, Italian and Ukrainian. Swedish is not on the list.
- Your data. "Calendly stores user and invitee data in U.S.-based data centers", according to its data storage page, and transfers from the EU rely on Standard Contractual Clauses. Owners and admins can delete an invitee's data by email address on every plan; the API for it is in Enterprise.
- Running it yourself. We found no edition of Calendly to run on your own server.
What you give up compared with Calendly
On your own server, bookings and customer details stay in Sweden, customers get Swedish pages and emails, and the cost is the server, not a price per person. But be honest about what you leave behind:
- Google and Microsoft calendars. Calendly connects to them with a click. Easy!Appointments needs a Google Cloud project for Google, and has nothing for Microsoft 365 except CalDAV, which Microsoft does not offer. Rallly has no calendar sync on your own server.
- Payments. Calendly takes payment through Stripe or PayPal when someone books. Easy!Appointments shows a price, but takes no payment; Rallly has no prices at all.
- Routing forms and team scheduling. Calendly asks questions first and sends the visitor to the right person, and books meetings with several hosts. Easy!Appointments has "Any Provider" and nothing more.
- Reminders. Calendly's workflows send reminder emails and texts. Neither of the two sends a reminder.
- Polish and buffers. Easy!Appointments' booking page is plain and looks dated, and it has no buffers between meetings.
- Running it. Updates, backups, email and the server are yours, and Easy!Appointments' image only gets system updates with each release.
Which one to pick
A booking page for customers: Easy!Appointments. It handles staff, working hours, breaks, notice, time zones, CalDAV, Jitsi links, webhooks and Swedish customer emails for free, in about 150 MB, with no limit on users. Its weak points are no buffers, no reminders, no meetings with several hosts, a cancellation that leaves the meeting in the customer's calendar, CalDAV passwords stored in plain text, a calendar on the same server that it refuses, an image built once per release, and a dated look. We have no guide for it yet: the files in How we ran Easy!Appointments are what we ran, on top of the Podman guide. It runs comfortably on a VC-P Alloy, next to other apps.
Finding a time for a group: Rallly. For a board meeting, a workshop or a dinner, it is quick for everyone, needs no account to vote, and gets time zones and Swedish right. Its weak points are that it is not a booking page, has no API, webhooks or calendar sync on your own server, needs a paid licence for a second user, needs working email to sign in, at least until each person sets a password, and keeps invitees' details in the database after you delete a poll. The files are in How we ran Rallly. A VC-P Alloy runs it with room to spare.
Both at once used under 1 GB even at their peaks, so one VC-P Alloy runs them side by side, as ours did.
Cal.com: wait. Until Cal.diy publishes images again and updates them, we do not recommend running it for customers.