CLI tool
deajan/osync avatar
deajan/osync

osync: a stateful two way rsync wrapper written in bash

A robust two way (bidirectional) file sync script based on rsync with fault tolerance, POSIX ACL support, time control and near realtime sync

998 stars105 forksShellBSD-3-Clause

At a glance

What is it?
osync wraps rsync in a stateful, bidirectional synchronizer that keeps conflict backups, soft deletes, ACLs and a run history. It suits operators who want scheduled two replica sync on a plain server, and it punishes anyone expecting a modern client and server pair.
Who is it for?
osync fits a server administrator who already runs bash, wants one scheduled job to mirror two directories in both directions, and accepts keeping a state directory and a retained backup tree on each side. It does not fit a team that needs a modern GUI, a real time watch, or documented conflict resolution.
Can I use it commercially?
Yes. BSD-3-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 7 days ago.
What is it written in?
Mainly Shell, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A stateful wrapper that compares replica file lists between runs

osync is a bash script that sits on top of rsync and turns it into a two way file synchronizer. The design decision that shapes everything else is that it is stateful: rather than watching the filesystem for changes, it compares the file lists of the two replicas from the previous run. The README describes this as an agentless design, and the practical consequence is that the remote side does not need to run anything for a plain scheduled sync.

Two numbers are given for the cost of a full run: about 2 seconds on a local to local replication and about 7 seconds on a local to remote one. Those figures describe the overhead of the wrapper around the transfer, not the transfer of a large payload, and the README notes that switching off attribute preservation and disk space checks makes execution faster. The list of features is long: local and remote sync, fault tolerance with resume scenarios, POSIX ACL and extended attribute synchronization, soft deletions with multiple backups, before and after run command execution, email alerts, logging, directory monitoring, and running on a schedule or as a daemon.

The initiator has to be local and the target may live on another host

osync uses an initiator and target schema. By definition the initiator replica is always a local directory on the system where osync runs, and the target is either a second local directory or a remote one reached over ssh. The naming is recent enough to trip up old configurations: the upgrade notes state that since v1.1 the config file format changed in semantics, and that master was renamed to initiator while slave was renamed to target.

Paths in osync are quoted with double quotes, and the README asks you not to use escaped characters in path names. That is a real constraint if you plan to wrap the tool in a script, since a directory name containing a quote or a backslash will not behave the way a shell user might expect. The target accepts an ssh style address with a port and a key path, so a remote pair is configured from the initiator host alone.

Pidlocks keep two runs away from the same pair of replicas

Concurrency is handled with pidlocks. osync uses them to prevent multiple concurrent sync processes operating on the same initiator and target pair, while still allowing several osync tasks to run on one system as long as the replicas involved are different. That is a narrower guarantee than a general scheduler, so a cron entry and a manual invocation against the same pair is exactly the case the lock exists to catch.

For the sequential case there is osync-batch.sh, a batch runner that launches multiple osync tasks one after another and offers a rerun option for tasks that failed. The repository root also holds osync-srv, osync-target-helper-srv and matching systemd unit templates such as [email protected] and [email protected], alongside target-helper.conf.example. Those files indicate a helper process that can be started per replica, which is the piece that makes the remote side reachable for pushes rather than pure pulls.

Installing from git and running the first quick sync

The quickstart is three commands. The stable branch is the sensible default for a first run, and install.sh places the script in /usr/local/bin while an example configuration file goes to /etc/osync.

bash
	$ git clone -b "stable" https://github.com/deajan/osync
	$ cd osync
	$ bash install.sh

The installer takes parameters for automation, and install.sh --help lists them. An RPM build is published for RHEL, CentOS and Fedora style distributions, and it installs to /usr/bin instead of /usr/local/bin to follow that convention. osync requires bash; the README states that using any other shell will most probably result in errors, and that BSD and BusyBox hosts need bash installed. If installation is not possible, you can copy osync.sh to the location you need and run it from there.

Quick sync mode is the fastest way to see the tool work, and it takes both sides as arguments:

bash
	# osync.sh --initiator="/path/to/dir1" --target="/path/to/remote dir2"
	# osync.sh --initiator="/path/to/another dir" --target="ssh://[email protected]:22//path/to/dir2" --rsakey=/home/user/.ssh/id_rsa_private_key_example.com

The first line is a local to local copy. The second points the target at a remote host over ssh with an explicit key. A stripped down variant skips attribute transfer, soft deletion, prior space checks and the remote connectivity probe, which is useful for a first look but removes the safety nets:

bash
	# MINIMUM_SPACE=0 PRESERVE_ACL=no PRESERVE_XATTR=no SOFT_DELETE_DAYS=0 CONFLICT_BACKUP_DAYS=0 REMOTE_HOST_PING=no osync.sh --initiator="/path/to/another dir" --target="ssh://[email protected]:22//path/to/dir2" --rsakey=/home/user/.ssh/id_rsa_private_key_example.com

Config file mode is where backups, ACLs and alerts actually live

Quick sync mode is a convenience, and the config file mode is where the feature list becomes reachable. The example file sync.conf.example in the repository root is the starting point, and the keys map directly to the documented capabilities: soft deletion with a retention in days, conflict backups, POSIX ACL and extended attribute preservation, a minimum free space check, and a remote host ping. The same knobs can be passed as environment variables on the command line, as the minimal example above shows.

The one-time cost of that richer mode is a configuration migration. Since v1.1 the file format changed in meaning and gained options, and v1.3 added more, so an old configuration file has to be rewritten:

bash
	$ ./upgrade-v1.0x-v1.3x.sh /etc/osync/your-config-file.conf

The script backs up the configuration file, rewrites its content, and then tries to connect to both replicas to update the state directory. That last part matters, because osync stores its idea of what each replica looked like between runs, and a configuration change that skips the state update leaves the synchronizer comparing against a stale file list.

Other operational features named in the README include running commands before and after a run, email alerts, a logging facility, directory monitoring, and the ability to run on a schedule or as a daemon. None of these are described in detail in the quickstart, so the full documentation on the author site at http://www.netpower.fr/osync is where the actual semantics live.

The failure modes: no rollback, bash dependency, and a first run with no history

osync is described as designed never to delete data, but to keep backups of conflicting files or to soft delete instead. Even so, the README advises keeping a clean backup of your data before trying a new sync tool, which is the sensible reading of a tool that manages retention on your behalf.

The README does not document a rollback command, and it does not define a conflict resolution policy beyond retaining the conflicting copies. If two replicas change the same file between runs, osync keeps both versions, but which one wins in the working tree, and how a user is meant to pick, is not spelled out. The state directory is the memory of the tool: because osync compares replica file lists between runs, the first run has no previous list to compare against, so its behaviour on an existing tree is the least predictable part of the lifecycle.

Second, bash is a hard requirement, and hosts whose default shell is not bash need an explicit `bash osync.sh` invocation. Third, the minimal command line disables the disk space check and the remote connectivity check, so a run can report success in situations a normal run would have caught. Anyone who copies the fastest invocation into a cron job is giving up the checks the tool was written to perform.

Where plain rsync is the better answer

osync is documented as an rsync wrapper with additional features baked in, so rsync is already doing the transport work. The difference in approach is everything the wrapper adds on top: a persistent state directory that records the previous file list, a decision about which replica is the initiator, backups of conflicting files, soft deletions with a retention window, a lock file per replica pair, and the option to run unattended.

If the job is a single copy between two directories, or a one direction mirror from a build output to a publish location, that machinery is dead weight. A direct rsync call does the transfer with no state to corrupt, no lock to reason about and no backup tree filling the disk on a schedule. osync earns its overhead when the same pair of directories must converge repeatedly from both ends, where the previous file list is what makes the next run cheap and where a conflict on a laptop that reconnects has to be recoverable rather than overwritten.

The same reasoning applies to monitoring. osync offers a monitor mode triggered on file changes and a daemon mode, but the stateful comparison approach means the plain scheduled run already catches changes without a watcher. Picking the mode is a question of latency tolerance, not of correctness.

Maintenance: pushed in October 2026, tagged in 2023

The repository is not archived and the last push was on 2026-10-16, so the script itself is still receiving changes. The release history is a different picture. The most recent tag is v1.3 from 2023-06-15, preceded by v1.3-rc3 from 2022-07-07 and v1.3-beta3 from 2019-05-22, so a clone of the stable branch is several years behind the head of master and the tagged releases are old relative to recent commits.

That gap matters for upgrade planning. The config format changed at v1.1 and gained further options at v1.3, and the repository carries the upgrade-v1.0x-v1.3x.sh script to migrate older files. The cost of staying current is therefore a config rewrite rather than a binary swap, since the deliverable is a script and an example configuration file. The project is distributed under the BSD 3 Clause licence, recorded in LICENSE.TXT, and the CHANGELOG.md and KNOWNISSUES.md files at the repository root are where behaviour changes and known problems are tracked. A KNOWNISSUES file in the root of a sync tool is a good sign that the author expects failures, but it also means the honest answer on whether a given edge case is safe comes from that file and not from the feature list.

Editorial conclusion

osync fits a server administrator who already runs bash, wants one scheduled job to mirror two directories in both directions, and accepts keeping a state directory and a retained backup tree on each side. It does not fit a team that needs a modern GUI, a real time watch, or documented conflict resolution. Before adopting it, check three things: that the rsync version on the host supports the ACL and extended attribute flags the script turns on, that a full run of the config file mode completes inside your cron window, and that you can accept a bash dependency on hosts whose default shell is something else.

Frequently asked questions

Is osync still maintained?

The repository is not archived and the last push was on 2026-10-16. The most recent tagged release is v1.3 from 2023-06-15, so tagged versions lag behind the current code.

Does osync need rsync and bash installed?

It is an rsync wrapper, so rsync is required, and it must run under bash. The README states that using any other shell will most probably result in errors, and that bash has to be installed on BSD and BusyBox systems.

How do I migrate an old osync configuration file?

Run the upgrade script in the repository root against your config file. It backs the file up, rewrites its content, and tries to connect to both replicas to update the state directory.

Does osync overwrite files that changed on both sides?

The documentation says osync is built not to delete data, and instead keeps backups of conflictual files or soft deletes them. A rollback command and a detailed conflict resolution policy are not documented.

Can osync sync a remote directory over ssh?

Yes. The target accepts an ssh style address with a port and a key path, and the initiator replica is always a local directory on the machine where osync runs. A full local to remote run is described as taking about 7 seconds.

Official sources

  1. deajan/osync on GitHub
  2. License: BSD-3-Clause
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/deajan-osync.svg)](https://hysenlabs.com/projects/deajan-osync)

Was this useful?