Self-hosted GitLab CE used as the git remote for an Obsidian vault, reached from other machines over Tailscale.
- Docker Desktop (Windows)
- Tailscale installed and connected on the host PC and all client machines
- 7-Zip installed (for backups)
-
Clone this repo onto the Windows PC that will host GitLab.
-
Create your
.envfile:cp .env.example .envEdit
.envand setGITLAB_HOSTNAMEto your Tailscale hostname (find it in the Tailscale admin panel, e.g.my-pc.tail12345.ts.net). -
Start GitLab:
docker compose up -dFirst boot takes 5–10 minutes. Watch progress with:
docker compose logs -f gitlab -
Wait until healthy:
docker compose psStatus should show
healthy. -
Get the initial root password (valid for 24 h):
docker exec -it gitlab cat /etc/gitlab/initial_root_password -
Open
http://<GITLAB_HOSTNAME>:8929, log in asroot, change the password. -
Create a regular user, sign in as that user.
-
Create an empty private project
obsidian-vault(no README, no default branch). -
Add SSH keys (ed25519) for the host PC and each client under Profile → SSH Keys.
gitlab-template/
├── docker-compose.yml # Service definition — uses $GITLAB_HOSTNAME from .env
├── .env.example # Copy to .env and fill in
├── .gitignore # Excludes data/, logs/, secrets, .env
├── README.md (this file)
├── config/ → mounted to /etc/gitlab (gitlab.rb; secrets generated at runtime)
├── data/ → mounted to /var/opt/gitlab (repos, db — gitignored)
├── logs/ → mounted to /var/log/gitlab (gitignored)
└── scripts/
├── backup.ps1 # Weekly 7z backup, keeps 4 archives
├── register-backup-task.ps1 # Register backup as Windows Scheduled Task (run as Admin)
└── firewall-rules.ps1 # Restrict ports 8929/2222 to Tailscale interface (run as Admin)
| Task | Command (run from repo root) |
|---|---|
| Start | docker compose up -d |
| Stop | docker compose down |
| Logs | docker compose logs -f gitlab |
| Shell | docker exec -it gitlab bash |
| Reconfigure | docker exec -it gitlab gitlab-ctl reconfigure |
| Backup now | powershell -File scripts\backup.ps1 |
Add to ~/.ssh/config on each client machine:
Host <GITLAB_HOSTNAME>
HostName <GITLAB_HOSTNAME>
User git
Port 2222
IdentityFile ~/.ssh/id_ed25519
Clone the vault with:
git clone git@<GITLAB_HOSTNAME>:<username>/obsidian-vault.git
Both machines commit and push/pull from the same GitLab project. Tailscale must be running on the host PC for clients to reach it.
- If the PC sleeps and sync breaks, wake it — both sides resume on the next pull.
.obsidian/workspace.jsonis excluded from git (churns constantly, causes merge conflicts).
Backups are weekly 7z archives of config/ + data/ + logs/, stored in backups/
(gitignored). The script keeps the 4 most recent archives.
Register the scheduled task (once, as Administrator):
scripts\register-backup-task.ps1Restore: stop the container, extract the archive over config/ + data/ + logs/,
then docker compose up -d.
To restrict ports 8929 and 2222 to the Tailscale interface only (recommended):
# Run as Administrator
scripts\firewall-rules.ps1Edit the image tag in docker-compose.yml, then:
docker compose pull
docker compose up -d
Volumes persist, so data is preserved across upgrades.