Open-source project
lennart-k/rustical avatar
lennart-k/rustical

RustiCal: a CalDAV and CardDAV server that keeps its data in one file

a calendar server aiming to be simple, fast and passwordless

555 stars48 forksRustAGPL-3.0

At a glance

What is it?
RustiCal is a Rust implementation of CalDAV and CardDAV aimed at small self-hosted setups, built around a single SQLite database, a one binary container image and optional OpenID Connect. The author still labels it as under active development, and the calendar semantics support is only partial.
Who is it for?
RustiCal fits a household or small team that wants its own calendar and address book without operating a full groupware stack, and it is a poor fit for anyone who needs server side scheduling, sharing rules or long term support guarantees. Before adopting it, check the timezone handling you actually rely on, because the RFC 7809 support is only partial, and confirm that the single SQLite file backed by version 0.16.6 fits your backup routine.
Can I use it commercially?
Yes, with strict conditions. AGPL-3.0 is a network copyleft licence: if people use a modified version over a network, for example as a hosted service, you must offer them its source code under the same licence.
Is it still maintained?
Yes. The repository last received commits 2 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

One SQLite file holds every calendar and contact

The design choice that shapes everything else in RustiCal is storage. The documentation states that everything is saved in one SQLite database, and that there is also an export feature in the frontend. For an operator this means a backup is a file copy rather than a dump of several stores, and a migration is a matter of moving that file to a newer version of the server.

The workspace layout reflects that split. The manifest lists rustical_store as a path dependency alongside rustical_store_sqlite, so the storage interface and the SQLite implementation are separate crates. That separation is what allows the export path to exist without tying it to the database engine, and it is also what makes the storage choice replaceable in principle.

There is a second consequence worth naming. SQLite is a single writer database, so calendar writes serialize behind one lock. The project description calls the server simple and fast, and the feature list calls it adequately fast, but the author is careful to add that no benchmarks exist, so the performance claim rests on design intent rather than measurement. Anyone sizing RustiCal for a busy shared instance should treat throughput as unknown rather than assume it away.

WebDAV Push is what makes DAVx5 feel instant

Ordinary CalDAV clients poll on a schedule, which shows up as lag between a phone and a laptop. RustiCal lists WebDAV Push among its features, and the workspace contains a dedicated crate, rustical_dav_push, wired in as a separate path dependency next to rustical_dav.

The effect is that DAVx5 can be told to push a notification to the server instead of waiting for its next poll, and the documentation describes the result as near instant synchronisation to DAVx5. The push mechanism is a delivery channel for change notifications, not a different sync protocol, so a client that does not speak it still synchronises through ordinary reporting queries.

The repository root also carries a compose.oidc.yml file next to the main Dockerfile, which tells you deployment is expected to be composed rather than hand rolled. The tested client list is broad enough to be useful when you are validating a sync path: DAVx5, Tasks.org, GNOME Accounts, GNOME Calendar, GNOME Contacts, Evolution, Apple Calendar, the Home Assistant CalDAV integration, Thunderbird and Chiri are all named, with Chiri credited to a specific contributor.

The container image contains exactly one binary

The Dockerfile builds on the official rust alpine image and ends in a scratch stage, which is unusual enough to look at closely. The final stage copies the compiled executable out of the builder and sets the container command.

dockerfile
FROM scratch
COPY --from=builder /usr/local/cargo/bin/rustical /usr/local/bin/rustical
CMD ["/usr/local/bin/rustical", "serve"]

Scratch has no shell and no package manager, so the resulting image is only the server binary plus the CA certificates copied from the builder stage, which the build needs for outbound TLS in OpenID Connect flows. The build comments explain that SQLite must be statically linked, otherwise the executable fails at startup with a missing file error, and the builder stage installs musl toolchain pieces and cargo-chef to cache dependency compilation. Platform selection is restricted to linux/amd64 and linux/arm64, and anything else fails the build early.

That single binary claim from the feature list is therefore literal. The runtime surface is one file, which makes image distribution and hardening simpler than for a stack that needs a web server, a database server and a scripting runtime side by side.

Running the server and pointing a client at it

The README does not carry installation steps in the usual copy and paste form. It points to the documentation site, and the repository ships a Justfile, a flake.nix and a Dockerfile, so the routes into the project are a container build, a nix flake, or a cargo install from a source checkout. There is no published package name to install on a workstation, so treat the container as the primary path.

Two configuration details are visible in the image itself. The data location is passed as an environment variable rather than a command line flag, and the container's default points at a file under the data directory.

dockerfile
ENV RUSTICAL_DATA_STORE__SQLITE__DB_URL=/var/lib/rustical/db.sqlite3

The doubled separator is the nesting of configuration sections rather than a typo, and it tells you that the storage settings are grouped, with the SQLite section carrying the database URL. Whatever volume you mount for that path becomes the unit you back up and the unit you copy when you move the instance to another host.

For the first real use, run the server, create a user, and register one client. The frontend covers account creation, calendar import from an existing file, and generation of an Apple configuration profile, so the first calendar can be set up without hand editing iCalendar files. What the documentation does not spell out is the exact frontend URL path or the environment variable names for the listen address and the admin secret, so read the installation pages before scripting a deployment.

OpenID Connect with a password login escape hatch

Authentication is the part of RustiCal that reaches beyond plain username and password. The feature list names OpenID Connect support, and it notes the option to disable password login entirely. The workspace includes rustical_oidc as its own crate and the root of the repository holds compose.oidc.yml, which suggests a container deployment with an identity provider beside the server.

The passwordless story is not limited to OIDC. A separate feature is the Nextcloud login flow, where authenticating through Nextcloud inside DAVx5 automatically generates an app token, so the phone does not have to hold a long term password for your server. For a small household that already runs Nextcloud, this is the cheapest path to a working mobile client.

Two boundaries are worth keeping in view. The Nextcloud flow depends on the client supporting it, so it is a convenience inside DAVx5 rather than a general rule for every client. And the repository contains a rustical_oidc crate and an OIDC compose file, but no settings for a specific provider, no claim about which flows are implemented, and no documentation of token lifetimes in the files you can see here. Confirm the exact claims against the OIDC chapter of the project documentation before you turn off password login, because turning it off without a working provider path locks you out of your own server.

Deleted calendars recover, and sharing is group based

Deletion is the operation that causes the most grief in a shared calendar, so the project treats it as recoverable. The feature list states that deleted calendars are recoverable, which means a mistaken removal is a state change in the database rather than the end of the data. That is a direct consequence of the single file design: the record survives, and it can be surfaced again.

Sharing is described as group based. The topic tags and crate names show the server handles both calendar and address book data, with rustical_caldav and rustical_carddav as separate crates and rustical_frontend providing the web interface. Group based sharing implies that access is granted to a group of users rather than attached to an individual address, which is the part you need to understand before importing an existing calendar, because the way principals are named decides who can read what.

The iCalendar handling is where the project is furthest from complete. The feature list calls RFC 7809 support partial, and explains what that means in practice: RustiCal will accept timezones by reference and will handle omitted timezones in objects. That is a deliberate tolerance for sloppy producers rather than a strict parser. The manifest lists rustical_ical as a crate and the dependency list includes a matching library entry for iCalendar work, but nothing in the visible files describes how ambiguous or floating times are surfaced to a client, and the partial support caveat applies to any calendar that carries unusual timezone data. Validate with a copy of your real calendar before you migrate the live one.

Where RustiCal stops being the right tool

The README opens with a warning that the project is under active development, and it says the author has been using it productively for more than half a year while still expecting bugs and rough edges. That is an honest signal rather than a disqualifier, but it changes the kind of commitment you are making. Version 0.16.6 sits on a 0.x line, and the release history shows three releases in a single month, v0.16.4 on 2026-09-22, v0.16.5 on 2026-10-05 and v0.16.6 on 2026-10-06. A rapid 0.x cadence means interface details can move between releases.

On the code side, the workspace manifest declares a rust-version of 1.98 and edition 2024, so building from source needs a current stable toolchain. The dependency list includes pbkdf2 with the phc feature and password-auth configured for argon2 and pbkdf2, which covers the password side of authentication, and the base64ct and uuid entries are the kind of utility crates that a security sensitive deployment should pin and re-review rather than inherit silently.

The clearest case against RustiCal is a requirement for server side scheduling, resource booking, or a stable support commitment, since nothing in the feature list or the crate layout points to those areas. A second case is a household that wants zero maintenance: even a single binary still means a database file, a backup, a certificate, and an update decision. A third case is a large shared deployment, where a single SQLite writer becomes the bottleneck the design cannot avoid.

On licensing, the manifest names AGPL-3.0-or-later for the workspace package and the repository carries an AGPL-3.0 licence file. If you expose a modified RustiCal to users over a network, the obligations that come with the AGPL deserve a proper reading by someone qualified to advise on it, and this review is not legal advice. The dependency list and a deny.toml file in the repository root suggest the supply chain is checked, which is a good sign, but it is not a substitute for your own review of what you ship.

Editorial conclusion

RustiCal fits a household or small team that wants its own calendar and address book without operating a full groupware stack, and it is a poor fit for anyone who needs server side scheduling, sharing rules or long term support guarantees. Before adopting it, check the timezone handling you actually rely on, because the RFC 7809 support is only partial, and confirm that the single SQLite file backed by version 0.16.6 fits your backup routine.

Frequently asked questions

How do I back up a RustiCal server?

Everything is saved in one SQLite database, so copying that single file is the backup operation. The frontend also provides an export feature if you would rather hold calendars in a more portable format.

Does RustiCal support OpenID Connect and passwordless login?

Yes, OpenID Connect is supported and password login can be disabled. There is also a Nextcloud login flow, where signing in through Nextcloud inside DAVx5 generates an app token automatically.

Which calendar clients are tested with RustiCal?

The list names DAVx5, Tasks.org, GNOME Accounts, GNOME Calendar, GNOME Contacts, Evolution, Apple Calendar, the Home Assistant CalDAV integration, Thunderbird and Chiri. WebDAV Push is supported for near instant synchronisation with DAVx5.

Is RustiCal suitable for production use right now?

The README carries a warning that RustiCal is under active development and that bugs and rough edges should be expected, while the author reports using it productively for more than half a year. The last push was on 2026-10-06, with the v0.16.6 release on the same day.

Official sources

  1. lennart-k/rustical on GitHub
  2. License: AGPL-3.0
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/lennart-k-rustical.svg)](https://hysenlabs.com/projects/lennart-k-rustical)

Was this useful?