- 01Why I added one more backup destination
- 02Sync and backup are different things
- 03The overall setup
- 04The difference between mirroring and streaming
- 05The basic rsync command
- 06Where to draw the line on what to exclude
- 07The stumble: it failed on long Japanese filenames
- 08Running it twice a day with launchd
- 09Checking the cloud side once a week
- 10Why I passed on Time Machine
- 11Common mistakes and pitfalls
- 12Things to confirm before you start
- 13Frequently asked questions
- 14Official sources I referred to
- 15Summary
Good morning, まさきん here.
I have set things up so that my whole set of Obsidian notes is backed up to Google Drive every day. I only meant to copy it over with the Mac’s built-in rsync, but it failed once on long Japanese filenames. Others are likely to get stuck at the same spot, so here are the steps and the workaround.
Note: this information is current as of publication. Please check the official sites for the latest conditions.
- An Obsidian backup can be built simply by copying the vault to Google Drive with rsync
- The rsync that ships with macOS (openrsync) failed on long Japanese filenames because it could not create its temporary file
- Adding
--inplacestops it from creating temporary files, and that got it through in my environment - It runs twice a day via launchd. The first run was about 20,000 files and several GB, and the exit code was 0
- Secrets and caches are excluded, and I check the cloud side once a week
Why I added one more backup destination
The whole set of Obsidian notes is called a “vault.” Until now, I had been backing up my vault to GitHub. But having only one backup destination felt a little shaky.
So I chose Google Drive as a second place to keep it. I prepared one folder under My Drive in Google Drive for desktop. rsync copies the incremental changes there.
The other day, I wrote about cancelling Obsidian Sync. I have moved the master copy of my notes to Notion, but the vault files themselves are still on my machine. Having given up sync, I wanted to keep the backup in my own hands.
A vault is just a folder
Obsidian’s official help says notes are stored as plain text files in Markdown format. A vault is a folder on your local file system. Subfolders are included.
Settings go into the .obsidian folder directly under the vault. The help explains that things like hotkeys and themes are kept there.
In other words, backing up needs no special tools. Copy the folder somewhere else and that is your backup. It is thanks to this design that rsync can be used.
Sync and backup are different things
Obsidian’s official help explains sync and backup separately. Sync keeps your notes up to date. But, it notes, it does not protect you from data loss.
| Item | Sync | Backup |
|---|---|---|
| Purpose | Keep every device in the same state | Keep a copy in a separate place |
| Direction | Two-way as a rule | One-way, from the source to the backup |
| When you delete | The deletion propagates elsewhere too | Depending on settings, it stays in the backup |
| When something breaks | The broken state can propagate too | The version from before it propagated may remain |
| Role this time | Not used | Copied with rsync twice a day |
What I am doing this time is the right-hand column, backup. I do not open the Google Drive folder in Obsidian. It sits there purely as the place things get copied to.
The official help also cautions against syncing the same vault with multiple services. This is to avoid data conflicts and corruption. I think not editing the backup destination directly fits with that caution as well.
The overall setup
The mechanism has just three parts.
- The vault folder (e.g.
~/Notes) - A backup folder under My Drive in Google Drive
- A shell script that calls rsync, and launchd to run it on a schedule
rsync copies to a local folder. Uploading from there onward is the job of Google Drive for desktop. The script does not call any cloud API.
The difference between mirroring and streaming
Google Drive for desktop has two ways of handling My Drive. I summarized the official help’s explanation in a table.
| Item | Streaming | Mirroring |
|---|---|---|
| Where files are stored | Mainly in the cloud | Both in the cloud and locally |
| Offline use | Only what you mark for offline | Always available |
| Space on your computer | Uses almost none | Uses as much as a full copy |
| Location on macOS | Under ~/Library/CloudStorage (cannot be changed) | The folder you choose in settings |
Streaming on macOS appears under “Locations” in the Finder sidebar. The default location is ~/Library/CloudStorage, and it is said that this cannot be changed.
Thinking of it as an rsync destination, it seems to break down like this. With mirroring, the copied result also stays on your machine in full. That means you use space twice over, for the vault and for the backup. Streaming saves space, but the actual files live mainly on the cloud side.
Which is right depends, I think, on how much free disk space you have. At a scale of several GB, the burden of mirroring may be small. Incidentally, Obsidian’s official help has guidance on vaults kept in Google Drive: set them to be available offline. That is about using it for sync, but the thinking seems useful as a reference.
The basic rsync command
The skeleton is about this much. The paths are generalized examples.
#!/bin/bash
SRC="$HOME/Notes/"
DEST="$HOME/GoogleDrive/vault-backup/"
rsync -a --inplace \
--exclude '.venv/' \
--exclude 'node_modules/' \
--exclude '.cache/' \
--exclude 'tmp/' \
--exclude '.env' \
--exclude '*.token' \
"$SRC" "$DEST"
rc=$?
echo "$(date '+%F %T') rc=$rc" >> "$HOME/Library/Logs/vaultbackup.log"
exit $rc
Replace DEST with the actual path under your own My Drive. With streaming, it will be under ~/Library/CloudStorage.
I use only a few options.
| Option | Meaning (per the man page) | How I use it here |
|---|---|---|
-a | Sets recursion, preserving timestamps and so on, all at once | Included |
--inplace | Writes directly to the destination without creating a temporary file | Included (the workaround described below) |
--exclude | Excludes files matching a pattern | Excludes secrets and caches |
-n | A dry run that does not actually change the destination | Used as a check before the first run |
--delete | Deletes files from the destination that are not in the source | Whether to include it depends on your policy |
If you add --delete, files you deleted at the source also disappear from the backup. The behavior moves closer to sync. If you want to keep notes you deleted by mistake, I think not adding it is also a valid choice.
The trailing slash on the source needs attention too. With it, rsync copies “the contents of the folder.” Without it, the folder itself goes in one level deeper.
Where to draw the line on what to exclude
A vault tends to pick up things other than notes. I excluded the following four kinds.
| Kind | Example | Why exclude it |
|---|---|---|
| Virtual environments | .venv/ and node_modules/ | Can be rebuilt. Huge number of files |
| Caches | .cache/ and the like | Can be rebuilt. Contents change frequently |
| Temporary storage | tmp/ and the like | No need to keep |
| Secrets | .env and token files | I don’t want to widen what sits in the cloud |
I think there are two axes for the decision. Exclude what can be rebuilt. Also exclude what would hurt if it leaked. Those two.
The more backup destinations you have, the more places your secrets sit. So I set it up to leave them out from the start. I also wrote about how to hunt for secrets in the two leaks that got through even though gitleaks said “no leaks”.
You can verify your exclusions with a dry run using -n. Add -v as well and you get a list of the files that would be copied. It is reassuring to check that no secret filenames show up.
Clicking this opens the Rakuten login page. Once you log in, you’ll see the campaign details.
The stumble: it failed on long Japanese filenames
The first run stopped partway through. The cause was files with long Japanese filenames.
What appeared in the error was a function name, mkstempat. It said that creating a temporary file had failed. The cause was that the temporary file’s name exceeded 255 bytes.
Here is how it works. rsync normally creates a temporary file at the destination. When the transfer finishes, it moves it to the real name. The rsync man page at samba.org also describes this default behavior.
The temporary file’s name is longer than the original name. So even when the original name only just fits, the temporary file’s name can go over the limit.
The file itself can exist, yet it fails only when being copied. That is because, even though the original filename is within the limit, the temporary file’s name exceeds it. If you give your notes longish Japanese titles, you may be prone to hitting this.
How many Japanese characters is 255 bytes?
In the macOS headers, NAME_MAX is defined as 255. The comment reads “max bytes in a file name.”
From here it is arithmetic. Most Japanese characters take 3 bytes each in UTF-8. Divide 255 by 3 and you get 85. So for a name made up only of Japanese, roughly 85 characters is the guide. If an extension or single-byte characters are mixed in, the number shifts a little.
That said, the story is a bit more complicated. On the Apple Developer Forums, an Apple support engineer gives a different explanation as well: the limit of APFS itself is 255 characters, which doesn’t line up with NAME_MAX. It also said that the limit varies with the volume format.
So I treat “roughly 85 characters” as a guide on the safe side. The limit that actually applies may change depending on what mechanism the destination folder sits on top of.
The workaround is —inplace
The fix was simply adding one option. --inplace.
The openrsync man page explains that it operates directly on the destination file without creating a temporary file. Since no temporary file is created, the problem of the name getting longer doesn’t occur either. In my environment, this got it through to the end.
There are trade-offs, though. The cautions that the samba.org man page lists are along these lines.
- During the transfer, the destination file is in a half-finished state
- If it stops partway, that half-finished state can be left behind
- It should not be used to update files that other people have open
The destination this time is a backup folder that nobody opens. Even if it stops, the next run copies it again. So I judged this an acceptable trade-off for this use.
Shortening the filenames is another option. But renaming about 20,000 files was not realistic.
rsync on macOS is openrsync
To begin with, rsync on the Mac is not what it used to be inside. On recent macOS, the standard rsync is an implementation called openrsync.
Apple publishes the source of its rsync. That repository contains an openrsync directory. The man page is also titled “openrsync.” It is described as compatible with protocols 27 to 29 of the samba.org version.
The switch is said to have happened around macOS Sequoia 15.4. However, I could not confirm it in Apple’s official release notes. So I will leave it at “is said to.”
There is one practical caution. rsync examples you find online won’t necessarily work as is. It seems best to check the options you use by running man rsync on your own machine.
Running it twice a day with launchd
I left the scheduled execution to launchd. In Apple’s developer documentation, cron still works but is not recommended. It directs you to use launchd.
The time is specified with a key called StartCalendarInterval. The skeleton of the plist looks like this. The job name and times are examples.
<?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.example.vaultbackup</string>
<key>ProgramArguments</key>
<array>
<string>/bin/bash</string>
<string>/Users/yourname/bin/vaultbackup.sh</string>
</array>
<key>StartCalendarInterval</key>
<array>
<dict>
<key>Hour</key><integer>3</integer>
<key>Minute</key><integer>30</integer>
</dict>
<dict>
<key>Hour</key><integer>13</integer>
<key>Minute</key><integer>30</integer>
</dict>
</array>
<key>StandardOutPath</key>
<string>/tmp/vaultbackup.out.log</string>
<key>StandardErrorPath</key>
<string>/tmp/vaultbackup.err.log</string>
</dict>
</plist>
According to the launchd.plist man page, you can list multiple time dictionaries. For twice a day, you write two dictionaries. Any field you leave out is treated as “all.”
I set it for two runs, in the small hours and just after midday. The early-morning run picks up the previous day’s work. The early-afternoon run picks up the morning’s work.
How sleep is handled differs from cron. Apple’s documentation says that if the machine was asleep at the scheduled time, the job runs when it wakes.
If you are used to writing cron, you may also like my post on the day-of-month and day-of-week trap in cron. With scheduled jobs, I believe double-checking after you write them matters.
Results of the first run and the scheduled runs
The first run was about 20,000 files and several GB. The first run after adding --inplace finished normally. The exit code was 0.
The scheduled run from launchd also finished with 0. Running it by hand, and running it on schedule. I checked both because the environments they run in differ slightly.
However, this means “it was copied to the local backup folder.” Uploading to the cloud is a separate matter. The initial upload is taking time. As of this post, I have not yet been able to confirm that it completed. So I am not writing that result.
Checking the cloud side once a week
Even if rsync’s exit code is 0, that doesn’t mean the files reached the cloud. So I set up a separate check on whether they arrived. The frequency is just once a week.
I look at two things.
- Freshness: whether the files on the cloud side carry recent dates
- Contents: whether I can actually open and read a file
By date alone, even a corrupted file looks new. Isn’t it only once you have opened and read it that you can call it “a backup you can restore from”?
I don’t do it every day because I wouldn’t keep it up. I decided weekly is enough here.
Don’t put failure notifications in the script
The script does not send notifications when it fails. I designed it to leave that to the monitoring I already have.
The script has only two jobs. Copy. Record the exit code in a log. Those two.
If you put notifications in too, notification settings and secrets creep into the script. If monitoring is gathered in one place, then even as jobs increase there is only one place to fix.
Why I passed on Time Machine
On a Mac, Time Machine is also a candidate. Obsidian’s official help lists it too, as an OS tool.
But on my Mac, no backup disk was set up. It can’t be used as is. According to Apple’s support page, an external storage device is required. The suggested capacity is at least twice that of your Mac.
I didn’t go as far as preparing that this time, and passed. Time Machine is a tool that protects the whole Mac. Its role may be a little different from my aim here of getting just the vault out to another place.
Common mistakes and pitfalls
Having built this, here are the points where I felt it was easy to stumble.
- Failing on long Japanese filenames because the temporary file can’t be created
- Trying to use rsync examples from the internet as is with openrsync
- Forgetting the trailing slash on the source and copying one level too deep
- Not excluding
.envor tokens and putting them in the cloud - Seeing exit code 0 and assuming it reached the cloud too
- Opening the backup folder in Obsidian and editing it
The one I actually hit was the first. I intend to guard against the fifth with the weekly check.
Things to confirm before you start
- The location of the vault folder, and its approximate file count and size
- Free space in Google Drive and free disk space on your computer
- Whether My Drive is set to streaming or mirroring
- Whether there are any
.envor token files in the vault - Whether there are any long filenames, such as over 80 Japanese characters
- Whether a dry run with
-nshows the files you intend to copy
Frequently asked questions
Q. Is Google Drive alone enough for backing up Obsidian?
With only one place, you are stuck when that place is unavailable. Obsidian’s official help also explains that the cloud is both an alternative to and a complement to physical storage. I went with two places, counting GitHub.
Q. Can I open the vault in Google Drive directly?
Obsidian’s official help says syncing via Google Drive is not officially supported. In this setup too, the premise is that the backup is never opened. I think it is safest to edit only in the original vault.
Q. Is it fine to always use --inplace?
It depends on the use. The man page cautions that the destination is in a half-finished state during transfer. For a backup destination nobody opens, the impact can probably be called small.
Q. What about installing rsync from Homebrew?
Installing the samba.org version of rsync separately is another approach. But I haven’t tried it. This time I dealt with it by adding --inplace.
Q. Isn’t twice a day too often?
rsync copies only the differences. If nothing has changed, it finishes having done almost nothing. The burden of adding runs is small, so it may be best to decide based on how much time you can afford to lose.
Q. What happens if the Mac is asleep?
According to Apple’s documentation, a job whose time passed during sleep runs when the machine wakes. How it is handled when the power was off needs checking separately.
Q. If I delete a note at the source, does it disappear from the backup too?
Unless you have added --delete, it stays in the backup. If you add it, files not in the source also disappear from the backup. Which you choose depends on what you expect from the backup.
Q. Where do I see whether the upload has finished?
You can check sync status in the Google Drive for desktop app. I have not yet been able to confirm completion of my initial upload. In the weekly check, I plan to go as far as opening and reading a file.
Official sources I referred to
- How Obsidian stores data (Obsidian Help)
- Back up your Obsidian files (Obsidian Help)
- Sync your notes across devices (Obsidian Help)
- Use Google Drive for desktop (Google Drive Help)
- Streaming and mirroring files (Google Drive Help)
- Use Google Drive for desktop on macOS (Google Drive Help)
- rsync(1) man page (samba.org)
- rsync source (Apple OSS Distributions)
- Scheduling Timed Jobs (Apple Developer)
- How to query APFS file name size limit? (Apple Developer Forums)
- Back up your Mac with Time Machine (Apple Support)
Summary
An Obsidian vault is just a folder. So I was able to build a daily backup with nothing but rsync and launchd.
Where I stumbled was long Japanese filenames. Adding a single --inplace gets it through, but it is worth knowing there is a trade-off.
Confirming that it reached the cloud is still to come. Why not start with a dry run using -n on your own vault?
Once you have sorted out a “monthly system” like backups, you may be able to look over your monthly phone bill with the same eye.
Clicking this opens the Rakuten login page. Once you log in, you’ll see the campaign details.
This article contains affiliate advertising. If you sign up for a product or service through a link on this site, we may receive compensation from the partner company. The site operator is also an employee of Rakuten Group and may receive compensation through an employee referral program. The content and opinions here are based on the operator’s own experience and research regardless of any advertising relationship, but please read with the above in mind. See the Disclaimer & Affiliate Disclosure for details. Disclaimer & Affiliate Disclosure
This article includes machine-translated content. Please check the official Rakuten Mobile multilingual page for exact terms.
If you have concerns about Rakuten Mobile coverage or signal quality, you can consult through the official signal improvement request form .