TL;DR
Use rsync and a launchdaemon configuration to keep a copy of notes remotely, leverage a cloud backup service like Google Drive/OneDrive/Dropbox as well.
Intro
I had a bunch of notes on my Mac that I moved away from Evernote after itâs been nerfed to only allow one device to access and sync notes. With this I decided to move to a more durable setup.
After moving to Obsidian as my current(tm) note taking app of choice (previously Evernote, previously Bear). I miss the sync aspect. Thereâs Obsidian Sync but that isnât necessarily allowed for my work laptop. I also didnât want to pay yet another subscription fee.
I will say that itâs not the easiest way for everyone, but I think this setup of using:
- A cloud service to back up notes
- A remote server/home computer to also keep a copy of notes
- Obsidian/Bear or any note taking app that keeps notes as portable files (eg Markdown).
Is a fairly good setup. If youâre thinking of following this guide, just remember, this is secondary to the process of note-taking. The best note-taking app is one that works for you! You do you!
Setup
A quick and dirty solution is Unison or Rsync. To keep things simple and use existing preinstalled tools on the Mac, I went with rsync.
Command is easy:
rsync -razu /Users/myUserName/Library/Drive-Documents/obsidian/notes/ [email protected]:/home/myUserName/media-homelab/docs/documents/nyUserName/notes
What the flags mean:
-r : Go recursive
-a : Archive (preserve read time stamps)
-z : Use text compression to reduce network overhead
-u : Update (skip files newer on receiver)
A great resource for me to understand rysnc is this blog post by DigitalOcean: https://www.digitalocean.com/community/tutorials/how-to-use-rsync-to-sync-local-and-remote-directories#using-rsync-to-sync-with-a-remote-system
Not Recommended: Use crontab
This didnât work on my Mac, so Iâd skip it. However, Iâm keeping it here if youâre on another OS! Iâd skip this section and go read the Launch Daemon steps instead.
Add crontab
Running crontab -e will pop open an editor for cron.
To Execute Script Every Hour
0 */1 * * * myUserName rsync -razu /Users/myUserName/Library/Drive-Documents/obsidian/notes/ [email protected]:/home/xxx/media-homelab/docs/documents/xxx/notes
To list all cron jobs, crontab -l.
If you need help configuring your cron schedules (who doesnât): https://crontab.guru/ is great!
Permissions
cron will need permissions to access the disk, I had to go through the steps of allow listing cron under the Macâs âPrivacy and Securityâ settings. Credit to this blog post that helped me figure that out: https://michaelriedl.com/2022/01/02/macos-cron-rsync.html
^I think rsync would also need this permission to access the disk as rsync is the program triggering the disk access.
Results
I found out cron didnât work (waited till on the hour but my computer went to sleep). Thatâs when I realized cron doesnât run if computer is asleep. It would just skip the run.
Rather, it turns out launchdaemon is the preferred method for Mac
Note:Â Although it is still supported,Â
cron is not a recommended solution. It has been deprecated in favor ofÂlaunchd. https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/ScheduledJobs.html.
Launch Daemon (Recommended)
- Hereâs a sample plist which you put into
~/Library/LaunchAgents:<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>com.myUserName.notessync</string> <key>ProgramArguments</key> <array> <string>/usr/bin/rsync</string> <string>-razu</string> <string>/Users/myUserName/Library/Drive-Documents/obsidian/notes/</string> <string>[email protected]:/home/xxx/media-homelab/docs/documents/xxx/notes</string> </array> <key>StartInterval</key> <integer>3600</integer> <key>RunAtLoad</key> <true/> <key>UserName</key> <string>wenlee</string> <key>StandardOutPath</key> <string>/tmp/com.myUserName.rsync.out.log</string> <key>StandardErrorPath</key> <string>/tmp/com.myUserName.rsync.err.log</string> </dict> </plist> -
launchctl load ~/Library/LaunchAgents/com.myUserName.rsync.plist -
Yes, youâll also have to grant
rsyncperms as a consequence of new privacy protections. -
If you happen to have copy-pasta errors:
plutil ~/Library/LaunchAgents/com.myUserName.rsync.plistwill printcom.myUserName.rsync.plistc.plist: OKto validate your set up is âvalidâ. - You might need to
unloadthenloadas well:
launchctl unload ~/Library/LaunchAgents/com.myUserName.rsync.plist
launchctl load ~/Library/LaunchAgents/com.myUserName.rsync.plist
launchctl kickstart gui/$(id -u)/com.myUserName.rsync.plist
Cloud Sync
Itâs probably a good idea to also keep a backup on the Cloud. To do so just install your favourite Cloud syncing app and ensure that folder is backed up. The Obsidian Vault is essentially just a folder you can backup!
Howâs it going
The setup has been working well. However, one caveat I noticed was that depending on where your vault is stored, if itâs a folder that is cloud folder, certain services (such as Amazon WorkDocs, or OneDrive) will try and move your files to the cloud and delete the local copy. This is undesirable as Obsidian doesnât work well with these files that have been moved to the cloud and are âonline-onlyâ, the notes simply disappear. One solution to this is to either move the files to outside of the cloud folder and rsync to that in addition to the external backup you have. Alternatively, OneDrive has the option to mark them as âalways availableâ by selecting âalways keep on this deviceâ.
Whatâs next
This is still kind of a one-way sync, but I think Iâll graduate to Unison if I need 2 way sync in the future and update this blog post.
Bonus: Static Site of your notes
I also wanted to be able to view my notes on the remote server I rsycned via a web browser. My quick low-effort solution was using MkDocs in addition to FileBrowser, which is a very lightweight web UI for viewing and uploading files and is feature complete.
MkDocs can be ran in Docker + Traefik using this polinux/mkdocs image.
Mkdocs
docker-compose.yaml
notes:
image: polinux/mkdocs:arm64v8-1.5.2
container_name: notes
environment:
- PUID=1000
- PGID=1000
- TZ='America/Los_Angeles'
- FAST_MODE=true
- UPDATE_INTERVAL=60
- AUTO_UPDATE='true'
volumes:
- path-to-notes-dir/:/mkdocs:ro
restart: unless-stopped
labels:
- 'traefik.enable=true'
- 'traefik.http.routers.notes.rule=Host(`notes.example.com`)'
- 'traefik.http.services.notes.loadBalancer.server.port=8000'
- 'com.centurylinklabs.watchtower.enable=true'
mkDocs.yml
site_name: Muh Notes
docs_dir: notes
theme:
name: 'readthedocs' #'material'
palette:
# Light mode
- media: "(prefers-color-scheme: light)"
scheme: default
primary: pink
accent: indigo
toggle:
icon: material/toggle-switch-off-outline
name: Switch to dark mode
# Dark mode
- media: "(prefers-color-scheme: dark)"
scheme: slate
primary: pink
accent: blue
toggle:
icon: material/toggle-switch
name: Switch to light mode
# Extensions
markdown_extensions:
- footnotes
- def_list
fence_code_format
- toc:
permalink: true
plugins:
- search
extra_javascript:
- javascripts/mathjax.js
- https://polyfill.io/v3/polyfill.min.js?features=es6
- https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js
- https://unpkg.com/mermaid/dist/mermaid.min.js %
MkDocs works OK but the formatting is a bit off here, as I do not want to introduce custom frontmatter for my notes.
Quartz
I found Dockerized Quartz which fit my setup perfectly (except it didnât have ARM builds, I will have to submit a PR for it looks like someone has a PR: https://github.com/shommey/dockerized-quartz/pull/4).
The site generated is much nicer for sure!
notes:
# image: shommey/dockerized-quartz
build:
context: ./dockerized-quartz
dockerfile: Dockerfile
container_name: notes
environment:
- BUILD_UPDATE_DELAY=120
# Optional: Auto rebuild Quartz after change in Obsidian Vault
- AUTO_REBUILD=true
networks:
- web
volumes:
# Mount your Obsidian vault for Quartz to read and build the site from
# If not set it will mount docs
- /path-to-my/notes:/vault:ro
#
# Optional: Mount existing Quartz repo
# - /path/on/host:/usr/src/app/quartz
#
# Optional: Persist nginx logs if needed
# - /path/to/nginx/logs:/var/log/nginx
#
# Optional: Mount nginx conf
# - /path/on/host:/etc/nginx
restart: unless-stopped
labels:
- 'traefik.enable=true'
- 'traefik.http.routers.notes.rule=Host(`notes.example.com`)'
- 'traefik.http.routers.notes.service=notes'
- 'traefik.http.services.notes.loadBalancer.server.port=80'
- 'com.centurylinklabs.watchtower.enable=true'
Youâll need an index.md at the top level though.