Skip to content

GodsQuantum/SyncBridge

Repository files navigation

SyncBridge logo

SyncBridge

One UI for every cron, watcher, and sync across your servers.
A self-hosted control panel for your cron jobs ⏰, folder watchers 👀 and rsync/rclone backups 🔁 — with live logs, safety guards, run history, and nothing hidden from you.

🇫🇷 Version française du README


Stop scattering your automation across crontabs, background inotifywait loops, and one-off rsync scripts on every box. SyncBridge pulls all of it into one web UI: pick a trigger (a schedule, a folder change, or on-demand) and an action (an rsync/rclone sync, or any shell command/script) — and watch it run. Think cronmaster + an rsync web UI + an inotify manager, and now multi-server, in a single ~9 MB Go binary. No database, no build step, zero runtime dependencies: pull the image and go. 🐳

🖼️ Screenshots

SyncBridge dashboard

The dashboard — every job with its trigger and live status, an instance switcher for other servers, and expandable per-job run history.

SyncBridge system import

System import — SyncBridge sees your host's crontabs, systemd units and inotify watchers, and lets you import, reversibly disable, or permanently delete them (with a recoverable trash).

SyncBridge job editor

Composing a job visually — pick a type, a backend, a trigger. No script to write.

🤔 Why

If your homelab looks like mine — a CRON/ folder here, an inotify/ watcher there, a dozen rsync one-liners you half-remember, spread over two or three machines — SyncBridge rounds all of it up. The rule of the house: total visibility. Nothing runs that you can't see. Every execution streams stdout/stderr to a live dashboard, keeps a run history, and dangerous operations are refused before they can hurt you.

✨ Features

  • Job scheduler — standard 5-field cron to run any script or command (docker system prune, stack updates, DB exports, diagnostics…).
  • 👀 File watcher — watch a folder, filter by pattern (*.mp4), fire an action. NFS-safe: hybrid inotify + polling, because inotify alone never sees remote NFS writes, plus a write-completion check so half-copied files aren't synced.
  • ▶️ On-demand runner — trigger utility scripts manually, async, with streamed logs and a kill button.
  • 🔁 rsync / rclone sync — mirror / accumulate / move, checksum or time compare, bandwidth limit, exclusions, rotating safety trash, and a faithful system backup mode (ACL/xattr/hardlinks via --fake-super, restorable identically).
  • 🔎 System monitor & control — scans host crontabs, systemd units (.service/.timer/.path) and stray inotifywait processes, so nothing on your box escapes you. Import splits them into your triggers vs system triggers (OS/packages), and lets you reversibly disable/re-enable a host trigger (cron lines commented with #SB-OFF#, systemd toggled via systemctl) or permanently delete one — batched, gated behind typing delete, and backed up to a recoverable trash. Needs a read-write host mount (see compose.example.system.yaml).
  • 🕸️ Multi-instance control — run SyncBridge on several boxes and drive them all from one UI (like Dockge agents). Add a remote by URL + login; the top-right switcher then pilots its jobs, its system import and its live logs through a built-in reverse proxy, with an online/offline badge per instance. You can't mount another server's system volumes across the network — so you talk to its SyncBridge instead.
  • 🕒 Run history — each job keeps its last runs (status, duration, first error line), persisted across reboots and shown inline.
  • 🔔 Failure notifications — point SyncBridge at an ntfy topic or any webhook; failed runs (optionally successes too) send a notification. One field, no extra container.
  • 🔐 Login & hardening — a themed web login (account created on first launch, password hashed with bcrypt), brute-force lockout with backoff, expiring sessions, and at-rest encryption (AES-GCM) of stored remote passwords using an auto-generated key in /config. Since the UI can run scripts on your box, never expose it without auth — keep it on your LAN / behind Tailscale or a reverse proxy.
  • 🌍 Bilingual UI (EN default / FR toggle) with keyboard navigation, focus outlines and aria labels for accessibility.

🛡️ Safety first

SyncBridge refuses to shoot you in the foot:

  • 💥 Empty/unmounted source guard — the classic rsync disaster: a mirror whose NFS source got unmounted looks empty, and --delete wipes the destination. SyncBridge aborts the run instead.
  • 🔒 Anti-overlap lock — a job never stacks on top of itself (fast watchers, cron overrunning its own interval).
  • ⏱️ Timeout — a hung job is killed after N seconds (whole process group), because a job that hangs forever is worse than one that fails.
  • 🚧 Delete guardrails — mirror refuses source == dest and dest-inside-source; optional max-deletions abort.
  • Write-completion check — a watcher waits for the folder to go quiet (stable size) before firing.
  • 🗑️ Recoverable trash — deleting a host trigger backs it up first; restore it from the Trash view.

🚀 Install (pre-built image — no build)

mkdir syncbridge && cd syncbridge
curl -O https://raw.githubusercontent.com/GodsQuantum/SyncBridge/main/compose.example.yaml
curl -O https://raw.githubusercontent.com/GodsQuantum/SyncBridge/main/.env.example
cp .env.example .env
# adjust the volumes in compose.example.yaml to your paths, then:
docker compose -f compose.example.yaml up -d

Open http://<server-ip>:8788 🌐. On first launch the UI asks you to create an account (or set SYNCBRIDGE_USER/SYNCBRIDGE_PASSWORD to skip that). Everything lives in ./data (/config): jobs, account, run history, notification settings, the encryption key and any remote-instance credentials — back up that folder and you've backed up everything. The image is multi-arch (amd64 + arm64) on ghcr.io/godsquantum/syncbridge:latest.

Two compose variants ship: compose.example.yaml (read-only — SyncBridge sees your host triggers but never touches them) and compose.example.system.yaml (mounts host cron read-write so SyncBridge can disable/delete/write host cron). Start with the read-only one.

📋 Step by step

  1. Create the config folder and grab the two files above (compose.example.yaml, .env.example).
  2. Write your .env — sensible defaults ship in .env.example; the only thing most people change is TZ.
  3. Pick your volumes (bind mounts). The only required one is ./data:/config. Map the host folders you sync between, the scripts you want to trigger (:ro is enough), and — if you want the System Monitor — the /host/... mounts. Every line is documented inline in compose.example.yaml, including the optional Docker socket (for docker jobs) and the rshared /mnt mount (for nested NFS).
  4. docker compose up -d, open the UI, create your first job. Use dry-run on syncs before trusting them. 🧪

🎛️ Trigger types

Trigger Fires when Example
🖐️ Manual you click Run YT.sh <url>
⏰ Cron a schedule 0 4 * * *docker system prune -f
👀 Watch a folder changes a .mp4 lands → run an rsync, or an ffmpeg script

⚙️ Execution backends (per job)

Every job picks where it runs:

  • 🟢 SyncBridge backend (default) — SyncBridge runs the job itself: live logs, kill button, clean shutdown, anti-overlap lock. Simple and fully visible. Stops if the container stops.
  • 🟣 System backend — SyncBridge writes a real host cron entry (/etc/cron.d, with flock + PATH) or a systemd .path/.service unit, so the job keeps running even if Docker/SyncBridge is down. Invariant: the artifact is removed before any rewrite and cleaned up on delete or backend switch — never a hidden duplicate. Writing host cron needs a read-write mount; the systemd variant needs a privileged container (see compose.example.yaml).

🕸️ Multi-instance (pilot other servers)

Install SyncBridge on each machine, then from one instance add the others by URL + their login (the top-right button). That instance proxies every API call to the selected remote, so its jobs, system import, trash and live logs all work as if local — you drive another box's crons without mounting its disks, and a 🟢/🔴 badge shows which instances are up. Requirement: the piloting instance must be able to reach the remote over the LAN (e.g. http://192.168.1.50:8788). Remote passwords are encrypted at rest. Keep it inside your LAN / Tailscale; the proxy is admin-only but there's no TLS between instances yet.

🕒 History & 🔔 notifications

Each job keeps its recent runs (status, duration, first error line) in /config/history.json — click the clock icon on a job to expand them. Under Settings (⚙), set a notification URL: SyncBridge sends a text POST with a Title header on failures (and optionally successes), which works natively with ntfy and most webhooks. A Test button confirms it end-to-end.

🔧 Configuration (environment variables)

All optional — sensible defaults ship in .env.example. Nothing here changes the volumes you mount.

Variable Default What it does
TZ UTC Container timezone (drives cron times).
SB_DATA /config Where jobs, history, settings and keys live.
SB_UID / SB_GID 1000 Owner of files SyncBridge writes (config stays 1000 even when the container runs as root).
SB_PORT 8787 Internal port. Mapped via ports: in compose; override only to run several instances bare-metal.
SB_IMPORT_PATHS /import/scripts:/import/crontab Colon-separated folders scanned for rsync/rclone commands to import.
SB_SYS_CRON_PATHS / SB_SYS_SYSTEMD_PATHS host cron / systemd Where the System Monitor reads host triggers.
SB_SECRET (auto) Encryption key for stored remote passwords. If unset, a random key is generated in /config/secret.key — no compose change needed.
SB_LOCK_COMMANDS (off) Set to 1 to forbid creating command/script jobs on this instance (locks it to sync-only).
SYNCBRIDGE_USER / SYNCBRIDGE_PASSWORD (unset) Pin the account via env instead of the first-run register screen.

🔌 API

GET/POST   /api/jobs             list / create
PUT/DELETE /api/jobs/{id}        edit / delete
POST       /api/jobs/{id}/run    run (?dry=1 to simulate a sync)
GET        /api/jobs/{id}/stream live logs (SSE)
GET        /api/jobs/{id}/history recent runs
POST       /api/jobs/{id}/kill   stop the running job
GET        /api/system/scan      host triggers detected (read-only)
GET        /api/import/scan      rsync/rclone commands found in your scripts/crontab
POST       /api/system/toggle    reversibly disable/re-enable a host trigger
POST       /api/system/delete    permanently delete host triggers (batch, confirm="delete")
GET        /api/system/trash     recoverable deleted triggers  ·  /api/system/restore
GET/POST   /api/remotes          list / add a remote instance   ·  DELETE /api/remotes/{id}
ANY        /api/remote/{id}/...  proxy to a piloted remote instance
GET/POST   /api/settings         notification settings (POST test=true to send a test)
POST       /api/auth/register    first-run account creation
POST       /api/auth/login       log in  ·  /api/auth/logout  log out

🛠️ Development

go build -o syncbridge .   # static binary
go test ./...              # unit tests
docker build -t syncbridge .

One Go package, a handful of files: main.go (jobs, scheduler, HTTP), sysmon.go (host scan), system_backend.go (host cron/systemd writer), sys_toggle.go (disable/delete/trash), remote.go (multi-instance proxy), secrets.go (at-rest encryption), notify.go (settings + notifications), history.go (run history), auth.go (login), and web/. Easy to fork. 🍴

🗺️ Roadmap

USB auto-ingest triggers · optional TLS between piloted instances · per-job history charts.

🔍 Keywords

self-hosted cron manager · cronmaster alternative · rsync web UI · rclone GUI · inotify watcher manager · folder-watch automation · multi-server orchestration · homelab control panel · docker cron scheduler · centralize Linux scripts · systemd timer dashboard · self-hosted task scheduler.

📄 License

MIT — see LICENSE. Contributions welcome. ❤️

About

SyncBridge — One UI for every cron, watcher, and sync across your servers. Self-hosted control panel for your cron jobs, file watchers & rsync backups. Live logs, safety guards, pre-built Docker image.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages