by ridafkih
Open-source calendar sync tool & universal calendar MCP server. Aggregate, sync and control calendars on Google, Outlook, Office 365, iCloud, CalDAV or ICS.
# Add to your Claude Code skills
git clone https://github.com/ridafkih/keeper.sh
Keeper is a simple & open-source calendar syncing tool. It allows you to pull events from remotely hosted iCal or ICS links, and push them to one or many calendars so the time slots can align across them all.
If you encounter a bug or have an idea for a feature, you may open an issue on GitHub and it will be triaged and addressed as soon as possible.
High-value and high-quality contributions are appreciated. Before working on large features you intend to see merged, please open an issue first to discuss beforehand.
The dev environment runs behind HTTPS at https://keeper.localhost using a Caddy reverse proxy with automatic TLS. The .localhost TLD resolves to 127.0.0.1 automatically per RFC 6761 — no /etc/hosts entry is needed.
bun install
No comments yet. Be the first to share your thoughts!
The dev environment runs behind HTTPS via Caddy. You need to generate a local root certificate authority and trust it so your browser accepts the certificate.
mkdir -p .pki
openssl req -x509 -new -nodes \
-newkey ec -pkeyopt ec_paramgen_curve:prime256v1 \
-keyout .pki/root.key -out .pki/root.crt \
-days 3650 -subj "/CN=Keeper.sh CA"
Then trust it on your platform:
macOS
sudo security add-trusted-cert -d -r trustRoot \
-k /Library/Keychains/System.keychain .pki/root.crt
Linux
sudo cp .pki/root.crt /usr/local/share/ca-certificates/keeper-dev-root.crt
sudo update-ca-certificates
bun dev
This starts PostgreSQL, Redis, and a Caddy reverse proxy via Docker Compose, along with the API, web, MCP, and cron services locally. Once running, open https://keeper.localhost.
| Service | Local Port | Accessed Via |
| -------- | ---------- | ------------------------------------ |
| Caddy | 443 | https://keeper.localhost |
| Web | 5173 | Proxied by Caddy |
| API | 3000 | Proxied by Web at /api |
| MCP | 3001 | Proxied by Web at /mcp |
| Postgres | 5432 | postgresql://postgres:postgres@localhost:5432/postgres |
| Redis | 6379 | redis://localhost:6379 |
Because I needed it. Ever since starting Sedna—the AI governance platform—I've had to work across three calendars. One for my business, one for work, and one for personal.
Meetings have landed on top of one-another a frustratingly high number of times.
I've probably tried it. It was probably too finicky, ended up making me waste hours of my time having to delete stale events it didn't seem to want to track anymore, or just didn't sync reliably.
Events are flagged as having been created by Keeper either using a @keeper.sh suffix on the remote UID, or in the case of a platform like Outlook that doesn't support custom UIDs, we just put it in a "keeper.sh" category.
I've made Keeper easy to self-host, but whether you simply want to support the project or don't want to deal with the hassle or overhead of configuring and running your own infrastructure cloud hosting is always an option.
Head to keeper.sh to get started with the cloud-hosted version. Use code README for 25% off.
| | Free | Pro (Cloud-Hosted) | Pro (Self-Hosted) | | --------------------- | ---------- | ------------------ | ----------------- | | Monthly Price | $0 USD | $5 USD | $0 | | Annual Price | $0 USD | $42 USD (-30%) | $0 | | Refresh Interval | 30 minutes | 1 minute | 1 minute | | Source Limit | 2 | ∞ | ∞ | | Destination Limit | 1 | ∞ | ∞ |
By hosting Keeper yourself, you get all premium features for free, can guarantee data governance and autonomy, and it's fun. If you'll be self-hosting, please consider supporting me and development of the project by sponsoring me on GitHub.
There are seven images currently available, two of them are designed for convenience, while the five are designed to serve the granular underlying services.
[!NOTE]
Migrating from a previous version? If you are upgrading from the older Next.js-based release, see the migration guide for environment variable changes. The new web server will also print a migration notice at startup if it detects old environment variables.
| Name | Service(s) | Description |
| ------------------------------ | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| DATABASE_URL | api, cron, worker, mcp | PostgreSQL connection URL.e.g. postgres://user:pass@postgres:5432/keeper |
| REDIS_URL | api, cron, worker | Redis connection URL. Must be the same Redis instance across all services.e.g. redis://redis:6379 |
| WORKER_JOB_QUEUE_ENABLED | cron | Required. Set to true to enqueue sync jobs to the worker queue, or false to disable. If unset, the cron service will exit with a migration notice. |
| BETTER_AUTH_URL | api, mcp | The base URL used for auth redirects.e.g. http://localhost:3000 |
| BETTER_AUTH_SECRET | api, mcp | Secret key for session signing.e.g. openssl rand -base64 32 |
| API_PORT | api | Port the Bun API listens on. Defaults to 3001 in container images. |
| ENV | web | Optional. Runtime environment. One of development, production, or test. Defaults to production. |
| PORT | web | Port the web server listens on. Defaults to 3000 in container images. |
| VITE_API_URL | web | The URL the web server uses to proxy requests to the Bun API.e.g. http://api:3001 |
| COMMERCIAL_MODE | api, cron | Enable Polar billing flow. Set to true if using Polar for subscriptions. |
| POLAR_ACCESS_TOKEN | api, cron | Optional. Polar API token for subscription management. |
| POLAR_MODE | api, cron | Optional. Polar environment, sandbox or production. |
| POLAR_WEBHOOK_SECRET | api | Optional. Secret to verify Polar webhooks. |
| ENCRYPTION_KEY | api, cron, worker | Key for encrypting CalDAV credentials at rest.e.g. openssl rand -base64 32 |
| RESEND_API_KEY | api | Optional. API key for sending emails via Resend. |
| PASSKEY_RP_ID | api | Optional. Relying party ID for passkey authentication. |
| PASSKEY_RP_NAME | api | Optional. Relying party display name for passkeys.