How I organize my notes app in 2024

Personnal process documentation

Notes on how I organize notes in Joplin.

Joplin is my main productivity tool. It completely replaced my paper-based GTD stack that I used for about a decade to manage all my tasks, projects and ideas.

In this post, I describe how I organize Projects, Areas, Resources and Archives — from the PARA method — in Joplin. I’ll show how I integrated the Getting Things Done (a.k.a. GTD) and Bullet Journal (a.k.a. BuJo) methods to manage my tasks and captured notes as well.

Table of Contents

Metrics

The ‘Synchronization Status’ page, under the ‘Help’ menu, provide some statistics about the total number of items in my Joplin database.

Here’s the count of objects by type:

  • Notes: 1506
  • Folders (a.k.a. Notebooks): 256
  • Resources: 561
  • Tags: 9
  • Tagged notes: 70

Top-level notebooks

Here’s my list of top-level notebooks and their size (as of 2024-11-30). I also included the emoji I selected to represent them in the UI.

  • 📥 0 Inbox (193)
  • 👷🏻 0 Tasks (113)
  • 🏗️ 1.1 Projets personnels (6)
  • 🚀 1.1 Projets sysadmin (9)
  • 🗓️ 1.2 Projets activités (14)
  • 1.3 Projets Grafana (29)
  • ✍🏻 1.4 Projets écriture (2)
  • 🚁 2 Areas (386)
  • 🗃️ 3 Références (405)
  • 🗄️ 4 Archives (279)

The PARA method

I follow the PARA method to organize my notes into Projects, Areas, Resources and Archives notebooks.

Learn more about PARA from Forte Labs:
https://fortelabs.com/blog/para/

There are many great videos on getting started with implementing PARA in your notetaking app. Joplin is not represented, but that’s okay, because PARA is software-agnostic by design.
https://fortelabs.com/blog/how-to-implement-para-in-your-favorite-notetaking-app/

My ‘0 Inbox’ and ‘0 Tasks’ notebooks

For tasks and captured notes, I integrated some components of the GTD and BuJo methods in Joplin.

PARA folders are commonly prefixed with numbers 1 to 4, wich keeps them in order of actionability. For example, ‘1 Projects’ are more actionable than ‘4 Archives’. Following this logic, tasks and captured notes are more actionable than Projects, therefore, I use the ‘0’ prefix so that these folders get sorted at the top.

Under ‘0 Inbox’, I have my ‘BuJo’ notebook, and several ‘Incubation’ notebooks, to hold on to Someday/Maybe items, tend to ideas and make them grow into actual projects.

Notebooks structure

Here is how I structure my Notebooks in Joplin. This list contains Notebooks, no Notes are represented.

  • 📥 0 Inbox
    • BuJo
    • Incubation
    • Incubation activités
    • Incubation écriture
    • Incubation Grafana
    • Incubation Riya
    • Incubation sysadmin
    • Meeting notes
  • 👷🏻 0 Tasks
    • Accounting
    • Errands
    • Reading
  • 🏗️ 1.1 Projets personnels
  • 🚀 1.1 Projets sysadmin
  • 🗓️ 1.2 Projets activités
  • 1.3 Projets Grafana
  • ✍🏻 1.4 Projets écriture
  • 🚁 2 Areas
    • Accounting
    • Blogging
    • Car
    • Fitness
    • 🧑🏻‍💻 Grafana Labs
      • Benefits
      • People
      • Professional Services
        • Clients
        • Lab environments
        • PS team
      • Workstation
    • Microblogging
    • Montréal
    • Poetry
    • ❤️ Riya
    • Santa Fe
    • Self
      • Checklists
      • Historique
    • Systems Administration
      • Backups
      • Home server
      • Laptop bootstrapping
      • Phone
      • Runbooks
      • Services
  • 🗃️ 3 Références
    • Accounting
    • Aikido
    • Art
      • Art and science inspiration
      • Artists
      • Writing
    • Astronomy
    • Career skills
    • Dance
    • Degoogling
    • Games
    • Horticulture
      • Flowers
      • Herbs
    • Information technology
      • Data science
      • Programming
      • Systems administration
    • Outdoors
      • Backpacking
      • Camping
      • Herbology
      • Hiking
      • Knots
      • Maps
      • Meteorology
    • 🧭 Places
      • Albuquerque
      • Montreal
      • New York City
      • Santa Fe
      • Taos
    • Productivité
    • Psychologie
    • 🥗 Recettes
      • Cocktails
      • Deserts
      • Ferments
      • Health
      • Repas
      • Repas bengali
      • Salads
      • Dips, dressings
      • Soups
    • Software
      • Android
      • Joplin
      • Kubernetes
    • Vin
  • 🗄️ 4 Archives
    • Archives Administration de systèmes
    • Archives Blogging
    • Archives Grafana
    • Archives Personnel

As you can see, I split my Projects directories into multiple top level categories. This makes it easier to find relevant projects based on context.

BuJo

Here’s what my BuJo notes in Joplin look like. We’re looking at one work-related BuJo note. This is how I manage all my tasks.

Left column: notebooks tree structure.
Middle column: content of my '0 Inbox/BuJo' notebook.
Right column: my most current work tasks list.
Left column: notebooks tree structure.
Middle column: content of my ‘0 Inbox/BuJo’ notebook.
Right column: my most current work tasks list.

Notes:

  • I don’t create a “task” note for every single actionable task that I have. Instead, I use the task list formating.
    • In Markdown, task lists have lines starting with - [ ] or - [x] for incompleted and completed items, respectively.
    • Using lists allows me to easily group, reorder, and add formatting like bold or highlighting to items.
    • I reorganize notes daily. Sometimes multiple times in a day.
  • All BuJo Notes are “Task” type in Joplin.
    • When the Note is unchecked, it means there are incomplete tasks or unprocessed notes therein.
    • When all tasks have been completed or migrated, and all notes have been processed or organized, then I check the Joplin note.
    • Eventually, I’ll review the checked notes, to make sure they don’t contain information I want to keep.
    • To close out old BuJo notes, I may migrate content to newer notes and mark the old one as checked.
  • Note titles start with the date of creation (yyyy-mm-dd), followed by keywords to identify the context.
    • ‘p’ means “Personal”.
    • ‘gl’ is work related (Grafana Labs).
    • ‘groceries’, self-explanatory.

Plugins

These are the plugins I install and configure on Joplin Desktop.

Tags

Sometimes, when I’m traveling or going through a very intense logistical process, I need to have several notes on “quick dial”. That’s when I use tags. I’ll tag notes for my itinerary, schedule, checklists, ticket scan codes, things like that.

Resources

Any file type can be attached to Joplin notes. My notes attachments consist mainly of images and PDFs, which can be displayed inline in a note.

Notes attachments are saved in the backend as ‘resources’. My resources directory contains 562 files, totaling 274.2MB, which I find quite manageable. I’m careful about storing large attachments, and only storing what I want to refer to later, but I wouldn’t mind if my resources grew to a few gigabytes.

Properties of my Joplin 'resources' directory.
Properties of my Joplin ‘resources’ directory.

Sync and backup architecture

This diagram shows how I use synchronization and backup features to use Joplin across devices and keep my data secure.

Notes:

The ~/Backups/ directory happens to be one of my top-level directories, which is synchronized to my Nextcloud account, and backed up to a remote Borg repository. See How I organize my digital files in 2024 for more information.

Randomnote

I like this idea of a button to bring up a random note from Joplin. This was before ForteLabs removed some free content from the blog. Here is the archived article:
https://web.archive.org/web/20221208130825/https://fortelabs.com/blog/p-a-r-a-iii-building-an-idea-generator/

The script randomnote.py:

#!/bin/env python3

import argparse
import logging
import random
import socket
import subprocess
import sys

import requests


excluded_notebooks_titles = [
    "0 Inbox",
    "4 Archives",
    ]
hostname = socket.gethostname()
if hostname == "electron":
    token = "aaaa"
elif hostname == "higgs":
    token = "bbbb"
elif hostname == "quark":
    token = "cccc"
params = {
    'token': token,
    }


def main(dry_run=False):
    try:
        ping_joplin()
    except:
        logging.error("Joplin is not running.")
        if not dry_run:
            subprocess.run(["notify-send", "randomnote.py: Joplin is not running."])
        sys.exit(1)

    all_notes = list_notes()
    notebooks = list_notebooks()

    global excluded_notebooks_ids
    excluded_notebooks_ids = set()
    for notebook in notebooks.values():
        recurse_parent_notebook(notebook, notebooks)

    logging.debug("Excluded notebooks:")
    for n in excluded_notebooks_ids:
        logging.debug(notebooks[n]['title'])

    valid_notes = []
    for note in all_notes.values():
        if note['parent_id'] in excluded_notebooks_ids:
            notebook = notebooks[note['parent_id']]
            logging.debug(f"Excluded note: {notebook['title']} / {note['title']}")
        else:
            valid_notes.append(note)

    random_note = random.choice(valid_notes)
    notebook = notebooks[random_note['parent_id']]
    print(f"Random note: {notebook['title']} / {random_note['title']}")

    if not dry_run:
        subprocess.run(["xdg-open", "joplin://x-callback-url/openNote?id={}".format(random_note['id'])])


def ping_joplin():
    r = requests.get("http://localhost:41184/ping")
    r.raise_for_status()


def list_notes(page=1):
    params['page'] = page
    r = requests.get("http://localhost:41184/notes", params=params)
    r.raise_for_status()
    data = r.json()
    all_notes = {}
    for note in data['items']:
        all_notes[note['id']] = note
    if data['has_more']:
        all_notes.update(list_notes(page+1))
    return all_notes


def list_notebooks(page=1):
    params['page'] = page
    r = requests.get("http://localhost:41184/folders", params=params)
    r.raise_for_status()
    data = r.json()
    notebooks = {}
    for notebook in data['items']:
        notebooks[notebook['id']] = notebook
    if data['has_more']:
        notebooks.update(list_notebooks(page+1))
    return notebooks


def recurse_parent_notebook(notebook, notebooks):
    is_excluded = False
    logging.debug(notebook)
    parent_id = notebook['parent_id']
    if parent_id:
        logging.debug("Recursion")
        is_excluded = recurse_parent_notebook(notebooks[parent_id], notebooks)
        if is_excluded:
            excluded_notebooks_ids.add(notebook['id'])
    elif notebook['title'] in excluded_notebooks_titles:
        excluded_notebooks_ids.add(notebook['id'])
        is_excluded = True
    if is_excluded:
        logging.debug(f"{notebook['title']} is excluded.")
    return is_excluded


if __name__ == "__main__":
    parser = argparse.ArgumentParser(
        description="Opens a random note in Joplin."
        )
    parser.add_argument("--dry-run", "-n",
        help="Select a random note but don't launch Joplin.",
        action="store_true",
        )
    parser.add_argument("--debug", "-d",
        help="Increase verbosity level.",
        action="store_true",
        )
    args = parser.parse_args()
    if args.debug:
        log_level = logging.DEBUG
    else:
        log_level = logging.INFO
    logging.basicConfig(
        level=log_level,
        )
    logging.debug("Log level set to DEBUG.")
    main(dry_run=args.dry_run)

The desktop application file joplin-randomnote.desktop:

[Desktop Entry]
Version=1.1
Type=Application
Name=Random Note
Comment=Joplin random note
StartupWMClass=Joplin
Icon=/home/alex/.local/share/icons/joplinrandomnote.png
Exec=/home/alex/Programs/personal/randomnote.py
Actions=
Categories=Office;
NoDisplay=false

The application icon:

Application desktop icon for my `randomnote.py` script.
Application desktop icon for my randomnote.py script.
Alexandre de Verteuil
Alexandre de Verteuil
Senior Solutions Architect

I teach people how to see the matrix metrics.
Monkeys and sunsets make me happy.

Related