Hyunho Kim99 downloadsTwo-way sync between your vault and Google Drive using your own Google credentials. Works on desktop and iOS.
Keep your Obsidian notes the same on all your devices by saving them in your own Google Drive.
Tested on Mac and iPhone. Windows and Linux sign in the same way as a Mac. Android should work like iPhone, but nobody has tested it yet.
You need:
accounts.google.com and
oauth2.googleapis.com to sign in, and www.googleapis.com to read and write your
notes in Google Drive. It contacts no other server.127.0.0.1 to receive Google's reply.In Obsidian, open Settings → Community plugins → Browse, search for Vault Sync for Google Drive, then click Install and Enable.
Do this on every device you want to sync. Obsidian keeps the plugin updated for you.
Google only lets apps into your Drive if they have a key. Instead of sharing one key among all users, you make your own, so only you control it. You need it once; all your devices use the same key.
In production. Don't skip this. If it stays
in Testing, Google signs you out after exactly 7 days and sync stops with an
invalid_grant error."Google hasn't verified this app" — when you sign in for the first time, Google shows this warning. That's normal, because the "app" is the key you just made for yourself. Click Advanced → Go to (your app name) (unsafe) to continue. If Google blocks the sign-in completely instead of warning, that is Google's decision and the plugin can't change it.
If you have more than one computer, repeat this step on each one.
Phones can't use Google's sign-in page for this kind of key, so you send the sign-in from your computer to your phone instead. It's like scanning a code to log in.
K7Q4-ZP9M.
Leave that window open.The phone checks the sign-in with Google, saves it, and syncs for the first time. If you mistype the code, it just asks again. After this, the phone works on its own; it doesn't need the computer anymore.
If you edit the same note on two devices before they sync, the plugin keeps both versions instead of guessing which one is right:
note (conflict 2026-09-22 1430 iPhone).md.You can sync with a folder someone shared with you, or any other folder in your Drive (for example, a class or group project):
Linked to "<folder name>" appears under the box.Good to know:
.trash folder or your system trash), never deleted forever.
Notes deleted in Google Drive go to the Drive trash.If you think someone else got access to your Google Drive through this plugin, remove its access at https://myaccount.google.com/permissions, then sign in again.
Note.md and note.md can't both exist. Macs and iPhones treat them as the
same name. If both are in Drive, one is skipped and you're told about it.| What you see | What to do |
|---|---|
invalid_grant about a week after setup |
Your Google project is still in Testing. Set it to In production (step 2.4) and sign in again. |
| "Google returned no refresh token" | Remove the app at https://myaccount.google.com/permissions and sign in again. |
| Sync stopped with a deletion warning | The safety check found many deleted files. Look at both your vault and Drive. If you really meant to delete them, click Reset next to Reset sync state (under Advanced). |
| "This device's sign-in is missing" | Obsidian's storage was cleared, for example after reinstalling. Your notes are fine. Sign in again on a computer, or paste a new setup token on a phone. |
| "That looks like the code" | You typed the code into the token box. Paste the token into box 1 and type the code into box 2. |
| "That code doesn't match this token" | The code was mistyped, or it's from a different token. Type it again, or copy a new token. |
| "unusable name(s) skipped" | A file in Drive has a name your device can't use, such as Plan?.md on Windows. Rename it on a device that can open it. |
| Korean or Japanese file names appear twice | This shouldn't happen. Please report it. |
To see more detail, open Obsidian's developer console. Skipped files and conflict copies are listed there. For a full log of every sync, turn on Verbose logging under Advanced, and set the console's log level to include "Verbose".
This section is for readers who want to know how the plugin works inside. You don't need it to use the plugin.
https://www.googleapis.com/auth/drive).
The narrower drive.file scope only sees files the app itself created or opened,
so it cannot see a folder someone shared with you, or files a collaborator adds
later. The Google Picker, which would widen it file by file, does not run inside
Obsidian on iOS. Full Drive is a Google restricted scope. The cost is the blast
radius: a leaked refresh token, or a setup token leaked together with its code, can
reach your whole Drive rather than one folder. It is still only ever your own OAuth
client; the plugin ships none and no one else's app holds the permission.md5Checksum), not timestamps.obsidian:// redirect URIs for
every client type, and the mobile app cannot open the loopback listener that the
desktop sign-in uses. After linking, the phone refreshes its own access tokens
directly with Google.http://127.0.0.1:<port> loopback, and the plugin picks a free port at runtime.What holds your credentials. The client secret and refresh token are kept in
Obsidian's secure storage (SecretStorage, Obsidian 1.11.4 and later): per device and
outside the vault folder, so copying, backing up or syncing the vault never carries
them. Obsidian does not document whether that storage is encrypted on disk, so treat
it as "out of the vault", not as "encrypted". It is also shared with other installed
plugins, which can read this plugin's settings file as well. The client ID is not a
secret and stays in data.json. Anyone who gets the refresh token has access to your
whole Google Drive; revoke it at https://myaccount.google.com/permissions.
This plugin's own folder is permanently excluded from sync, since its data.json
still holds the Drive folder id and sync records. It is resolved from the actual
install directory at runtime rather than a hardcoded name.
The setup token is encrypted. It is AES-256-GCM, keyed by PBKDF2-SHA256 (600,000 rounds) from an 8-character one-time code. The code is shown only on the copying device's screen and never goes to the clipboard, so a token left in clipboard history or a message is useless on its own. Trying every code against a leaked token takes roughly two years on one high-end GPU, less with more GPUs. Tokens do not expire: if one leaks together with its code, revoke access at https://myaccount.google.com/permissions and link again.
Remote filenames are untrusted input. Google Drive is not a path-based store, and a
file's name may contain /, .. or control characters. Those names become vault
paths and get written to disk, so every remote path is validated before use; anything
containing a traversal segment, a control character or a drive letter is skipped and
reported rather than silently renamed. On Windows, names it would silently rewrite
(a trailing dot or space, device names like CON, or <>:"|?*) are skipped too.
Each skipped name is listed in the developer console once per session. A file
skipped this way is left alone: it is not treated as deleted, so it stays in Google
Drive and on the devices that can hold it.
Local deletes are never permanent. They follow Obsidian's "Deleted files" setting
when it is the system trash or the vault's .trash. When it is set to "Permanently
delete", sync still moves the file to the vault's .trash. Files in the config
folder, which Obsidian does not index, always go to the vault's .trash.
File access goes through the adapter API. Obsidian's Vault API only sees files it
indexes, which leaves out dotfolders such as the config folder. Syncing those when
asked to means reading and writing through vault.adapter instead.
Downloads are verified against Drive's md5Checksum before they touch the vault.
Uploads are pinned to https://*.googleapis.com. The resumable session URI arrives
in a response header and then receives your file contents, so it is checked rather than
followed wherever it points.
Sign-in. PKCE (S256) with a random state that is verified on return. The loopback
listener binds to 127.0.0.1 only, on an OS-assigned port, and shuts down as soon as
the code arrives or after five minutes.
Enabling config-folder sync has a cost. This plugin's folder stays excluded, but other
plugins' data.json files do not — several community plugins keep API keys there, and
turning this on uploads them. Plugin code syncs too, so any linked device can change
the plugins that run on every other one. The config folder is read from the vault
rather than assumed to be .obsidian, so a renamed one is excluded the same way.
From a release. Download main.js, manifest.json and styles.css from the
latest GitHub release
into .obsidian/plugins/vault-sync-for-google-drive/ in your vault, then enable it under
Settings → Community plugins.
From source. Copy the build into your vault:
npm install
npm run build
PLUGIN="$HOME/path/to/YourVault/.obsidian/plugins/vault-sync-for-google-drive"
mkdir -p "$PLUGIN"
cp main.js manifest.json styles.css "$PLUGIN/"
Then enable it under Settings → Community plugins. On a phone, put the same files in the vault's plugin folder, for example via the Files app on iOS.
npm run dev # watch build
npm test # pure-logic tests (paths, globs, conflicts, setup token, credential store, md5)
npm run check # typecheck + tests
The iOS bundle must never statically reference a Node builtin. http and electron
are reached only through window.require inside a desktop-only branch; verify with:
grep -oE 'require\("[a-z_]+"\)' main.js | sort -u # expect only require("obsidian")
Changes reach main through pull requests. After the version bump is merged, tag the
merge commit on main and push the tag:
npm version patch # or minor / major
git push origin main
git push origin <version>
npm version bumps package.json, copies the version into manifest.json, records it
in versions.json, commits, and creates a bare tag like 1.0.1 (no v — Obsidian
requires that). Pushing the tag runs .github/workflows/release.yml, which checks that
the tag matches the manifest, runs the tests, builds, checks the bundle for Node
builtins, and publishes a GitHub release with main.js, manifest.json and styles.css attached.