Hi everyone!
I want to share a project I’ve been building to solve something that’s been bugging me (and probably many of you) for a while: syncing an Obsidian vault across all devices, including iOS with full git history, without losing your mind.
The backstory
Like a lot of people here, I wanted my vault in a git repo. Version history, diffs, branching - all the good stuff. But getting git sync to actually work across devices turned out to be an adventure. I went through weeks of trying different approaches: Working Copy on iOS with manual pull/push routines, .git folder hacks, extra apps to bridge the gap, Apple Shortcut scripts… each solution worked kind of, but every single one had sharp edges and required way too many moving parts.
At some point I stepped back and thought: why can’t my OG fileserver just do git push for me? Then I looked at the Remotely Save plugin, which already syncs to my file server. And I thought: what if I can just build another API interface for git?
That’s how git3 was born.
What it actually is
git3 is a small server (a single ~8 MB binary) that exposes an S3-compatible API. Behind the scenes, it stores your files as plain files on an ephemeral disk and automatically commits and pushes to a git remote on every change. It also periodically pulls, so changes made directly in the repo (say, from github interface) show up on your other devices too.
In short: Obsidian talks to git3 via Remotely Save thinking it’s an S3 bucket. git3 translates those S3 operations into git commits. You get seamless sync and full git history.
Here’s what makes it different from other approaches:
-
No git client needed on your devices. Your phone doesn’t need to know git exists. Remotely Save handles the sync, git3 handles the git part.
-
Works on iOS, Android, desktop. Anything that runs Obsidian + Remotely Save.
-
Single binary or Docker container. No runtime dependencies, no system git installation required - just one binary.
-
Debounced commits. It doesn’t create a commit per file. it waits a configurable number of seconds after the last change, then commits everything at once.
How to get it running
Step 1: Prepare a git repo
Create a private repository on GitHub (or GitLab, Gitea, etc.) for your vault. If you already have one - perfect, use that.
You’ll need a Personal Access Token with write access to the repo:
-
Go to GitHub → Fine-grained tokens
-
Give it a name like
git3 -
Under Repository access, select “Only select repositories” → pick your vault repo
-
Under Permissions → Repository permissions, set Contents to Read and write
-
Generate and copy the token
Step 2: Deploy git3
Docker Compose:
services:
git3:
image: ghcr.io/0xa1bed0/git3:latest
container_name: git3
restart: unless-stopped
environment:
- ACCESS_KEY=your-access-key # make up any key you want
- SECRET_KEY=your-secret-key # make up any secret you want
- BUCKET=vault
- REGION=us-east-1
- GIT_REPO=https://github.com/you/obsidian-vault.git
- GIT_TOKEN=github_pat_xxxxxxxxxxxx
- GIT_BRANCH=main
- DEBOUNCE=10 # Git commit interval in seconds
- PULL_INTERVAL=60 # Pull interval in seconds
$ docker compose up -d
That’s it on the server side. The ACCESS_KEY and SECRET_KEY are credentials you define - they’re used to authenticate Remotely Save requests, not tied to any AWS account.
Don’t have a server? git3 is tiny and runs comfortably on free tiers. The README has step-by-step guides for Flyio, Render, and other hosting providers. You can literally get this running without spending a cent. No need to get a persistent volume - just ephemeral one is good enough. git3 will figure out when it should clone, or when it should pull.
Step 3: Configure Remotely Save
-
In Obsidian, install the Remotely Save community plugin
-
Open its settings and configure:
-
Remote service: S3 or S3-compatible
-
Endpoint:
https://your-server-address(use a domain with HTTPS Cloudflare free tier works great as a reverse proxy) -
Region:
us-east-1 -
Access Key ID: the access key you set above
-
Secret Access Key: the secret key you set above
-
Bucket:
vault -
Check “S3 path style” / Force Path Style
-
-
Click Check Connectivity — it should pass
-
Set your auto-sync interval
Repeat on every device. Done.
Configuration reference
The server configuration can be set as environment variables or CLI flags:
| Variable | Default | What it does |
|---|---|---|
ACCESS_KEY / SECRET_KEY |
(none) | Credentials for authenticating requests |
BUCKET |
vault |
Bucket name |
GIT_REPO |
(none) | HTTPS URL of your git remote |
GIT_TOKEN |
(none) | Personal access token for git auth |
GIT_BRANCH |
main |
Branch to sync with |
DEBOUNCE |
10 |
Seconds to wait after last change before committing |
PULL_INTERVAL |
60 |
Seconds between git pulls (0 to disable) |
VAULT_DIR |
/vault |
Local directory for vault files |
ADDR |
:80 |
Listen address |
How it works under the hood
The S3 API coverage is intentionally minimal, just enough for Remotely Save:
-
PutObject: writes file to disk, triggers debounced git commit + push
-
GetObject / HeadObject: reads file from disk
-
DeleteObject: removes file, triggers debounced git commit + push
-
ListObjectsV2: lists files (skips
.gitdirectory)
There’s no CopyObject or multipart upload because Remotely Save doesn’t need them for typical vault files. If somebody need it - I can add.
The whole thing is written in Go, uses go-git Go library for all git operations (so no system git binary needed), and authenticates requests using standard AWS Signature V4.
I’ve been running this for my own vault and it’s been solid. I edit notes on my Mac, my iPhone, my iPad and everything just stays in sync. Meanwhile, my GitHub repo has a clean commit history of every change. I can browse diffs, revert things, and generally have peace of mind that nothing is ever lost.
This is my first time sharing here, so I’d really appreciate any feedback, questions, or ideas. The project is open source under the Elastic License 2.0 — free for personal and work use. Maybe someday I’ll build a hosted version for those who don’t want to maintain their own deployment, but I’m not sure it’s needed — throwing a single binary or tiny Docker container into random ephemeral environment somewhere should be easy enough.
Links:
-
GitHub: 0xa1bed0/git3
-
Docker image:
ghcr.io/0xa1bed0/git3:latest