NeoVim ZK Plugin Tutorial
This tutorial walks you through building the devcontainer, connecting via SSH, and using the full Zettelkasten workflow: creating notes, linking them together, tethering untethered thoughts into permanent knowledge, searching, generating graphs, and managing todos.
Prerequisites
Section titled “Prerequisites”- Podman (or Docker, substituting
dockerforpodman) - An SSH client
- A terminal emulator (Ghostty recommended for full color support)
1. Build the Container
Section titled “1. Build the Container”Clone the repository and build the image:
git clone https://github.com/infrashift/zettelkasten.gitcd zettelkasten
podman build -f Containerfiles/Containerfile -t zk-devcontainer:latest .The build compiles the zk Go binary, installs NeoVim, Claude Code, tmux, and
all plugins. It takes a few minutes the first time.
2. Start the Container
Section titled “2. Start the Container”The container runs an SSH server on port 2222. You need to mount an
authorized_keys file so you can authenticate.
Using the included test key (for local development only):
podman run -d --name zk-dev \ --userns=keep-id \ -p 2222:2222 \ -v ./Containerfiles/config/test_ssh_key.pub:/home/user/.ssh/authorized_keys:ro,Z \ zk-devcontainer:latestUsing your own SSH key (recommended):
podman run -d --name zk-dev \ --userns=keep-id \ -p 2222:2222 \ -v ~/.ssh/id_ed25519.pub:/home/user/.ssh/authorized_keys:ro,Z \ zk-devcontainer:latestVerify the container started:
podman logs zk-dev# Should show: Starting sshd on port 2222...# Server listening on 0.0.0.0 port 2222.3. Connect via SSH
Section titled “3. Connect via SSH”With the test key:
ssh -p 2222 -i ./Containerfiles/config/test_ssh_key \ -o StrictHostKeyChecking=no user@localhostWith your own key:
ssh -p 2222 user@localhostYou land in a tmux session with three panes:
+---------------------------+------------+| | bash || NeoVim +------------+| | claude |+---------------------------+------------+- Left pane: NeoVim (your editor)
- Top-right pane: Bash shell
- Bottom-right pane: Claude Code AI assistant
Navigate between panes with your mouse (mouse mode is enabled) or with tmux
keybindings (Ctrl-b then arrow keys).
If you disconnect and reconnect, SSH reattaches to the same tmux session automatically. Your editor state, shell history, and Claude conversation are preserved.
4. Where Are Notes Stored?
Section titled “4. Where Are Notes Stored?”All notes live under ~/zk_vault/ inside the container, organized as:
~/zk_vault/ untethered/ Untethered notes (quick captures) <id>.md daily/ Daily notes 2026/ 02/ <id>.md tethered/ Tethered notes (refined knowledge) <id>.md .zk_index/ Full-text search index (auto-managed)Untethered notes are quick captures: ideas, meeting notes, snippets. They don’t require a project.
Tethered notes are refined, rewritten knowledge that you’ve decided to keep. They require a project tag.
Daily notes are date-stamped journal entries stored under untethered/daily/
in a YYYY/MM/ directory hierarchy.
Each note has an ID in the format YYYYMMDDHHmmss-UUIDv4, which combines a
timestamp for natural sorting with a UUID to prevent collisions.
To persist notes across container restarts, mount a host directory:
podman run -d --name zk-dev \ --userns=keep-id \ -p 2222:2222 \ -v ~/.ssh/id_ed25519.pub:/home/user/.ssh/authorized_keys:ro,Z \ -v ~/zk_vault:/home/user/zettelkasten:Z \ zk-devcontainer:latest5. Initialize Your Zettelkasten
Section titled “5. Initialize Your Zettelkasten”SSH into the container and switch to the bash pane (click it or press Ctrl-b
then right-arrow). Initialize the zettelkasten directory:
mkdir -p ~/zk_vaultcd ~/zk_vaultgit initgit config user.email "you@example.com"git config user.name "Your Name"The zk tool auto-creates the folder structure (untethered/, tethered/,
etc.) on first use.
6. Start Your Day with a Daily Note
Section titled “6. Start Your Day with a Daily Note”Switch to the NeoVim pane and create your first daily note:
:ZkDailyNeoVim opens today’s daily note. It has YAML frontmatter at the top and sections for your morning plan, tasks, notes, and end-of-day reflection:
---id: "20260219143000-a1b2c3d4-..."title: "Daily Note - 2026-02-19"type: daily-notecategory: untetheredtags: - dailycreated: "2026-02-19T14:30:00Z"---
# Daily Note - 2026-02-19
## Morning- [ ] ...
## Tasks- [ ] ...
## Notes
## End of Day
## Links CreatedFill in your morning plan. Add a few tasks. Save the file with :w. The note
is automatically indexed for search.
You can also view yesterday’s daily note:
:ZkDaily yesterdayOr browse all daily notes with a picker:
:ZkDailyList7. Create an Untethered Note
Section titled “7. Create an Untethered Note”Capture a quick idea. In NeoVim:
:ZkNoteYou are prompted for a title. Type Learning Zettelkasten Method and press
Enter. A new untethered note opens with frontmatter and an empty body. Write
some content:
The Zettelkasten method is a personal knowledge management systemdeveloped by Niklas Luhmann. Key principles:
- One idea per note (atomicity)- Write in your own words (elaboration)- Connect notes to each other (linking)- Use untethered notes for captures, tethered notes for refined ideasSave with :w.
8. Create a Note from a Template
Section titled “8. Create a Note from a Template”Templates give notes structure. Open the template picker:
:ZkTemplateAvailable templates: meeting, book-review, snippet, project-idea, user-story,
feature, daily, todo, issue.
Create a meeting note:
:ZkTemplate meetingEnter a title like Team Standup - Knowledge Management. The template provides
sections for attendees, agenda, discussion, action items, and next steps. Fill
it in and save.
Create a code snippet note:
:ZkTemplate snippetTitle it Bash - Find Files by Extension. Fill in the code block with a useful
snippet and save.
9. Link Notes Together
Section titled “9. Link Notes Together”Notes become powerful when connected. Open the meeting note you just created, place your cursor where you want a link, and insert one:
\l(\ is the local leader key followed by l)
A picker opens showing all your notes. Select Learning Zettelkasten Method and press Enter. A [[id]] link is inserted at your cursor.
To insert a link that shows the title (more readable):
\LThis inserts [[id|Learning Zettelkasten Method]] instead.
You can also insert links from the search picker. Run :ZkSearch,
highlight a note, and press Ctrl-l to insert its link at the cursor.
10. Create More Related Notes
Section titled “10. Create More Related Notes”To build a meaningful graph, create several connected notes. Here is a suggested
set. For each one, use :ZkNote, write content, and link to related notes using
\l:
Note 2: “Atomic Notes”
An atomic note contains exactly one idea, fully developed.This makes notes reusable across contexts.Link to: Learning Zettelkasten Method
Note 3: “Linking as Thinking”
The act of linking notes forces you to articulate the relationshipbetween ideas. This is where insight happens.Link to: Learning Zettelkasten Method, Atomic Notes
Note 4: “Progressive Summarization”
Layer highlights on top of notes over time. Bold the most importantpassages, then highlight the bold, then write a summary.Link to: Atomic Notes
Note 5: “Untethered vs Tethered Notes”
Untethered notes are raw captures. Tethered notes are refined ideasyou've rewritten in your own words with context and connections.Link to: Learning Zettelkasten Method, Atomic Notes
Note 6: “Graph Thinking”
A zettelkasten is a graph of ideas, not a hierarchy.Any note can connect to any other note.Link to: Linking as Thinking, Untethered vs Tethered Notes
After creating these, you have a small web of interconnected notes.
11. View Backlinks
Section titled “11. View Backlinks”Open the Learning Zettelkasten Method note. Several notes link to it. View
its backlinks:
\bA floating panel appears at the top-right showing every note that references this one. From the panel:
- Press
Enteroroto open a backlink - Press
pto preview it in a floating window - Press
qorEscto close the panel
Toggle the panel on and off with \b.
12. Preview a Note
Section titled “12. Preview a Note”Rendered Markdown with Glow
Section titled “Rendered Markdown with Glow”The devcontainer includes Glow, a terminal-based Markdown renderer. With a note open, run:
:GlowGlow renders the current buffer as styled Markdown in a floating window —
headings, bullet lists, code blocks, and links are all formatted for the
terminal. Press q to close the Glow preview.
Quick preview floating window
Section titled “Quick preview floating window”To peek at a note’s raw content without leaving your current file:
\pA floating preview window shows the note content. Inside the preview, press
Enter to open it for editing or q to close.
13. Tether a Note
Section titled “13. Tether a Note”The Learning Zettelkasten Method note has been refined and linked. Tether it
to permanent status. With the note open, press \t and select tether:
\t → Pick: tether / untetherYou are prompted for a project name. Enter knowledge-management. The note’s
frontmatter updates: category changes from untethered to tethered and the
project field is set.
You can set or change a project on any note with \p:
\p → Project: _14. Search Your Notes
Section titled “14. Search Your Notes”Quick search
Section titled “Quick search”:ZkSearch zettelkastenA picker shows matching notes. Select one and press Enter to open it,
or Ctrl-p to preview.
Live search (updates as you type)
Section titled “Live search (updates as you type)”:ZkSearch!The ! (bang) enables live search mode. Start typing and results filter in
real-time.
CLI search (from the bash pane)
Section titled “CLI search (from the bash pane)”Switch to the bash pane and search from the command line:
cd ~/zk_vault
# Full-text searchzk search "atomic notes"
# JSON output (for scripting)zk search --json --limit 5
# Filter by categoryzk search --category tethered
# Filter by tagzk search --tag daily15. Generate a Graph
Section titled “15. Generate a Graph”Visualize how your notes connect. In NeoVim:
:ZkGraph 20This generates a Mermaid flowchart showing up to 20
connected notes and opens it in a scratch buffer. Each node is labeled with its
title and every [[id]] link in note bodies becomes an arrow. Copy the output
into mermaid.live or any Mermaid renderer to see the
visual graph.
From the CLI:
zk graph ~/zk_vault --limit 20You can also start the tree from a specific note and control depth:
zk graph ~/zk_vault --start 20260219143000-a1b2c3d4 --depth 316. Manage Todos
Section titled “16. Manage Todos”Create a todo
Section titled “Create a todo”:ZkTodo Buy a notebook for handwritten zettel draftsOr with a due date and priority:
:ZkTodo Review meeting notes --due 2026-02-21 --priority highChange todo status
Section titled “Change todo status”With the todo open in the editor, press \s to open the status picker. Choose from:
open— not startedin_progress— being worked onclosed— completed
Generate a todo list
Section titled “Generate a todo list”Create a markdown summary of your todos:
:ZkTodoListThis generates a markdown summary and opens it in a split.
17. Use Templates for Structured Notes
Section titled “17. Use Templates for Structured Notes”Beyond the meeting and snippet templates shown earlier, try these:
Book review (creates a tethered note, requires a project):
:ZkTemplate book-review --project reading-listSections: author, rating, summary, key takeaways, favorite quotes.
Project idea:
:ZkTemplate project-ideaSections: problem statement, proposed solution, goals, non-goals, success metrics.
Feature spec:
:ZkTemplate featureSections: requirements, design, API changes, testing strategy, rollout plan.
Issue (bug report or enhancement):
:ZkTemplate issueSections: type (bug/enhancement/question), description, steps to reproduce, expected vs actual behavior.
18. Tag Completion
Section titled “18. Tag Completion”When editing a note’s YAML frontmatter, place your cursor in the tags:
section and press Ctrl-x Ctrl-t in insert mode. This triggers tag
autocompletion from all tags used across your zettelkasten.
Refresh the tag cache if you’ve added new tags externally:
:ZkRefreshTags19. Index Your Notes
Section titled “19. Index Your Notes”Notes are automatically indexed when you save them in NeoVim. To manually reindex everything:
:ZkIndexOr from the CLI:
zk index ~/zk_vault20. Day Start / Day End (Git Workflow)
Section titled “20. Day Start / Day End (Git Workflow)”The CLI includes two convenience commands for a daily git workflow:
Start your day:
zk helloThis pulls the latest main branch and creates a new branch named with today’s
date (e.g., 20260219).
End your day:
zk goodbyeThis stages all changes, commits with a dated message, merges to main, and cleans up the dated branch.
Quick Reference
Section titled “Quick Reference”NeoVim Commands
Section titled “NeoVim Commands”| Command | Description |
|---|---|
:ZkDaily | Open/create today’s daily note |
:ZkDaily yesterday | Open yesterday’s daily note |
:ZkDailyList | Browse daily notes |
:ZkNote | Create a new untethered note |
:ZkNote tethered | Create a new tethered note |
:ZkTemplate [name] | Create from template |
:ZkSearch [query] | Search notes |
:ZkSearch! | Live search (updates as you type) |
:Glow | Render current note as styled Markdown |
:ZkGraph [limit] | Show Mermaid graph |
:ZkTodo [title] | Create a todo |
:ZkTodoList | Generate todo list markdown |
:ZkIndex | Reindex all notes |
:ZkRefreshTags | Refresh tag cache |
Keybindings (in zettel markdown files)
Section titled “Keybindings (in zettel markdown files)”| Key | Action |
|---|---|
\l | Insert [[id]] link |
\L | Insert [[id|title]] link |
\b | Toggle backlinks panel |
\p | Set project (note and todo types) |
\t | Tether / Untether (note and todo types) |
\a | Add tags (all zettel types) |
\v | Validate frontmatter (all zettel types) |
\s | Set todo status (todo-type only) |
Ctrl-x Ctrl-t | Tag completion (insert mode) |
Picker Keys
Section titled “Picker Keys”| Key | Action |
|---|---|
Enter | Open selected note |
Ctrl-p | Preview selected note |
Ctrl-l | Insert link to selected note |
CLI Commands
Section titled “CLI Commands”| Command | Description |
|---|---|
zk create [title] | Create a note |
zk daily | Create/open daily note |
zk todo [title] | Create a todo |
zk todos | List todos |
zk set-status [file] [status] | Set todo status (open/in_progress/closed) |
zk search [query] | Search notes |
zk graph [path] | Show graph tree |
zk tether [file] | Tether a note |
zk untether [file] | Untether a note |
zk backlinks [file] | Show backlinks |
zk add-tags <file> <tag1> [tag2...] | Add tags to a zettel |
zk validate <file> | Validate frontmatter against CUE schema |
zk index [path] | Index notes |
zk templates | List templates |
zk hello | Start-of-day git workflow |
zk goodbye | End-of-day git workflow |
Container Management
Section titled “Container Management”# Stop the containerpodman stop zk-dev
# Start it again (notes and tmux session preserved)podman start zk-dev
# SSH reconnects to the same tmux sessionssh -p 2222 user@localhost
# Remove the containerpodman rm -f zk-dev
# Custom SSH portpodman run -d --name zk-dev \ --userns=keep-id \ -e SSH_PORT=3333 -p 3333:3333 \ -v ~/.ssh/id_ed25519.pub:/home/user/.ssh/authorized_keys:ro,Z \ -v ~/zk_vault:/home/user/zettelkasten:Z \ zk-devcontainer:latest