This project is dedicated to Joe Ipson, the original creator of DailyNotes, who passed away in the summer of 2025 after a courageous battle with cancer.
Joe was a kindred spirit who believed in the simple power of writing things down. He built DailyNotes to bring the mindful experience of a physical planner into the digital world. His vision was to create something personal, self-hosted, and beautifully simple.
This project continues in his memory. Every commit, every feature, every bug fix is a small tribute to a dear friend whose spirit lives on in the code he wrote and the ideas he shared.
Rest easy, Joe. We'll take it from here.
The idea for this app came from using my Hobonichi Techo planner every morning to write down what I needed to accomplish that day & using it for scratching down random thoughts and notes as the day went on. The closest thing I've seen to an app for replacing this system is Noteplan, but I don't use a Mac or an iOS device, and it's not self-hostable, so I decided to write my own.
To check your current version, open Settings in the app and look in the About section.
Since I had the need for keeping track of to-dos throughout the day, regular Markdown didn't work for me since it doesn't natively support tasks. So as an alternative I'm using Github Flavored Markdown (GFM). I really wanted it to feel like an actual text editor and not just a textbox, so I decided to use CodeMirror to handle all the input. Fira Code is used to provide font ligatures. Some other nice features include code highlighting, text/code folding, and a task list where you can toggle the status of any task from any date or note.
Joe had a vision for what DailyNotes could become before calling it a 1.0 release. I've done my best to interpret and implement those features, along with feature requests from GitHub issues that Joe was considering. Here's what makes DailyNotes a powerful daily planning tool:
- [ ] / - [x]), tables, code blocks, and moretag:, project:, and full-text querieswork/meetings, home/family)Cmd+K V (side-by-side) or Shift+Cmd+V (full screen)DailyNotes supports password recovery and passwordless sign-in via email. These features require SMTP configuration (see Environment Variables).
Once configured, you can use password recovery and magic link sign-in.
If you forget your password:
Sign in without entering your password:
| Feature | Description |
|---|---|
| Secure tokens | Cryptographically random, SHA-256 hashed |
| Rate limiting | 3 requests per email per hour |
| Short expiration | Reset: 1 hour, Magic link: 15 minutes |
| Single-use tokens | Each token can only be used once |
| Email enumeration prevention | Same response whether email exists or not |
| Encrypted email storage | Email addresses encrypted at rest (AES) |
Gmail (with App Password):
SMTP_HOST: smtp.gmail.com
SMTP_PORT: 587
SMTP_USER: [email protected]
SMTP_PASSWORD: your-app-password # Generate at myaccount.google.com/apppasswords
SMTP_FROM_NAME: DailyNotes
APP_URL: https://your-dailynotes-instance.com
Mailgun:
SMTP_HOST: smtp.mailgun.org
SMTP_PORT: 587
SMTP_USER: [email protected]
SMTP_PASSWORD: your-mailgun-password
SMTP_FROM_EMAIL: [email protected]
APP_URL: https://your-dailynotes-instance.com
Amazon SES:
SMTP_HOST: email-smtp.us-east-1.amazonaws.com
SMTP_PORT: 587
SMTP_USER: your-ses-smtp-username
SMTP_PASSWORD: your-ses-smtp-password
SMTP_FROM_EMAIL: [email protected]
APP_URL: https://your-dailynotes-instance.com
Important Notes:
APP_URL must match the URL users access DailyNotes from (for email links to work)DailyNotes supports Light, Dark, and System themes to match your preferred working environment.
| Theme | Description |
|---|---|
| 🌙 Dark | Default dark interface, optimized for low-light environments |
| ☀️ Light | Clean, bright interface with light backgrounds |
| 💻 System | Automatically follows your operating system's color scheme setting |
The System option automatically switches between light and dark themes based on your OS settings (e.g., macOS Dark Mode, Windows Dark Theme). This is perfect if you prefer dark mode at night and light mode during the day.
DailyNotes supports Mermaid diagrams in the markdown preview, allowing you to create flowcharts, sequence diagrams, class diagrams, and more directly in your notes.
Use a fenced code block with mermaid as the language:
```mermaid
graph TD
A[Start] --> B{Decision}
B -->|Yes| C[Do something]
B -->|No| D[Do something else]
C --> E[End]
D --> E
```
Diagrams are rendered in the HTML preview:
Cmd+K V (Mac) or Ctrl+K V (Windows/Linux)Shift+Cmd+V (Mac) or Shift+Ctrl+V (Windows/Linux)Mermaid supports many diagram types. Here are some examples:
| Diagram Type | Use Case | Example Syntax |
|---|---|---|
| Flowchart | Process flows, decisions | graph TD or graph LR |
| Sequence | API calls, interactions | sequenceDiagram |
| Class | Object relationships | classDiagram |
| State | State machines | stateDiagram-v2 |
| Entity Relationship | Database schemas | erDiagram |
| Gantt | Project timelines | gantt |
| Pie | Data distribution | pie |
| Git Graph | Branch visualization | gitGraph |
Diagrams automatically adapt to your app theme:
If a diagram has syntax errors, DailyNotes displays a helpful error message instead of breaking the preview. This makes it easy to debug and fix diagram issues.
For full syntax documentation and examples, visit the Mermaid documentation.
GET/POST /api/calendar_token (requires auth)./api/calendar.ics?token=<your_token>.DELETE /api/calendar_token.DailyNotes features a powerful syntax-based search that lets you quickly find notes using text queries.
| Syntax | Description | Example |
|---|---|---|
tag:value | Filter by tag | tag:meeting |
project:value | Filter by project | project:work |
t:value | Shorthand for tag | t:1on1 |
p:value | Shorthand for project | p:DN |
tag:"multi word" | Quoted values for spaces | tag:"code review" |
| Plain text | Search note content | budget report |
| Query | What it finds |
|---|---|
budget | All notes containing "budget" |
tag:meeting | All notes tagged "meeting" |
project:work tag:Q4 | Notes in "work" project with "Q4" tag |
tag:1on1 tag:feedback | Notes with both tags (AND) |
project:DN project:personal | Notes in either project (OR) |
tag:meeting notes agenda | Tagged "meeting" containing "notes" AND "agenda" |
tag: or project: to see suggestions from your existing tags/projects? button for a quick referenceDailyNotes supports hierarchical tag organization using / as a delimiter. This lets you create tag hierarchies like work/meetings, home/family, or projects/dailynotes/frontend.
Use the / character in your frontmatter to create nested tags:
---
title: Weekly Team Sync
tags: work/meetings, work/1on1, home/family
---
Nested tags appear as a collapsible tree in the sidebar:
| Display | Description |
|---|---|
▶ work | Collapsed parent tag (click arrow to expand) |
▼ work | Expanded parent showing children below |
meetings | Child tag indented under parent |
1on1 | Flat tag (no /) displayed inline at top |
/) are displayed inline at the top, wrapping as neededNested tags support hierarchical search - searching for a parent tag matches all its children:
| Search Query | Matches |
|---|---|
tag:work | Notes with work, work/meetings, work/1on1 |
tag:work/meetings | Only notes with exactly work/meetings |
tag:home | Notes with home, home/family, home/tech |
This makes it easy to search broadly (tag:work for all work-related notes) or specifically (tag:work/meetings for just meeting notes).
| Tags in Frontmatter | Sidebar Display |
|---|---|
tags: meeting, 1on1, review | meeting 1on1 review (inline) |
tags: work/meetings, work/reviews | ▼ work → meetings reviews |
tags: home/tech, home/family, work | ▼ home → family tech, work |
/ continue to work exactly as beforetag:work/ to see suggestions for nested tagsprojects/dailynotes/frontend/componentsDailyNotes includes an optional Kanban board view for organizing tasks with drag-and-drop support.
When enabled, the Tasks icon in the header changes to a columns icon. Click it to open the Kanban modal.
Use the >>column syntax at the end of any task to assign it to a specific column:
| Task Syntax | Column Assignment |
|---|---|
- [ ] Plain task | Defaults to "todo" |
- [x] Completed task | Defaults to "done" |
- [ ] In progress >>doing | Explicit "doing" column |
- [x] Done but in review >>review | Stays in "review" (explicit wins) |
Column names support letters, numbers, and hyphens: >>in-progress, >>stage-2, >>Q4
| Setting | Default Value |
|---|---|
| Default columns | todo, done |
| Configurable in | Settings → Kanban |
Add, remove, or reorder columns in the Settings panel.
Override columns for a specific note using YAML frontmatter:
---
title: Sprint Planning
kanban:
- backlog
- in-progress
- review
- done
---
- [ ] Design mockups >>backlog
- [ ] Implement API >>in-progress
- [ ] Write tests >>review
If you use a column that doesn't exist in your configuration:
| Scenario | Result |
|---|---|
Columns: [todo, done] | Default setup |
Task uses >>staging | Columns become [todo, staging, done] |
Task uses >>review | Columns become [todo, staging, review, done] |
New columns are automatically inserted before "done". To reorder, update the columns in Settings or note frontmatter.
>>column syntax is automatically updated in the markdown| Condition | Resulting Column |
|---|---|
No >>column + unchecked [ ] | todo |
No >>column + checked [x] | done |
Explicit >>column (any checkbox state) | The specified column |
Key point: Explicit >>column syntax always takes precedence over the checkbox state. This lets you have completed tasks in a "review" column.
Here are some screenshots of what it looks like:
Main editor:

Search page:

Task list:

The recommended way of running is to pull the image from Docker Hub.
| Environment Variable | Description | Default |
|---|---|---|
| API_SECRET_KEY | Used to sign API tokens. | Will be generated automatically if not passed in. |
| DATABASE_URI | Connection string for DB. | Will create and use a SQLite DB if not passed in. |
| DB_ENCRYPTION_KEY | Secret key for encrypting data. Length must be a multiple of 16. Warning: If changed data will not be able to be decrypted! | Will be generated automatically if not passed in. |
| PREVENT_SIGNUPS | Disable signup form? Anything in this variable will prevent signups. | False |
| BASE_URL | Used when using a subfolder on a reverse proxy | None |
| PUID | User ID (for folder permissions) | None |
| PGID | Group ID (for folder permissions) | None |
| DEFAULT_TIMEZONE | Optional TZ name (e.g., America/Denver) for external ICS events; falls back to server local time | None |
| SMTP_HOST | SMTP server hostname for sending emails (password reset, magic links) | None (email features disabled) |
| SMTP_PORT | SMTP server port | 587 |
| SMTP_USER | SMTP username/email for authentication | None |
| SMTP_PASSWORD | SMTP password or app-specific password | None |
| SMTP_USE_TLS | Use TLS for SMTP connection (true or false) | true |
| SMTP_FROM_EMAIL | From address for sent emails | SMTP_USER value |
| SMTP_FROM_NAME | Display name for sent emails | DailyNotes |
| APP_URL | Public URL of your DailyNotes instance (used in email links) | http://localhost:8000 |
| Volume Name | Description |
|---|---|
| /app/config | Used to store DB and environment variables. This is not needed if you pass in all of the above environment variables. |
By default, the easiest way to get running is:
docker run -p 8000:8000 -v /config_dir:/app/config xhenxhe/dailynotes
Here is a complete docker-compose example with all configuration options:
services:
dailynotes:
image: xhenxhe/dailynotes:latest
container_name: DailyNotes
ports:
- '8000:8000'
volumes:
# Persistent storage for database and config
- ./dailynotes-data:/app/config
environment:
# Required: Secret key for signing JWT tokens
# Generate with: openssl rand -hex 32
API_SECRET_KEY: 'your-secure-api-secret-key-here'
# Required: Encryption key for data at rest (must be multiple of 16)
# Generate with: openssl rand -hex 32
# WARNING: Changing this will make existing data unreadable!
DB_ENCRYPTION_KEY: 'your-secure-db-encryption-key-here'
# Optional: Database connection string
# Default: SQLite database in /app/config/app.db
# Examples:
# PostgreSQL: postgresql://user:password@host:5432/dailynotes
# MySQL: mysql+pymysql://user:password@host:3306/dailynotes?charset=utf8mb4
# DATABASE_URI: "sqlite:////app/config/app.db"
# Optional: Prevent new user signups (set to any value to disable)
# PREVENT_SIGNUPS: "true"
# Optional: Base URL when using a reverse proxy subfolder
# Example: If accessing via https://example.com/notes, set to "/notes"
# BASE_URL: ""
# Optiona
Content type
Image
Digest
sha256:0b3bc9dd9…
Size
72.9 MB
Last updated
7 months ago
docker pull xhenxhe/dailynotes