Cached at:
05/12/26, 11:18 AM
# Unison In Anger
Source: [https://blog.feld.me/posts/2026/05/unison-in-anger/](https://blog.feld.me/posts/2026/05/unison-in-anger/)
I use the Keepass family of software for my password management and on my iPhone I can use the wonderful[KeePassium](https://keepassium.com/)which supports syncing the password database over WebDAV for self\-hosting your "cloud" sync infrastructure, but[KeePassXC](https://keepassxc.org/)on Linux/FreeBSD offers nothing of the sort\. If you want to sync your password databases, you have to figure it out yourself\.
Typically at this point someone will suggest a DropBox\-style file sync software like Syncthing\. That would work, but let's be honest: we're talking about running a pretty heavy service, which even though it's written in Go, is still consuming ~400MB of RAM on one of my servers\.
I've seen people suggest git, but that's also heavy\. My primary password database is almost 2MB and KeePass rewrites the whole file with every change, so that git repo will grow a lot over time\.
Rsync? But doing it two\-way is going to be tricky\.\.\.
We're currently living through a severe memory shortage\. There must be something simpler, right? Decades of people working with files on \*nix and the we best we can do is Syncthing?
I continued searching for a lightweight file sync utility for \*nix and mostly hit dead ends but then Unison came up\.

This seemed promising\.
[## Enter Unison](https://blog.feld.me/posts/2026/05/unison-in-anger/#enter-unison)
[Unison](https://github.com/bcpierce00/unison)\.\.\. I've known about you for literally decades\. Tried a GUI version of you for 5 minutes, got confused, never looked again\. It's time to take a deeper look\.
About
Unison is a file\-synchronization tool for POSIX\-compliant systems \(e\.g\. \*BSD, GNU/Linux, macOS\) and Windows\. It allows two replicas of a collection of files and directories to be stored on different hosts \(or different disks on the same host\), modified separately, and then brought up to date by propagating the changes in each replica to the other\.
Unison has been around since 1998, and like all software that was written by people who care about its users it comes with[an incredibly detailed manual](https://raw.githubusercontent.com/bcpierce00/unison/documentation/unison-manual.pdf)\. Just look at this thing\! It's wonderful, just like the official Bacula docs\.
On the other hand, it's overwhelming\. There's so much going on in here\. I had one goal though: figure out how to use this to solve my problem*without*requiring something heavy like SSH\. The docs do say you can have it listen on a socket, but tries to scare you away\.
Warning
The TCP socket method is insecure: not only are the texts of your changes transmit\- ted over the network in unprotected form, it is also possible for anyone in the world to connect to the server process and read out the contents of your filesystem\! \(Of course, to do this they must understand the protocol that Unison uses to communicate between client and server, but all they need for this is a copy of the Unison sources\.\) The socket method is provided only for expert users with specific needs; everyone else should use the ssh method\.
Hello 👋 I am an expert user and would like to do this, thank you\!
[## Making It Work](https://blog.feld.me/posts/2026/05/unison-in-anger/#making-it-work)Here is my plan and my goals\.
- Sync an entire directory which can have many KeePass database files in it
- Do not allow any client to delete any file \(prevent "oops, bad sync state; deleted everything"\)
- Make this as fast and lightweight as possible
Working with Unison and the socket mode proved difficult\. So difficult in fact that my specific configuration doesn't have an example anywhere in the docs at all\. I literally had to grind the source code through an LLM to identify the exact syntax required to make it work\. Below is the result\.
[### Server](https://blog.feld.me/posts/2026/05/unison-in-anger/#server)The server side runs like this:
`UNISON=/var/db/unison /usr/local/bin/unison default \-socket 8080`
Inside`/var/db/unison`is a config profile,`default\.prf`:
```
# /var/db/unison/default.prf
# Beneath this root is a directory called "keepass".
# That is what we will actually sync.
root = /path/to/dir
# we don't want to preserve user/group ownership when syncing
owner = false
group = false
# this backup line tells it what to include -- glob is everything
backup = Name *
# Notice "keepass" is mentioned here
nodeletionpartial = BelowPath keepass -> /path/to/dir
```
Important
**nodeletionpartial**prevents Unison from performing any file deletion\. In this case it applies to anything below the keepass directory\.
[### Client](https://blog.feld.me/posts/2026/05/unison-in-anger/#client)In my home dir I have this file for the client's profile\. I'm syncing the keepass directory as a subdir of`/home/feld/unison`
```
# /home/feld/.unison/default.prf
# Yes, two roots. Weird eh?
root = /home/feld/unison
root = socket://192.168.1.100:8080//path/to/dir
batch = true
silent = false
auto = true
owner = false
group = false
times = true
prefer = newer
# dirs to sync
path = keepass
# On the client side, we also tell it to not delete. Just in case...
# And it has to be without the port here. Confusing, right? Thanks Claude
nodeletionpartial = BelowPath keepass -> 192.168.1.100
```
Alright\! Now when you run`unison default`it should sync\. Just pop this baby in crontab and move on with your life:
```
# minute hour mday month wday command
MAILTO=""
*/5 * * * * /usr/local/bin/unison default 2>&1 >/dev/null
```
Now it will sync every 5 mins which is more than enough considering how rarely I make password changes\. If I'm ever paranoid I can just run`unison default`to verify it's synchronized before I make any changes\. Worst case scenario I somehow clobber it due to bad time sync on my client fooling the server into thinking I have a newer file, but I have ZFS snapshots and Bacula backups so I'm not too worried about that\.
[## But Security\!](https://blog.feld.me/posts/2026/05/unison-in-anger/#but-security)Ok, I know this is not the best because I'm not securing the transport at all\. Get creative\. My suggestion is to use something like[stunnel](https://www.stunnel.org/)or[spiped](https://www.tarsnap.com/spiped.html)\. I ended up picking spiped and running it as a daemon on my clients which creates the unix socket`~/\.unison/s\.pipe`, and then I tell Unison to work through that\. This required a few changes on the client and server\. The details of the spiped/stunnel configuration are not interesting and easy enough for anyone to figure out, but the server and client changes I had to make are as follows:
[### Server](https://blog.feld.me/posts/2026/05/unison-in-anger/#server-1)I made it listen on`127\.0\.0\.1:8081`, and then spiped will listen on`0\.0\.0\.0:8080`so I don't have to change any firewall rules\. It's the`listen`flag that controls the IP address you bind to, while socket is only the port\. \(strange, right?\)
`/usr/local/bin/unison default \-listen 127\.0\.0\.1 \-socket 8081`
Note
I could have made Unison listen on a unix socket instead of`127\.0\.0\.1:8081`, but I'll leave that as an exercise for the reader\.
[### Client](https://blog.feld.me/posts/2026/05/unison-in-anger/#client-1)I had to augment the client's config profile to have these values:
```
root = socket://{/home/feld/.unison/s.pipe}//path/to/dir
nodeletionpartial = BelowPath keepass -> {/home/feld/.unison/s.pipe}
```
[## sync; sync; sync](https://blog.feld.me/posts/2026/05/unison-in-anger/#sync-sync-sync)Conclusion: it works\. It's fast\. It uses almost no resources\. Thank you Benjamin C\. Pierce for making this tool\. I have no idea who tortured you to implement so many different features in Unison\. This blog post should probably have been a docs pull request, but do we really want to encourage more people to do that? Hmm\.\.\.