Local-first, for real
Every device keeps a full copy of your tasks. The app opens instantly, works on a plane, and syncs the moment it sees your server again.
Open source · AGPL-3.0 · Self-hosted
An open-source, local-first task manager you host yourself. Everything you expect from a polished task app, without the subscription, the tracking or the lock‑in.
Go ahead, it works. Press q to add a task, j/k to move, c to complete.
All clear. Enjoy your day. 🌿
Why OpenTodo
Every device keeps a full copy of your tasks. The app opens instantly, works on a plane, and syncs the moment it sees your server again.
Capture, triage and finish without touching the mouse, or switch on Vim mode. Try the keys below.
A single SQLite file in one volume. Scheduled backups to a folder, S3 or WebDAV; JSON export any time.
One distroless container, no database server, no Redis. Happy on a Raspberry Pi or the cheapest VPS.
The app talks only to your server. No telemetry, no crash reporters, no third-party fonts. The server calls out only to what you configure.
Estimates on every task, Day and Week calendars with time blocks, reminders with web push, and recurring rules you can read.
Invite your household or team to your server. Share projects as viewer, editor or admin; assign tasks, comment, @mention, follow the activity.
Two-factor codes, passkeys, single sign-on, an audit log, and optional end-to-end encryption so the server only sees ciphertext.
REST endpoints and API tokens write through the same op log as the app. Create a task with curl and watch it land on your phone within a second or two. CalDAV, webhooks, a CLI and a read-only MCP endpoint build on the same data.
# add a task from a script curl -X POST $OPENTODO/api/v1/tasks \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"title":"Renew passport","priority":1}'
Every feature started as an OpenSpec change. Behaviour is written down before it is built, so you can read exactly how it works.
Sync that just converges
Every change is a tiny operation on one field, stamped with a hybrid logical clock. Devices queue them while offline and exchange them when they reconnect. The same rule runs everywhere, so every device lands on the same state. Play with it:
Toggle Wi-Fi on a device, change something, then reconnect.
Tip: click a task title to rename it, click the circle to complete it.
What you get today
Real screenshots of the app, seeded with demo data. Pick a chapter, then a feature.


The same app, the same offline copy of your data. The detail panel slides over the list; the sidebar tucks behind a menu. Share pages into it from the share sheet or the browser extension. No app store, no account on anyone else's server.
The honest comparison
Self-host in 2 minutes
Point Coolify at the repository, or run one command. Open the URL, create the owner account, done. Updates are a redeploy; migrations run on start.
# In Coolify: + New Resource → Public Repository Repository https://github.com/pixari/opentodo Branch main Build pack Dockerfile Port 8080 Storage volume → /data # Add your domain, press Deploy. That's it.
$ git clone https://github.com/pixari/opentodo.git $ cd opentodo $ docker compose up -d # → open http://localhost:8080
# needs Go 1.26+ and Node 22+ $ make build $ ./bin/opentodo # serves on :8080, data in ./data
Shipped
Each feature started as an OpenSpec change: a proposal, behaviour specs with testable scenarios, a design and a task list, validated before a line of code was written. Those changes are now implemented and archived, and their specs are the live description of how OpenTodo behaves. Click any item to read it.
Read the roadmap → · Browse the live specs → · Archived changes →
Deploy OpenTodo tonight. Your tasks will still be yours in ten years.