A calendar for people who have too many calendars.

Work, family, the school, the on-call rota, two clients and a conference feed, and a month grid that chops every long thing into weekly fragments. meercal draws time as one continuous strip instead, so a three-week trip is one bar you can follow, and twenty calendars fit on one screen without any of them being drawn twice. Self-hosted, Apple Calendar and CalDAV, everything in PostgreSQL.

Long events, drawn as long events

Every calendar app wraps time every seven days. It is why the thing you are actually in the middle of (the trip, the freeze, the fortnight the Kita is shut) is the hardest thing on the screen to see.

One bar, however many weeks

The Ribbon does not wrap: days run down the page continuously, and a long event is a single unbroken bar in a rail beside them. Overlapping ones are packed into parallel lanes the way a commit graph packs branches, so what you are inside of is answerable at a glance.

“Day 4 of 19”

Each bar's label is sticky: it rides down beside whatever day you are reading and counts where you are inside the event. Scroll into the middle of a nineteen-day trip and the rail says so, instead of leaving you to work it out from the dates.

Empty days cost one line

Runs of days with nothing on them collapse to a single “8 quiet days” line, so a whole month of several calendars fits on a screen. Week boundaries keep their rule and their ISO week number. Losing the rhythm of the week was the one thing a continuous view could not afford.

Days run down the page without wrapping. Long events are unbroken bars in the rail beside them; quiet days collapse to one line.

What makes meercal different

Built for the person who has a calendar for everything, and needs to see all of them at once without them turning into soup.

Sets, and solo

You do not think in calendars, you think in situations. Group them into sets (Work, Family, the release) and switch with a number key; 0 always means everything. A set is something you keep, so it is something you can change: rename it, move its key, add the calendar you only started using last week. Alt-click any calendar to solo it, the way a layer solo works in an editor; alt-click again and the previous set comes back.

Apple Calendar, CalDAV, Google

iCloud with an app-specific password (the family calendar included), plus Nextcloud, Fastmail, Radicale, SOGo or anything else that speaks CalDAV. Google over OAuth, and any secret .ics address as a read-only feed. Sync tokens mean a quiet calendar costs one request per pass.

Your passwords stay on your machine

meercal splits in two, exactly as meerail does. A small agent runs on your own machine and owns the whole write path to every calendar server; the web app runs in Docker and only ever reads the database. The two share nothing but Postgres, so there is no code path that could send a credential anywhere.

Reminders, and the lunch problem

A reminder can land on your desktop, on your phone as a push, or on your actual phone as a call, and a rule is a filter string with a lead time, not a second query language: cal:work is:busy, an hour before and ten minutes before. Since a rule is a statement about a kind of event, the event has the last word: the bell in the panel sets each channel to auto, on or off, and off beats everything, so “never call me about lunch” is one bit that stays true against rules you have not written yet. It runs in the agent, where the credentials already are.

Filter like you search code

Words are ANDed, a quoted phrase matches whole, and the filters are the nouns of a calendar: cal:family, with:anna, in:berlin, is:span, is:recurring, is:free. Turn on Regex and the pattern goes to Postgres as a POSIX regular expression over an indexed column. Enter searches every calendar, including the ones you have hidden.

Places you keep typing

Half the locations in a calendar are the same handful: the office, that room, the same meeting link. Put them in meercal.toml as name-and-value pairs and they are offered as chips under the event panel's Where field, in the order you wrote them.

Clashes, marked

Two meetings overlapping on one calendar is usually deliberate. The same across two calendars is the double booking you meant to notice, so meercal marks it, on the event and in the margin of the day, rather than leaving you to spot it.

Everything in PostgreSQL

Events are stored as the server sent them, and recurrence is expanded into rows over a rolling horizon, so drawing a fortnight is one index scan whatever the number of calendars, and years of your time are a table you can point psql at.

Keyboard first

t for today, r w m d y for the views, g then a month number to jump, 09 for calendar sets, / to filter, n for a new event, q to expand the quiet days. The cheat sheet in the sidebar is generated from the same table that binds the keys, so it cannot drift.

It knows your mail

Point meercal at a meerail database and the attendee field autocompletes from the people you actually correspond with: meerail's address book is built from every message you have, ranked by how often. Read-only: meercal never writes to your mail.

The views you already know

Week, month and day are all there, and they are the right drawing for “what is my Tuesday like”. They just do not chop the long things up: bars run continuously across the week, and a bar that continues past the edge says so. The year opens with every long event of the year laid across one twelve-month axis. The wheel pages between them: in the week and day grids it scrolls the hours first and pages when it runs out. Light and dark, following the system or pinned.

On a phone

The same app, laid out for the width: the sidebar becomes a drawer behind the ☰, the view switch gets a row of its own rather than falling off the end of the toolbar, and Create is a floating button. The Ribbon is the view that gains most from a narrow screen (it is a single column by nature), and the week grid scrolls sideways instead of squeezing seven columns into forty pixels each.

See it

Real screenshots of a running instance. Click any of them to see it full size.

The Ribbon
Week
Month
Year
Calendar sets
Filtered to the long ones
Search
An event

Get started

One script, three containers, and your calendars. You need Docker with the Compose plugin, nothing else, on any operating system.

1. Run the installer

It checks Docker, asks where your calendars live, picks a free port and pulls the published images. No clone, no build, no Python. Everything it writes lands in ~/.meercal, and your events live in a Docker volume.

curl -fsSL https://raw.githubusercontent.com/ribalba/meercal/main/meercal.sh -o meercal.sh
bash meercal.sh

2. Point it at your calendars

The installer asks, one account at a time: iCloud with an app-specific password, any CalDAV server, a published .ics feed, or Google. Add more later with meercal.sh config.

bash meercal.sh test
bash meercal.sh sync
bash meercal.sh logs agent

3. That is it

The agent is a container here too: it talks to CalDAV servers out on the internet, so there is nothing on your machine it needs to reach. Your passwords stay in ~/.meercal/meercal.toml, mode 0600, mounted read-only into the two containers that read it, which is also why they run as you rather than as root.

bash meercal.sh status
bash meercal.sh update
bash meercal.sh backup

Or run it as a desktop app

The same Electron shell meerail and meerato have. It adds the two things a browser tab cannot: it tells the page when the window is behind another one (not hidden, just not the app you are in), so meercal stands its polling down and reloads when you come back, and it spell checks the event panel.

make desktop
cd electron
make distinstall

No calendars to hand? bash meercal.sh demo fills it with a week worth looking at (seven calendars, a nineteen-day trip, an on-call rota and a double booking), so you can see the Ribbon before you hand over a password. Prefer a checkout? make up builds the same stack from the source.