itsnotes

# itsnotes A self-hosted Google Keep alternative. It sticks to the familiar masonry layout but overhauls how you actually navigate and organize a large amount of notes. Instead of an endless wall of text, the feed is broken down by month markers. There are quick-search panels for colors and tags, a basic folder implementation, and a proper list view. It also handles data import natively, so moving off Keep or importing text files is straightforward. **[Try the demo →](https://try.itsnotes.app)** itsnotes badge **Features:** - **Better navigation:** The timeline has month markers to break up the list. There are quick-access panels for colors, tags, saved searches, and a calendar. - **More ways to organize:** Basic folders, internal note linking, and the ability to rename color labels to whatever makes sense to you. - **View modes:** The standard masonry grid, a stacked view, and a proper dense list view. - **Customization:** A heavy settings modal to tweak layouts, page backgrounds, and form behaviors. - **Easy imports:** Drop in a Google Takeout `.zip` to import Keep data, or bulk upload `.txt` and `.md` files directly. - **Markdown mirror:** Two-way sync between your notes and a folder of `.md` files. Every note is continuously written out with a metadata header (tags, color, reminders, pin/archive/trash state) and images/attachments alongside, and edits you make to the files in any editor (Obsidian, VS Code) are pulled back into your notes — automatically if you want. The database stays the source of truth: conflicting edits are kept safe in a `conflicts/` folder and renames are tracked by note ID, never duplicated. Great for backups, grep, git, or living in Obsidian. - **Quality of life:** Built-in note history for revisions, plus automatic metadata fetching for books and movies. - **Optional AI stuff:** Hooks for auto-tagging and reminder parsing, plus a built-in MCP server so external AI clients can query your database. Built with React, Node.js, PostgreSQL, and Socket.io. ![grid-dark](docs/screenshots/grid-dark.png)
### On mobile
## Docker Setup Save this as `docker-compose.yml`: ```yaml name: itsnotes services: itsnotes-db: image: postgres:17-alpine restart: unless-stopped environment: POSTGRES_USER: itsnotesuser POSTGRES_PASSWORD: change-this-password POSTGRES_DB: itsnotes volumes: - postgres_data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"] interval: 10s timeout: 5s retries: 5 itsnotes-server: image: ghcr.io/alexmicuplusfour/itsnotes-server:latest restart: unless-stopped depends_on: itsnotes-db: condition: service_healthy environment: NODE_ENV: production PORT: 5000 DB_HOST: itsnotes-db DB_PORT: 5432 DB_USER: itsnotesuser DB_PASSWORD: change-this-password DB_NAME: itsnotes PUPPETEER_EXECUTABLE_PATH: /usr/bin/chromium-browser BACKUP_PATH: /app/backups # Markdown Mirror target — exported .md files appear in ./notes-mirror on the host MD_MIRROR_PATH: /data/notes-mirror volumes: - attachments_data:/app/uploads - backups_data:/app/backups # Browsable folder for the Markdown Mirror feature (turn it on in Settings) - ./notes-mirror:/data/notes-mirror itsnotes-client: image: ghcr.io/alexmicuplusfour/itsnotes-client:latest restart: unless-stopped depends_on: - itsnotes-server ports: - "80:80" volumes: postgres_data: attachments_data: backups_data: ``` Change the database password (and set any optional values — see [`.env.example`](.env.example)), then start it: ```bash docker compose up -d ``` The app will be available on port 80. ### With Caddy (HTTPS + domain) For automatic HTTPS, use [`docker-compose.caddy.example.yml`](docker-compose.caddy.example.yml) instead — it adds a Caddy service. You'll also need a `Caddyfile` ([`Caddyfile.example`](Caddyfile.example)) with your domain. Then `docker compose up -d`; Caddy handles SSL certificates automatically. ### Serving under a subpath itsnotes can share a domain with other apps by living under a path prefix, e.g. `https://example.com/itsnotes`. Set `BASE_PATH` on the client container and put a reverse proxy in front that strips the prefix before forwarding. The normal published image is used — changing `BASE_PATH` only needs a restart, not a rebuild. Leave `BASE_PATH` unset to serve at the root as usual. ```yaml itsnotes-client: image: ghcr.io/alexmicuplusfour/itsnotes-client:latest environment: BASE_PATH: /itsnotes ``` With Caddy, `handle_path` strips the prefix: ```caddyfile example.com { redir /itsnotes /itsnotes/ handle_path /itsnotes/* { reverse_proxy itsnotes-client:80 } } ``` For an existing Traefik installation, [`docker-compose.traefik.example.yml`](docker-compose.traefik.example.yml) is a complete example using Traefik's Docker provider — edit the placeholder domain, path, and network at the top of the file. ## Configuration All configuration is via the `environment:` blocks in `docker-compose.yml`. See `.env.example` for the full list of available options. Optional AI features (auto-tagging, OCR, summarization, reminder parsing) work with three providers, configured under **Settings → AI**: - **OpenAI** or **Anthropic** — needs an API key (`OPENAI_API_KEY` / `ANTHROPIC_API_KEY`, or just paste it in Settings). - **[Ollama](https://ollama.com)** — no API key. Local models keep note content on your own hardware; Ollama's cloud models send it to ollama.com. ### Local AI with Ollama Pick **Ollama** in Settings → AI, set the base URL, hit **↻ Refresh models**, and assign a model per feature: - itsnotes in Docker, Ollama on the host: `http://host.docker.internal:11434` - Ollama as a compose service (commented-out block in [`docker-compose.example.yml`](docker-compose.example.yml)): `http://ollama:11434` - Elsewhere on your network: `http://:11434` OCR requires a model with vision support. Quality depends entirely on the models you run. If summaries of long notes miss content or fail, raise Ollama's context length (`OLLAMA_CONTEXT_LENGTH`, see [Ollama's docs](https://docs.ollama.com/context-length)). ## REST API The same API the frontend uses is available to external scripts and apps. Log in with your credentials, call `POST /api/auth/api-token` to get a long-lived token, then pass it as a Bearer header on any request: ```bash curl https://your-instance/api/notes/search?query=yr:2024 \ -H "Authorization: Bearer " ``` See **[docs/api.md](docs/api.md)** for the full reference: endpoints, query params, search operators, and the note object schema. ## Chatting with your notes (MCP) There's a built-in [MCP](https://modelcontextprotocol.io/) server that lets Claude (and other AI clients) search and read your notes. Turn it on under **Settings → AI → MCP Server** and generate a token. To connect, paste the URL into Claude's custom connector, or run the `claude mcp add` command it gives you for Claude Code. It's off by default, read-only, and won't work without the token. ## Importing from Obsidian If you keep your notes in Obsidian, you can bring your entire vault over: 1. Zip your Obsidian vault folder (the folder that contains your `.md` files and the `.obsidian` config directory). 2. In itsnotes, open **Settings → Backup & Restore → Import from Obsidian** and select the `.zip`. Alternatively, you can select individual `.md` files if you only want to import a subset. What comes across: note titles, body text, frontmatter tags (including nested `project/sub` hierarchies), inline `#tags`, `[[wikilinks]]` (resolved to internal note links), `![[embedded images]]`, standard `![](images)`, `==highlights==`, task lists, and frontmatter fields for created/modified dates, pinned, and archived state. Obsidian comments (`%%...%%`) are stripped. A two-pass approach ensures wikilinks resolve correctly even when the linked note appears later in the import batch. ## Importing from Google Keep If you're moving over from Google Keep, you can bring your notes with you: 1. Go to [Google Takeout](https://takeout.google.com/) and request an export of just **Keep**. 2. Download the resulting `.zip` when it's ready. 3. In itsnotes, open **Settings → Backup & Restore → Import from Google Keep → Import from Takeout** and select the `.zip`. Notes, checklists, labels, colors, archive/trash/pin state, original timestamps, and images all come across. Image attachments land in each note's gallery, and any other attachments are brought over too. ## Alternatives - **[memos](https://github.com/usememos/memos)** — A lightweight, privacy-first hub for quick notes and memos. Leans toward a fast, microblog-style stream rather than a Keep-like board, and ships as a single Go binary. - **[Joplin](https://github.com/laurent22/joplin)** — A mature, full-featured note and to-do app with Markdown, end-to-end encryption, web clipper, and sync across desktop, mobile, and web. The heavyweight option if you want notebooks and offline apps everywhere. - **[Zen](https://github.com/sheshbabu/zen)** — A minimal, single-user self-hosted notes app focused on simplicity and staying out of your way. A good pick if you want something tiny and no-frills. - **[Karakeep](https://github.com/karakeep-app/karakeep)** — A self-hosted "bookmark everything" app for links, notes, and images, with automatic AI tagging and full-text search. More of a hoarding-and-recall tool than a Keep-style note board. --- This project is entirely 𝚟𝚒𝚋𝚎𝚌𝚘𝚍𝚎𝚍.