Two-way sync between a local Linux folder and Google Drive β managed from a single interactive bash script.
Uses rclone bisync + inotify-tools + systemd + cron to keep a local directory and a Google Drive folder in continuous sync. Local changes push up within seconds; remote changes pull down on a schedule you choose.
Local file saved βββΊ inotify detects βββΊ debounce (5s) βββΊ rclone bisync βββΊ Drive updated
Cron fires (N min) βββΊ rclone bisync βββΊ Local folder updated
- Single file, zero dependencies β one
.shfile; installs rclone + inotify-tools automatically - Interactive menu β add, list, edit, remove, and monitor syncs without memorizing commands
- Bidirectional sync β local β Drive via
rclone bisync - Instant local β Drive β
inotifywaitwatches for changes, debounces (5 s), then syncs - Scheduled Drive β local β cron pulls remote changes at your chosen interval
- Conflict resolution β newer file wins (
--conflict-resolve newer); losers are renamed, not lost - Lock & debounce β prevents overlapping syncs and Google API rate-limit floods
- Auto log rotation β logs rotate at 50 MB
- Systemd managed β watcher survives reboots; start/stop with standard
systemctlcommands - Multi-sync β manage multiple independent Drive β folder pairs
- Safe β never runs as root; per-remote cache cleanup (never wipes other syncs); systemd hardening
| Requirement | Notes |
|---|---|
| OS | Ubuntu 20.04+, Debian 11+, or any systemd-based Linux |
| Bash | 4.4+ (ships with all supported distros) |
| Internet | Required for Google Drive API access |
| Google Account | With access to the target Drive folder |
| Google Cloud Project | Free tier β see setup guide below |
Everything else (rclone, inotify-tools, curl) is installed automatically on first run.
# Clone the repo
git clone https://github.com/wahidlahlou/gdrive-sync.git
cd gdrive-sync
# Make executable
chmod +x gdrive-sync.sh
# Run
./gdrive-sync.shBefore adding your first sync, you'll need Google API credentials. Follow the guide below β it takes about 5 minutes.
You need a Client ID and Client Secret from a Google Cloud "Desktop app" OAuth client. This is what rclone uses to authenticate with your Google account. You do this once; the credentials work for all your syncs.
-
Go to Google Cloud Console
-
Sign in with the Google account that has access to the Drive folder
-
Click the project dropdown at the top-left (next to "Google Cloud"):
-
Click New Project
-
Enter a name (e.g.
gdrive-sync) β organization and location can stay as defaults -
Click Create
-
Make sure your new project is now selected in the dropdown
-
In the left sidebar, navigate to APIs & Services β Library
Direct link: https://console.cloud.google.com/apis/library
-
Search for Google Drive API
-
Click on the result, then click the blue Enable button
-
Wait a few seconds for it to activate
Before you can create credentials, Google requires a consent screen configuration:
-
Go to APIs & Services β OAuth consent screen
Direct link: https://console.cloud.google.com/apis/credentials/consent
-
Select External as the User Type, then click Create
(If you're in a Google Workspace org and only you will use it, you can choose "Internal" instead β this skips the test-user step.)
-
Fill in the required fields:
Field Value App name gdrive-syncUser support email your email Developer contact email your email Leave everything else blank/default.
-
Click Save and Continue
-
Scopes page β click Add or Remove Scopes
- In the filter box, type
drive - Check the box for
.../auth/drive(described as "See, edit, create, and delete all of your Google Drive files") - Click Update
- Click Save and Continue
- In the filter box, type
-
Test users page β click + Add Users
- Enter your own Gmail / Google Workspace email address
- Click Add
- Click Save and Continue
-
Click Back to Dashboard
About "Testing" mode: Your app will stay in "Testing" status β this is fine. It means only the test users you added (yourself) can authenticate. You do not need to publish the app or go through Google's verification process. The only limitation is that tokens expire after 7 days, but rclone handles automatic refresh.
-
Go to APIs & Services β Credentials
Direct link: https://console.cloud.google.com/apis/credentials
-
Click + Create Credentials at the top, then choose OAuth client ID
-
Set the fields:
Field Value Application type Desktop app Name gdrive-sync(or anything you like) -
Click Create
-
A dialog appears with your credentials:
Client ID: 123456789-xxxxxxxxx.apps.googleusercontent.com Client Secret: GOCSPX-xxxxxxxxxxxxxxxxxxxxxxx -
Copy both values β you'll paste them into the script when adding a sync
Security note: Treat the Client Secret like a password. Don't commit it to Git, don't share it publicly. It's stored locally in
~/.config/rclone/rclone.confon your server.
-
Open Google Drive in your browser
-
Navigate to the folder you want to sync
-
Look at the URL bar:
https://drive.google.com/drive/folders/1aBcDeFgHiJkLmNoPqRsTuVwXyZ012345 ββββββββββββββ Folder ID ββββββββββββββ -
Copy the string after
/folders/β that's your Folder ID
Tip: If you want to sync a subfolder, navigate to that subfolder and copy its ID. If you want to sync your entire Drive, leave the Folder ID field empty when the script asks (rclone will use the Drive root).
./gdrive-sync.shChoose 1) Add new sync and paste your Client ID, Client Secret, and Folder ID when prompted. A browser window will open (or a URL will be printed for headless servers) for you to authorize access.
./gdrive-sync.sh βββββββββββββββββββββββββββββββββββββββββββββββ
β Google Drive Sync Manager v0.2.0 β
β rclone bisync + inotify-tools β
βββββββββββββββββββββββββββββββββββββββββββββββ
Active syncs:
β my-documents
β project-backups
Menu
1) Add new sync 2) List configurations
3) Show status 4) Edit a configuration
5) Remove a configuration 6) View logs
7) Manual sync now 8) Test Drive connection
9) Settings 10) Run tests
0) Exit
# Quick status check (scriptable)
./gdrive-sync.sh --status
# Test that a sync's Drive connection is healthy
./gdrive-sync.sh --test-drive my-documents
# Dry run β walk through setup without writing anything
./gdrive-sync.sh --dry-run
# Verbose mode (extra rclone output + diagnostics)
./gdrive-sync.sh -v
# Combine flags
./gdrive-sync.sh -v --dry-run
# Run the test suite
./gdrive-sync.sh --test
# Help / version
./gdrive-sync.sh --help
./gdrive-sync.sh --versionEach sync creates a systemd service. You can manage them with standard tools:
# Check status
sudo systemctl status gdrive-sync-my-documents
# Stop syncing
sudo systemctl stop gdrive-sync-my-documents
# Restart
sudo systemctl restart gdrive-sync-my-documents
# View logs with journalctl
journalctl -u gdrive-sync-my-documents -fββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β gdrive-sync.sh (interactive manager) β
β β
β Sets up: β
β βββ rclone remote (OAuth + folder config) β
β βββ systemd service (runs the inotify watcher) β
β βββ cron job (periodic Drive β local pull) β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
ββ systemd service βββββββββββββββββββββββββββββββββββββββββββ
β β
β inotifywait -m -r (watches local folder) β
β β β
β βββ file change detected β
β βββ debounce (5 seconds) β
β βββ acquire lock (skip if another sync is running) β
β βββ rclone bisync local β remote β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
ββ cron job βββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
β Every N minutes: β
β rclone bisync local β remote β
β (catches Drive-side changes: web edits, mobile, etc.) β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
| Change made on | Detected by | Delay | Mechanism |
|---|---|---|---|
| Local filesystem | inotifywait |
~5 seconds (debounce) | systemd service |
| Google Drive (web/mobile/API) | Cron schedule | 1β15 min (configurable) | cron job |
When the same file is modified on both sides between syncs:
- The newer file wins (
--conflict-resolve newer) - The older version is kept as a renamed copy with a numeric suffix (
--conflict-loser num), e.g.report.docxβreport.docx.conflict1 - No data is ever silently lost
For each sync named <name>:
| File | Purpose |
|---|---|
~/.config/gdrive-sync/<name>.env |
Config (folder, remote, schedule) |
~/.config/gdrive-sync/<name>-watcher.sh |
inotify watcher script |
~/gdrive-sync-<name>.log |
Sync log (auto-rotated at 50 MB) |
/etc/systemd/system/gdrive-sync-<name>.service |
Systemd unit |
~/.config/rclone/rclone.conf |
rclone remote entry (shared file) |
Global defaults are stored in ~/.config/gdrive-sync/settings.conf and can be edited via menu option 9 (Settings) or by hand:
# ~/.config/gdrive-sync/settings.conf
DEBOUNCE_SEC="5" # Seconds before syncing after local change
LOG_MAX_SIZE_MB="50" # Rotate log above this size
MIN_RCLONE_VERSION="1.62.0"
DEFAULT_CRON="*/5 * * * *" # Default schedule for new syncs
LOG_LINES="50" # Default lines shown in log viewerThese values are used when creating new syncs and by the watcher scripts. Changes take effect on the next sync event or next run.
| Variable | Default | Description |
|---|---|---|
RCLONE_CONF |
~/.config/rclone/rclone.conf |
Path to rclone config |
GDRIVE_SYNC_DIR |
~/.config/gdrive-sync |
Where sync configs are stored |
If your server doesn't have a desktop/browser, rclone will print a URL during OAuth setup:
If your browser doesn't open automatically go to the following link:
https://accounts.google.com/o/oauth2/auth?client_id=...
Log in and authorize rclone for access.
Enter verification code>
- Copy the URL and open it in a browser on any machine
- Sign in and authorize
- Copy the verification code and paste it back into the terminal
Alternatively, you can run rclone authorize "drive" on a machine with a browser, then copy the token to your server. See rclone remote setup docs.
This means the bisync tracking files are out of date. Run a manual sync from the menu (option 7) β the script will offer to re-establish the baseline with --resync.
Google OAuth tokens from apps in "Testing" mode expire after 7 days. rclone handles auto-refresh, but if it fails:
# Re-authorize the remote
rclone config reconnect <remote-name>:Check that the cron job is running:
crontab -l | grep gdrive-syncCheck the log for errors:
tail -50 ~/gdrive-sync-<name>.logIf you get Failed to watch /path; upper limit on inotify watches reached!:
# Check current limit
cat /proc/sys/fs/inotify/max_user_watches
# Increase it (temporary)
sudo sysctl fs.inotify.max_user_watches=524288
# Make it permanent
echo "fs.inotify.max_user_watches=524288" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p# Check service logs
journalctl -u gdrive-sync-<name> -e --no-pager
# Verify the watcher script is executable
ls -la ~/.config/gdrive-sync/<name>-watcher.shUse the interactive menu to remove individual syncs (option 5). To fully clean up:
# Remove all services
for svc in /etc/systemd/system/gdrive-sync-*.service; do
name=$(basename "$svc" .service)
sudo systemctl stop "$name" 2>/dev/null
sudo systemctl disable "$name" 2>/dev/null
sudo rm -f "$svc"
done
sudo systemctl daemon-reload
# Remove cron jobs
crontab -l 2>/dev/null | grep -v 'gdrive-sync' | crontab -
# Remove configs and logs
rm -rf ~/.config/gdrive-sync
rm -f ~/gdrive-sync-*.log
# Optionally remove rclone remotes
rclone config show # review first
# rclone config delete <remote-name>- The script never runs as root β it uses
sudoonly for systemd operations - OAuth credentials are stored in
~/.config/rclone/rclone.conf(file permissions:0600) - Systemd services run with
NoNewPrivileges=trueandProtectSystem=strict - Client Secret is only entered once during setup and never logged
- No data leaves your server except to Google Drive via rclone's encrypted HTTPS connection
- Linux only β requires
inotifywaitandsystemd - Not real-time for Drive β local β remote changes are detected by polling (cron), not push
- Google API quotas β free tier allows 12,000 queries per day; very active folders with 1-minute cron may approach this
- Large files β very large files (multi-GB) may time out on slow connections; consider increasing the debounce
- Symbolic links β rclone does not follow symlinks by default
- Google Docs/Sheets/Slides β these are not real files on Drive; rclone can export them (see
rclone --drive-export-formats) but bisync of exported formats can be tricky
Pull requests welcome! See CONTRIBUTING.md for the full guide β how to fork, branch, test, and submit a PR.
To report a bug or request a feature, open an issue.
MIT β see LICENSE.