Your lists, in plain files, one tap away.
Natlas is a small self-hosted app for the lists that run your life: todos and reminders, bills and renewals, birthdays, races and workouts, the things you own. It installs on your phone like an app, and every list is a plain YAML or CSV file you can open in any editor, sync with your notes and change with your own scripts.
This is a private instance - there is no public demo. Natlas is free and open source; the steps below get your own copy running in about five minutes.
☀️ Today screen
Overdue, today and the next 7 days from every list in one place, each with a Done button - plus the numbers that matter, like monthly spend or birthdays this week.
✅ One tap
Done, Tomorrow, Cancel, Completed: each list has its own buttons. Tap a badge such as open or high to change it without opening a form.
👆 Swipe & select
Swipe right for the main action, left to edit. Long-press to select many rows and close, reschedule or re-tag them together.
↩️ Undo everything
Every change - an edit, a Done, a delete, a bulk update - shows an Undo button for a few seconds. Mis-taps cost nothing.
⚡ Quick add
Type Call bank tomorrow #todo !high and the title, date, tag and priority are filled in for you. Weekdays, “12 oct”, “25/12” and “+3” all work.
🧩 Plugins in YAML
A list is one plugin.yml: fields, dropdowns, sections, buttons, Today rules and totals. Add a new kind of list without writing code.
📄 Plain files
No database. Each list is one human-readable file next to your notes - greppable, diffable, and safe to edit by hand or from Obsidian.
🤝 Script-friendly
Only the record you change is rewritten. Comments, unknown fields and file order are kept, and an edit that would overwrite a script's change is refused.
📱 Installable app
Bottom navigation, full-screen forms and shortcuts on your phone; a side panel on the desktop. Opens offline with your last data, read-only.
🔒 Private by design
Single user, hashed password, signed cookies, login lockout, strict browser policies, no third-party requests and no tracking.
🪶 Tiny
One Go binary of about 8 MB with the web app built in. Runs in a 10 MB Docker image as your own user, with a read-only filesystem.
🌗 Light & dark
The nss teal theme follows your system, with a manual switch. Large tap targets, readable on any screen.
Lists it ships with
| List | File | What you get |
|---|---|---|
| 📅 Events | event.md | Todos and reminders grouped Overdue / Today / Next 7 days / Later / Closed. Done, Tomorrow and Reopen buttons, smart quick add. |
| 💳 Subscriptions | subscription.md | Bills and renewals, “due in 7 days”, and true monthly spend across any cycle: monthly, yearly, “84 days”, “3 years”. |
| 🎁 Birthdays | birthdays.md | Read-only, sorted by the next birthday, with the age each person turns. |
| ❤️ Health | health.md | Planned races (a Completed button asks for time and cost and moves the race to history), race history, gym routine in your own order, and a profile. |
| 📦 Inventory | inventory.csv | What you own, its condition and where it is kept, with value and weight totals by place, type or condition. |
Turn lists on or off and choose their order in natlas.yml. Each one is just a folder under plugins/.
What it stores
An event is a few plain lines. Natlas, your editor and your scripts can all read and write it.
updated: 2026-10-07 02:10:00 events: - id: renew-passport title: Renew passport date: '2026-10-12' status: open # open | pending | closed priority: high # high | medium | low tags: todo frequency: none # none | daily | weekly | monthly | quarterly | yearly note: Book a slot first
Install
You need a Linux box with Docker and a reverse proxy that serves HTTPS (Caddy, Traefik, nginx, or a Cloudflare tunnel).
-
Get the code
git clone https://gitlab.com/niharokz/natlas.git cd natlas cp .env.example .env cp natlas.example.yml natlas.yml
-
Set your login
Build the image, then create a password hash and a secret key and paste both into
.env:docker compose build read -rs PW && printf '%s' "$PW" | docker run --rm -i natlas:local hash-password openssl rand -hex 32 # → NATLAS_SECRET_KEY
Also set
NATLAS_USERNAME,NATLAS_DATA_DIR(the folder with your files),TZ,PUID/PGID(fromid -u/id -g) andNATLAS_NETWORK(the Docker network your proxy is on). -
Check and start
docker compose run --rm natlas check # lists every config mistake, with file and key docker compose up -d --build docker logs natlas # "natlas … listening on :8080"
Missing data files are created on the first save;
examples/data/has samples of every format. -
Put it behind HTTPS
natlas.example.com { reverse_proxy natlas:8080 }Open the site, sign in, then use your browser's Install / Add to Home Screen.
-
Update later
git pull && docker compose up -d --build
Edited a
plugin.ymlornatlas.yml?docker compose restart natlasis enough.
Just looking? With Go 1.24+, sh scripts/dev.sh runs it on http://localhost:8080 against a throwaway copy of the example data (login demo / demo).
Configure
.env - secrets and machine
- NATLAS_USERNAME
- the single login
- NATLAS_PASSWORD_HASH
- from
natlas hash-password(or a plainNATLAS_PASSWORD) - NATLAS_SECRET_KEY
- signs the session cookie, 32+ characters
- NATLAS_DATA_DIR
- host folder with your list files
- TZ · PUID · PGID
- timezone for “today”; the user that owns the files
- NATLAS_NETWORK
- Docker network shared with your proxy
natlas.yml - the app
title: Natlas currency: "₹" locale: en-IN data_dir: /data plugins: # menu order - events - subscriptions - birthdays - health - inventory lists: # shared dropdowns rooms: [Kitchen, Bedroom]
Add your own list
Create plugins/books/plugin.yml, add books to natlas.yml, restart. That is the whole plugin:
title: Books
icon: box
file: books.md
collections:
- id: books
path: books
fields:
- { key: title, type: text, required: true }
- { key: author, type: select, options: data } # learns values from your file
- { key: status, type: select, options: [to read, reading, read], default: to read }
- { key: finished, type: date }
list:
title: title
subtitle: [author, finished]
badges: [status] # tap to change in place
groups:
- { label: Reading, where: { status: reading } }
- { label: Read, where: { status: read }, sort: [-finished], collapsed: true }
actions:
- { id: finish, label: Finished, primary: true, set: { status: read, finished: today } }
quick_add: { title: title }
widgets:
- { label: Reading, count: { status: reading } }
Field types: text, textarea, number, money, date, select, bool, list, url and anniversary.
Conditions use eq ne in lt lte gt gte empty contains plus any / all / not,
with dates like today+7. The README documents every option.
Security
- The password can be kept as a PBKDF2-SHA256 hash instead of plain text; credentials are compared in constant time.
- HMAC-signed, HttpOnly, Secure, SameSite cookie (
__Host-prefixed). Changing the password signs every session out. - Five wrong passwords lock that IP out for 30 minutes; the real client IP is read safely behind Cloudflare and proxies.
- Writes must be same-origin JSON - cross-site forms and requests are refused.
- Strict Content-Security-Policy, HSTS, no framing, no third-party scripts, fonts or analytics.
- Runs as your user in a read-only container with no extra privileges; it can only write to your data folder.
- Every change is logged with time and client IP - never with passwords or contents.