All apps · 0 apps
keepIT
Docker app from Richy1989's Repository
Overview
Readme
View on GitHub
Honestly? I just wanted a simple notes app, and couldn't find one with the three things I actually cared about — so I built it myself. With a little AI help 😉, modern problems require modern solutions.
The features I really wanted:
- a simple notes app with a modern web UI
- note sharing between different users
- a native Android app, including a home-screen widget
It has since grown into a blazing-fast app with optimistic editing, lists, search, sharing, and real-time sync — so a note edited on one device shows up on your others without a refresh. I'm really happy with how this turned out.
Status: work in progress.
Contents
- What you can do with it
- Run your own keepIT
- The Android app
- What's next
- For developers
- Support
- License
What you can do with it
- 📝 Write notes your way — quick text notes with rich formatting (bold, headings, lists, links, code), or checklists you tick off as you go.
- 🗂️ Stay organized — group notes into lists, pin the important ones to the top, archive what's done, and find anything instantly with search. Deleted notes wait in the trash until you're sure.
- 🎨 Make it yours — give any note a background color, and pick your own accent color for the whole app.
- ⏰ Reminders — remind yourself about any note, once or on a schedule (daily, weekly, monthly, yearly). On your phone they arrive as real notifications — even with the app closed, the screen locked, or no internet.
- 👥 Share notes — invite someone by email to view or edit a note with you. You each keep your own pins, lists, and reminders; edits show up for everyone, live.
- 🔄 Always in sync — change a note on one device and watch it update on your others, no refresh needed.
- 📱 Android app included — the same notes on your phone, with a home-screen widget for recent notes and one-tap capture. It works fully offline: read and edit anywhere, and your changes sync as soon as you're back online.
- 🔒 Your notes stay yours — keepIT is self-hosted. Everything lives on your server, no third-party cloud, no account with anyone but yourself.
Coming up: photos and images in notes — see What's next.
Run your own keepIT
keepIT runs on your own machine or home server with Docker. One command, no database to set up:
docker run -d \
--name keepit \
-p 8080:80 \
-v keepit-data:/data \
-e Jwt__Key="your-random-secret-at-least-32-chars" \
richy1989/keepit:latest
Then open http://localhost:8080 (or your server's address on port 8080) and create your account. That's it.
A few things worth knowing:
Jwt__Keyis a secret that keeps your sign-ins secure — replace it with any random string of at least 32 characters, and keep it the same across restarts.- Your data lives in the
keepit-datavolume — back that up and you've backed up your notes. - Once your accounts are created, you can close public sign-up by adding
-e App__AllowRegistration=false— recommended if your server is reachable from the internet. - Forgot password works without any mail server: the reset link is written to the server
log (
docker logs keepit), where you — the operator — can grab it. To have it emailed to users instead, configure SMTP with theEmail__*settings below. - Running Unraid? A Community Apps template is included at
deploy/keepit.unraid.xml.
Prefer Docker Compose, Postgres, or building the image yourself?
Docker Compose (three containers: app, web server, and a PostgreSQL database) — from a clone of this repo:
cp .env.example .env # set JWT_KEY (32+ chars), optionally POSTGRES_PASSWORD
docker compose up -d --build # builds everything locally, then starts the stack
Open http://localhost:8080. Data persists in named Docker volumes.
Use PostgreSQL with the single container instead of the built-in database — either the
discrete variables (POSTGRES_HOST is the switch; port/db/user default to 5432/keepit/keepit):
docker run -d \
--name keepit \
-p 8080:80 \
-v keepit-data:/data \
-e Jwt__Key="your-secret" \
-e POSTGRES_HOST=<host> \
-e POSTGRES_PASSWORD=<pass> \
richy1989/keepit:latest
…or a full connection string (takes precedence when both are set):
docker run -d \
--name keepit \
-p 8080:80 \
-v keepit-data:/data \
-e Jwt__Key="your-secret" \
-e "ConnectionStrings__Postgres=Host=<host>;Port=5432;Database=keepit;Username=keepit;Password=<pass>" \
richy1989/keepit:latest
Build the image yourself (no Docker Hub needed):
docker build -f deploy/Dockerfile -t keepit:local .
docker run -d --name keepit -p 8080:80 -v keepit-data:/data \
-e Jwt__Key="your-random-secret-at-least-32-chars" keepit:local
All settings (environment variables)
| Variable | Required | Default | Description |
|---|---|---|---|
Jwt__Key |
yes | — | Random secret, min 32 chars — keeps sign-ins secure. The variable the app actually reads; use it for docker run / Unraid / the single-container image. |
JWT_KEY |
compose only | — | Convenience .env value that docker-compose.yml passes through as Jwt__Key. Not read directly by the app. |
ConnectionStrings__Postgres |
no | (built-in SQLite) | Full Postgres connection string. If neither this nor POSTGRES_HOST is set, a zero-setup SQLite database is used. Takes precedence over the discrete POSTGRES_* variables below. |
POSTGRES_HOST |
no | — | Postgres host — the friendlier alternative to a full connection string: setting it switches the API to Postgres, built from the POSTGRES_* variables. |
POSTGRES_PORT |
no | 5432 |
Postgres port (discrete setup only). |
POSTGRES_DB |
no | keepit |
Postgres database name (discrete setup only). |
POSTGRES_USER |
no | keepit |
Postgres username (discrete setup only). |
POSTGRES_PASSWORD |
no | keepit |
Postgres password. Read by the API for the discrete setup, and by the Compose stack for both the db service and the connection string it hands the API. |
App__AllowRegistration |
no | true |
Whether new accounts may be created. On an internet-exposed instance: register your own accounts first, then set false to close public sign-up. |
App__DataRoot |
no | ./App_Data |
Directory for the database, security keys, and media. |
App__ForwardedProxyHops |
no | 1 |
Trusted reverse-proxy hops in front of the app — 1 for the plain setups above, 2 if you put another proxy (e.g. Traefik) in front. |
App__PublicBaseUrl |
no | (auto-detected) | Public address of your instance (e.g. https://notes.example.com), used to build password-reset links. Usually auto-detected from the request; set it if reset links point to the wrong host. |
Email__SmtpHost |
no | — | SMTP server for outgoing email (password-reset links). Leave empty to run without email — reset links then land in the server log. |
Email__From |
with SMTP | — | From address, e.g. keepIT <no-reply@example.com>. Required once Email__SmtpHost is set. |
Email__SmtpUsername |
no | — | SMTP login username. Leave empty (along with the password) for an unauthenticated relay. |
Email__SmtpPassword |
no | — | SMTP login password, paired with Email__SmtpUsername. |
Email__SmtpPort |
no | 587 |
SMTP port — 587 for STARTTLS submission, 465 for implicit TLS (set Email__UseStartTls=false too). |
Email__UseStartTls |
no | true |
true = STARTTLS (port 587); false = implicit TLS (port 465). |
Auth__RefreshCookie__Secure |
no | true (Compose) / false (single container) |
Sign-in cookie is HTTPS-only. Keep true behind TLS; set false only when serving plain HTTP on a non-localhost address (e.g. a LAN IP without TLS). |
Jwt__Issuer / Jwt__Audience |
no | keepITCore / keepIT.api |
Advanced: token claims. |
Jwt__AccessTokenMinutes / Jwt__RefreshTokenDays |
no | 15 / 14 |
Advanced: how long sign-in tokens last. |
ASPNETCORE_ENVIRONMENT |
no | Production |
Set to Development for verbose logging and the API explorer at /scalar/v1. |
The Compose stack sets most of these itself and reads only five values from .env: JWT_KEY,
POSTGRES_PASSWORD, REFRESH_COOKIE_SECURE, FORWARDED_PROXY_HOPS, and ALLOW_REGISTRATION.
The Android app
The app in app/ brings your notes to your phone: offline-first, live sync, native
reminder notifications, and a home-screen widget. It isn't on the Play Store (yet), so you build
and install it yourself — open app/ in Android Studio, or from the command line (requires
the Android SDK):
cd app
./gradlew :app:assembleDebug # build the APK
./gradlew :app:installDebug # or install straight onto a connected phone
On first launch, enter your server address on the sign-in screen — the same URL you open in
the browser (from the Android emulator, your own machine is http://10.0.2.2:5025). For
reminders that fire on the minute even while your phone sleeps, grant Alarms & reminders
in the app's Settings screen.
What's next
- 🖼️ Photos & images in notes — attach images, and use one as a note's background.
- 📤 Sharing from the phone — invite people to a note directly in the Android app.
- ✉️ Invite anyone — share a note with someone who hasn't signed up yet.
For developers
Interested in how it works or want to hack on it? ARCHITECTURE.md holds
the full design and reasoning. The short version: an ASP.NET Core (.NET 10) REST API + SignalR
for realtime, a React 19/TypeScript web app, and a Kotlin/Jetpack Compose Android app — all
speaking the same API, with the C# DTOs as the single source of truth for the contract (the
typed TS client is generated from OpenAPI: cd web && npm run generate:api).
Run it locally with the .NET 10 SDK and Node.js 22+ (no database setup needed — a SQLite dev database is created automatically):
# 1) Backend — http://localhost:5025 (Scalar API UI at /scalar/v1)
dotnet run --project keepIT/keepITCore
# 2) Frontend — http://localhost:5173 (proxies /api to the backend)
cd web && npm install && npm run dev
Open http://localhost:5173 and register an account — or seed test data
(test@test.com / Test1234#1234, plus lists and a variety of notes):
./scripts/seed-dev-data.sh # PowerShell twin: ./scripts/seed-dev-data.ps1
Support
keepIT is free and self-hosted — no accounts, no subscriptions. If it's useful to you and you'd like to say thanks, you can buy me a coffee ☕. Much appreciated, but never expected.
License
Released under the MIT License — © 2026 Richard Leopold. Free to use, modify, and distribute; just keep the copyright and license notice.
Install keepIT on Unraid in a few clicks.
Find keepIT in Community Apps on your Unraid server, review the template, and click Install. Unraid handles the Docker app or plugin setup from the published template.
Categories
Download Statistics
Related apps
Explore more like this
Explore allDetails
richy1989/keepit:latestRuntime arguments
- Web UI
http://[IP]:[PORT:80]/- Network
bridge- Shell
bash- Privileged
- false
Template configuration
Host port for the keepIT web UI. Browse to http://[Unraid-IP]:[this port].
- Target
- 80
- Default
- 8080
- Value
- 8080
Host folder where keepIT stores its SQLite database, encryption keys and uploaded images. Point this at your appdata share to persist data across updates.
- Target
- /data
- Default
- /mnt/user/appdata/keepit
- Value
- /mnt/user/appdata/keepit
REQUIRED. A random secret of at least 32 characters used to sign login tokens. Change this before first use; changing it later signs everyone out.
- Target
- Jwt__Key
- Value
- change-me-to-a-random-secret-of-at-least-32-characters
Production (default) or Development. Development also enables the Scalar API explorer at /scalar/v1.
- Target
- ASPNETCORE_ENVIRONMENT
- Default
- Production
- Value
- Production
Keep false when accessing over plain http. Set true only when keepIT is served over https (e.g. behind a reverse proxy with TLS).
- Target
- Auth__RefreshCookie__Secure
- Default
- false
- Value
- false
Number of reverse-proxy hops in front of the API. This image already contains one nginx hop, so: 1 = you reach keepIT directly (browser → this container); 2 = another reverse proxy such as Traefik sits in front (Traefik → nginx → api). Must be exact: too low and all clients share one rate-limit bucket; too high and clients can spoof their IP.
- Target
- App__ForwardedProxyHops
- Default
- 1
- Value
- 1
Whether new accounts may be created. If keepIT is reachable from the internet, register your own accounts first and then set this to false so strangers cannot sign up. Existing users keep working.
- Target
- App__AllowRegistration
- Default
- true
- Value
- true
Optional. Public address of your instance (e.g. https://notes.example.com), used to build password-reset links in emails. Usually auto-detected from the request; set it only if reset links point to the wrong host.
- Target
- App__PublicBaseUrl
Optional. SMTP server for outgoing email (password-reset links). LEAVE EMPTY to run without email — reset links then land in the container log instead. Setting this is the switch that enables email delivery (then also set From).
- Target
- Email__SmtpHost
From address for outgoing email, e.g. keepIT no-reply@example.com. Required once SMTP Host is set; ignored while SMTP Host is empty.
- Target
- Email__From
Optional SMTP login username. Leave empty (with SMTP Password) for an unauthenticated relay. Ignored while SMTP Host is empty.
- Target
- Email__SmtpUsername
Optional SMTP login password, paired with SMTP Username. Ignored while SMTP Host is empty.
- Target
- Email__SmtpPassword
SMTP port — 587 for STARTTLS submission (default), 465 for implicit TLS (set Use STARTTLS to false too). Ignored while SMTP Host is empty.
- Target
- Email__SmtpPort
- Default
- 587
- Value
- 587
true = STARTTLS on port 587 (default); false = implicit TLS on port 465. Ignored while SMTP Host is empty.
- Target
- Email__UseStartTls
- Default
- true
- Value
- true
Optional. LEAVE EMPTY to use the built-in SQLite database (recommended for testing). This field is the switch: empty = SQLite, filled = Postgres. Set it to your Postgres host/container to switch databases (then also set Postgres Password).
- Target
- POSTGRES_HOST
Optional Postgres port (defaults to 5432 if empty). Ignored while Postgres Host is empty.
- Target
- POSTGRES_PORT
Optional Postgres database name (defaults to 'keepit'). Ignored while Postgres Host is empty.
- Target
- POSTGRES_DB
Optional Postgres username (defaults to 'keepit'). Ignored while Postgres Host is empty.
- Target
- POSTGRES_USER
Optional Postgres password. Ignored while Postgres Host is empty.
- Target
- POSTGRES_PASSWORD
