Updated
Daily notes and todo lists in plain markdown
Daily notes in markdown: date-based file names, a short daily note template, shell shortcuts, and a markdown todo list you can search across every file.
A daily note in markdown is one file per day, named after the date as 2026-10-10.md and kept in a folder such as Daily/. Start each one from a short template, write the day’s log in it, and link out to the notes that own each topic. Any editor opens the files, and because the names start with the year, they sort into date order on their own.
Tasks live in the same files as checklist lines: - [ ] Call the bank for open and - [x] Call the bank for done. One search, such as rg "\[ \] " Daily/, lists every open task in the folder. Every script below was tested in bash and in Windows PowerShell.
Name each daily note after its date
Use YYYY-MM-DD.md, the ISO 8601 date format. It sorts correctly everywhere, and nobody misreads it as a US or European date. Put the weekday in the note’s heading, because a script that builds today’s path from the date cannot find 2026-10-10 Saturday.md.
The folder layout is the main decision. The format strings use Moment.js tokens, the syntax Obsidian’s Daily notes plugin writes to its settings file and Jotura reads from it.
| Layout | Example path | Format string |
|---|---|---|
| Flat | Daily/2026-10-10.md |
YYYY-MM-DD |
| Year and month | Daily/2026/10/2026-10-10.md |
YYYY/MM/YYYY-MM-DD |
A flat folder is the easiest to script against. Nesting keeps each listing to a month of files, and the full date in the filename still identifies a moved file. Match the layout your notes app expects, since app calendars and task lists only recognize notes at their configured path.
Daily note templates
A template is the starting text every new daily note gets. {{date}} is a placeholder replaced with the note’s date when the file is created. Jotura’s templates and Obsidian’s core Templates plugin both support {{time}}, {{title}} and custom formats. Jotura fills every variable in UTC and takes numeric tokens such as {{date:DD/MM/YYYY}}, and Obsidian also accepts names, as in {{date:dddd D MMMM}} for “Saturday 10 October”.
Each variant ends with Log, because the shell functions on this page append to the end of the file. A minimal one:
# {{date}}
## Tasks
## Notes
## Log
An end-of-day variant with review prompts:
# {{date}}
## Tasks
## Review
- What got done:
- What moves to tomorrow:
- What I learned:
## Log
A variant with YAML frontmatter, the block between --- lines that holds metadata:
---
date: {{date}}
tags: [daily]
---
# {{date}}
## Tasks
## Log
Jotura’s Tags pane reads the tags: key, Obsidian shows frontmatter as Properties, and the Dataview plugin can query any field in it.
Open and append to today’s note from the shell
These bash and zsh functions create today’s note from the template the first time any of them runs, so the template is always in place. Put them in ~/.bashrc or ~/.zshrc and change the two paths to match your folder. They use the year-and-month layout; for a flat folder, change the path in daily_file to $DAILY/$(date +%F).md.
DAILY="$HOME/notes/Daily"
TEMPLATE="$HOME/notes/templates/daily.md"
OPEN='^[[:space:]]*([-*+]|[0-9]+[.)]) \[ \] '
daily_file() {
local f="$DAILY/$(date +%Y/%m/%F).md"
mkdir -p "$(dirname "$f")"
[ -f "$f" ] || sed "s/{{date}}/$(date +%F)/g" "$TEMPLATE" > "$f"
printf '%s\n' "$f"
}
today() { "${EDITOR:-nano}" "$(daily_file)"; }
note() {
local f; f=$(daily_file)
[ -n "$(tail -c 1 "$f")" ] && echo >> "$f"
printf -- '- %s %s\n' "$(date +%H:%M)" "$*" >> "$f"
}
today opens the note in your editor. note Called the bank about the card adds - 14:15 Called the bank about the card to the bottom of the file without opening anything. The tail -c 1 check adds a line break first when the file does not end with one, so the entry never joins the previous line.
PowerShell 7 on Windows gets the same pair. Add them to the file that $PROFILE points to:
$Daily = "$HOME\notes\Daily"
$Template = "$HOME\notes\templates\daily.md"
function Get-DailyNote {
$d = Get-Date
$f = Join-Path $Daily ('{0:yyyy}\{0:MM}\{0:yyyy-MM-dd}.md' -f $d)
if (-not (Test-Path $f)) {
New-Item -ItemType Directory -Force (Split-Path $f) | Out-Null
(Get-Content -Raw $Template).Replace('{{date}}', $d.ToString('yyyy-MM-dd')) |
Set-Content -Path $f -NoNewline
}
$f
}
function today { & ($env:EDITOR ?? 'notepad') (Get-DailyNote) }
function note {
$f = Get-DailyNote
$text = Get-Content -Raw $f
$sep = if ($text -and -not $text.EndsWith("`n")) { "`n" } else { '' }
Add-Content -Path $f -NoNewline -Value ("{0}- {1:HH:mm} {2}`n" -f $sep, (Get-Date), ($args -join ' '))
}
Both versions write straight to the file. A note open with unsaved edits keeps whatever the editor saves next.
Watch for UTC dates
date +%F and PowerShell’s Get-Date give your local date. JavaScript’s toISOString(), date -u and machines set to UTC (Coordinated Universal Time), such as servers, CI runners and Docker containers, give the UTC date. In New York that date rolls over at 8pm in summer and splits an evening across two notes. Pass the date in explicitly or set the TZ environment variable, as in TZ=Europe/London date +%F.
Use the daily note as a landing pad
Write in the daily note as things happen, with a link to the note that owns each subject. An entry that grows past a few lines moves to its own note, with a link left behind. A line such as Fixed the login timeout on [[Project A]] keeps the detail in the project note and the date in the journal. Any app with backlinks, or grep -rl "Project A" Daily/, then shows every day you touched the project. The wikilinks and backlinks guide covers the link syntax.
Tasks follow the same path: capture them in today’s note and move project tasks into the project’s note during a weekly review. A weekly note named for the ISO week, such as Weekly/2026-W41.md, holds that review. Run the open-task search below, paste in what needs a decision, and record what you dropped.
Markdown todo list syntax
A markdown todo list is a list item with a bracket pair after the bullet. A space inside the brackets means open, and an x means done.
- [ ] Draft the quarterly report
- [x] Book the venue
- [X] Send the invite
* [ ] Asterisk bullets work too
1. [ ] So do numbered items
- [ ] An indented task is a subtask
- A plain bullet, which is not a task
The syntax comes from GitHub Flavored Markdown (GFM). The GFM spec defines the marker as a space, x or X between brackets, so - [] with no space is not a task. A strict CommonMark renderer, which lacks the extension, shows the brackets as literal text. Keep each task on one line, because line-based searches miss wrapped text.
GitHub lets you tick the boxes in issues and comments, and shows an issue’s task progress in the repository’s issue list. VS Code’s built-in Markdown preview renders them once you install the Markdown Checkboxes extension. Jotura and Obsidian tick them in the editor.
Extended task states
Some apps read other characters in the brackets. These four extra states are a common set:
| Marker | Meaning | Jotura | Obsidian, no plugins | GitHub |
|---|---|---|---|---|
- [/] |
In progress | Set from the editor’s right-click menu, kept out of the Tasks pane | Ticked box | Plain text |
- [-] |
Cancelled | Same | Ticked box | Plain text |
- [>] |
Forwarded to a later day | Same | Ticked box | Plain text |
- [?] |
Question | Same | Ticked box | Plain text |
Obsidian’s help says any character in the brackets marks a task complete, and themes or CSS snippets can style each one. The Tasks plugin lets you define each character as a custom status to filter on. In Dataview, checked is true for any character and completed only for x.
A due-date convention search can use
Plain text has no fields, so a due date is a convention you choose and keep. Use YYYY-MM-DD inside it, because that format sorts as text and lets a search match a month by prefix.
| Convention | Example | Read by |
|---|---|---|
@due(...) |
- [ ] Renew passport @due(2026-11-02) |
Jotura’s Tasks pane |
| Dataview inline field | - [ ] Renew passport [due:: 2026-11-02] |
Obsidian Dataview, and Obsidian Tasks since version 3.3.0 |
| Calendar emoji | A calendar emoji, then 2026-11-02 |
Obsidian Tasks, its default format |
| Bare text | - [ ] Renew passport due 2026-11-02 |
Search only |
The formats do not read each other: Jotura ignores the Tasks plugin’s markers, and the Tasks plugin ignores @due(...). Pick the one your app reads. The searches below match both @due(...) and the Dataview field, and for the emoji format you put the emoji in the pattern.
Find every open task across your files
ripgrep (rg) is a free command-line search tool you install separately. It skips hidden folders such as .obsidian/, .trash/ and .jotura/ by default, which keeps deleted notes and templates out of the results. Run these from the top of your notes folder:
# Every open task, with file and line number
rg -n --glob "*.md" "^\s*([-*+]|\d+[.)]) \[ \] " .
# Open tasks due in November 2026, in either date format
rg -n --glob "*.md" '\[ \] .*(@due\(|due:: )2026-11' .
# Overdue tasks
rg --no-heading -o --glob "*.md" '\[ \] .*(?:@due\(|due:: )(\d{4}-\d{2}-\d{2}).*' -r '$1 $0' . \
| awk -F: -v today="$(date +%F)" '$2 < today'
grep ships with macOS and Linux. Searching * with --exclude-dir='.*' keeps it out of hidden folders at every level:
grep -rEn --include="*.md" --exclude-dir='.*' "^[[:space:]]*([-*+]|[0-9]+[.)]) \[ \] " *
On Windows, PowerShell needs no install. The Where-Object line drops any path with a dot-folder in it, such as Jotura’s .jotura/trash/:
# Every open task
Get-ChildItem -Recurse -Filter *.md |
Where-Object { $_.FullName.Substring($PWD.Path.Length) -notmatch '[\\/]\.' } |
Select-String -Pattern '^\s*([-*+]|\d+[.)]) \[ \] '
# Overdue tasks
$today = Get-Date -Format 'yyyy-MM-dd'
Get-ChildItem -Recurse -Filter *.md |
Where-Object { $_.FullName.Substring($PWD.Path.Length) -notmatch '[\\/]\.' } |
Select-String -Pattern '\[ \] .*(?:@due\(|due:: )(\d{4}-\d{2}-\d{2})' |
Where-Object { $_.Matches[0].Groups[1].Value -lt $today }
Line-based search treats a checkbox inside a fenced code block as a real task, shows a subtask without its parent, and cannot tick anything off.
Roll unfinished tasks forward
This bash function finds the most recent earlier daily note that still has open tasks, skipping empty notes and notes created ahead of time for future days. It copies those tasks under today’s ## Tasks heading and marks the originals [>]. It uses daily_file and OPEN from the functions above. On Windows, run it in Git Bash or WSL.
rollover() {
local f last
f=$(daily_file)
last=$(find "$DAILY" -name '????-??-??.md' | sort -r | awk -v t="$f" '$0 < t' |
while IFS= read -r p; do grep -qE "$OPEN" "$p" && { echo "$p"; break; }; done)
[ -n "$last" ] || return 0
awk -v src="$last" '
FILENAME == src { if ($0 ~ /^[ \t]*([-*+]|[0-9]+[.)]) \[ \] /) carry = carry $0 "\n"; next }
{ print }
/^## Tasks/ && !done { printf "%s", carry; done = 1 }
END { if (!done) printf "%s", carry }
' "$last" "$f" > "$f.tmp" && mv "$f.tmp" "$f"
sed -i.bak -E 's/^([[:space:]]*([-*+]|[0-9]+[.)])) \[ \] /\1 [>] /' "$last" && rm "$last.bak"
}
A second run carries nothing new, because the originals are already marked. A note without a ## Tasks heading gets the tasks at the bottom. The sed -i.bak form works with GNU sed on Linux and BSD sed on macOS. Delete the sed line to leave the originals open for tools that do not understand [>].
A task carried for three weeks needs a decision of its own: schedule it, move it to a project note, or delete it. A text file also cannot remind you of anything, so a deadline that needs an alert belongs in a calendar, with a link to the note.
Daily notes and tasks in Jotura
Jotura is a free notes app for macOS, Windows and Linux that opens a folder of markdown files as a vault, including an existing Obsidian vault. Its Android app reaches notes through Jotura Sync, which is paid, and there is no iOS app.
The desktop calendar sits in the right sidebar. Clicking a day creates its note, and Create upcoming daily notes in Settings pre-creates 1 to 60 days ahead. Notes go to Daily/2026/10/2026-10-10.md by default. On Android, Quick capture appends a line to today’s daily note.
A vault with Obsidian’s .obsidian/daily-notes.json uses the folder, format and template from that file, and Jotura never writes to it. Formats built from YYYY, YY, MM, M, DD and D are followed exactly; other tokens, such as MMMM, make Jotura use its own layout. A vault that already holds Jotura daily notes keeps that layout until the Obsidian folder has daily notes of its own.
The Tasks pane collects [ ], [x] and [X] lines from every note, with any list marker at any indent. A task’s date comes from an @due(YYYY-MM-DD) marker, or from its daily note’s date. Tasks fall into Overdue, Today, Upcoming, No date and Done, and clicking one rewrites only its checkbox characters in the file.
Its limits:
- Dates and calendar dots come only from notes at the configured layout. A flat
Daily/2026-10-10.mdin a vault with nodaily-notes.jsongets no calendar dot, and its tasks get no date from the filename. - The pane shows a rolling window, 30 days by default and adjustable from 1 to 365. A task appears when its daily note falls in the window, its file changed in the window, or its date is in the future. The
rgsearch remains the full sweep. - A ten-character
@duevalue that is not a real date, such as@due(2026-13-01), drops every task in that note from the pane. - There are no recurring tasks and no query blocks, and no plugin system to add them.
- The desktop calendar applies a template to new daily notes only when
daily-notes.jsonnames one.
The bundled jotura command-line tool appends to today’s note and lists tasks from a shell, with jotura today resolving dates in UTC. The daily notes, tasks and templates docs cover every flag, and coming from Obsidian covers what carries over from an existing vault.
How Obsidian’s Tasks and Dataview plugins approach it
In Obsidian, the community plugins Tasks and Dataview turn checklist lines into live, filtered views inside a note. Both search every note in the vault, and ticking a box in a view writes the change back to the source file.
Tasks adds query blocks in near-plain English. This one lists every open task in your daily notes due today or earlier:
```tasks
not done
due before tomorrow
path includes Daily
```
Tasks also creates the next copy of a recurring task, written with a rule such as every week, when you complete the current one, and it tracks start, scheduled, done and cancelled dates. Dataview runs queries such as TASK WHERE !completed, filtered on any field you add.
Nothing else on this page offers recurring tasks or arbitrary queries, so stay with Obsidian and these plugins when you rely on them. The views exist only inside Obsidian with the plugins installed, and any other app shows a query block as a code block.
Frequently asked questions
Does markdown checkbox syntax work everywhere?
- [ ] and - [x] render as checkboxes in Jotura, in Obsidian and on GitHub. They come from GitHub Flavored Markdown, an extension the original Markdown and CommonMark specs leave out, so a strict CommonMark renderer shows the brackets as text. Extended states such as [/] and [-] have narrower support.
Should I keep tasks in one todo.md file or spread across notes?
Spread them across daily notes and project notes, where each task sits next to the information it needs. A search such as rg -n "\[ \] " . gathers them into one list whenever you want to see everything. A single todo.md suits a short list with little context behind each item.