macOS: migrating Signal
#macos #signal #migration
SQLite database & its encryption key to a new Mac.
Table of Contents
macOS, really?
Turns out quite a few people across creative communities, from artists to open source developers to die-hard legacy UNIX enthusiasts use macOS as a daily driver. Definitely not fully open source (though Darwin is and some of the userspace is), but a weird mix of NeXTSTEP, FreeBSD & even some NetBSD code intertwined with Apple’s own free & non-free components. And yet, there are solid uses for it, so without further ado, let’s rock!
Whenever we upgrade Mac hardware, we face the same daunting choice of either:
- use Migration Assistant to get things up & running the quick & dirty way;
- use the new hardware as an opportunity to run a thorough cleaning operation, only transferring what is truly special, leaving years of clutter behind;
While a bunch of stuff can be synced through iCloud or by copying $HOME
(excluding ~/Library), this is not the case for Signal. Which is problematic
to say the least, since most if not all of the privacy-conscious and/or
technically inclined crowd tend to favor this open source messaging
system over proprietary alternatives.
Why this?
Signal Desktop is a linked device, not a standalone client — it only knows what happened while it was linked to your phone. If you unlink and re-link on a new computer, Signal will sync message history from your phone, but media older than 45 days is permanently gone from Signal’s servers. Text messages transfer fine, but photos, voice notes, and files do not survive beyond that window.
Signal doesn’t officially support desktop-to-desktop transfers (as their own backup & restore docs say so). Signal Secure Backups are phone-only and don’t cover desktop data either. But unofficially? That’s what this guide is for.
This means the desktop’s local SQLite database sitting in
~/Library/Application Support/Signal/sql/db.sqlite is the only copy of
your full desktop-side history including all media. If you lose it, it’s gone.
Gone: the love letters, the audio poetry, the enthusiastic screenshots. The photo club conversations, the impromptu video declarations. Everything visual, vibrant, precious — everything worth revisiting. These don’t just convey information: they cement relationships, actualize friendships, fuel movements.
So let’s move that database (and its encryption key) to a new Mac, and make Signal pick it up as if nothing changed. Ready?
Prerequisites
- Old Mac reachable via
ssh— this requires enabling Remote Login in System Settings > General > Sharing on the old Mac (set$OLDMACto its hostname or the IP of its point-to-point Thunderbolt bridge — see below, e.g.export OLDMAC=oldmac.local); - Homebrew
rsyncinstalled on the new Mac (macOS ships rsync 2.6.9 from 2006, which lacks flags we need); - Signal is quit on the old Mac (the database is SQLite: copying while Signal writes to it can produce a corrupt copy);
- Signal has never been opened on the new Mac (opening it before migrating will create a fresh identity and registration, making it much harder to restore the old database);
# Modern rsync (macOS built-in is ancient)
brew install rsync
# Install Signal on the new Mac **but do not launch it**:
brew install --cask signal
Go!
0. Verify
We need to make sure source data exists:
# Check the old Mac has got the goods
ssh "$OLDMAC" 'ls -lh ~/Library/Application\ Support/Signal/sql/db.sqlite \
~/Library/Application\ Support/Signal/sql/db.sqlite-wal' 2>&1
STOP if db.sqlite is missing. The -wal file may or may not exist (it’s a
SQLite write-ahead log); its absence is fine.
1. Sync
Any network path works here — Wi-Fi, Ethernet, whatever you’ve got. But if you have an Apple Thunderbolt 4 cable, plug it in: macOS automatically creates a Thunderbolt Bridge — a point-to-point network link between the two Macs, no router or switch needed. The cable supports up to 40 Gbps; real-world throughput over rsync/SSH will be lower (expect several Gbps — still dramatically faster than Wi-Fi or gigabit Ethernet, and the fastest Mac-to-Mac direct transfer you can get without Target Disk Mode).
The rsync and ssh flags below are tuned for that kind of bandwidth; on a
slower link they won’t hurt, they’ll just matter less:
# Bring all that precious Signal history over!
$(brew --prefix)/bin/rsync -avPHW \
-e "ssh -c aes128-gcm@openssh.com -o Compression=no" \
--exclude='*Cache*' --exclude='Logs' --exclude='*.log' --exclude='GPUCache' \
"$USER"@"$OLDMAC":Library/Application\ Support/Signal/ \
~/Library/Application\ Support/Signal/
rsync flags explained:
-aarchive mode — preserves permissions, symlinks, timestamps, recurses into directories-vverbose output-Pshows progress per file + enables resuming partial transfers if interrupted-Hpreserves hard links-Wtransfers whole files, skipping the delta/checksum algorithm — faster on high-bandwidth local links where computing diffs costs more than just sending the raw bytes
ssh tuning:
-c aes128-gcm@openssh.comlightweight cipher, reduces CPU overhead on a fast link-o Compression=noskips SSH-level compression — pointless at Thunderbolt speeds and wastes CPU
For sanity check, you’ll want to compare the total directory size on both ends to make sure the transfer is complete (database + all media attachments):
# Check sizes remotely, then locally
ssh "$USER"@"$OLDMAC" 'du -sh ~/Library/Application\ Support/Signal/'
du -sh ~/Library/Application\ Support/Signal/
Both should be roughly the same (the new side will be slightly smaller due to
the excluded caches). If it’s significantly smaller, the transfer may have been
interrupted, so just re-run the rsync command above (-P will resume where it
left off).
2. Restore
The keychain key really is where it’s at: without it, Signal can’t decrypt the database we just synced, and all that history is unreadable. This is the step that makes or breaks the whole migration.
We use security, macOS’s built-in command-line interface to the Keychain, to
pull the key straight from the old Mac and inject it into the local keychain in
one go! No intermediate file, the key only lives in a shell variable for a few
seconds.
Heads up: accessing the Keychain via SSH may trigger a GUI “Allow” dialog on the old Mac — make sure its screen is unlocked so you can approve it, otherwise the command will hang silently:
# Grab Signal key from remote keychain
SIGNAL_KEY=$(ssh "$USER"@"$OLDMAC" "security find-generic-password \
-s 'Signal Safe Storage' \
-w ~/Library/Keychains/login.keychain-db")
# Clear the local entry if present
security delete-generic-password \
-s "Signal Safe Storage" \
~/Library/Keychains/login.keychain-db 2>/dev/null
# Create a new local "Signal Safe Storage" with the Signal key
security add-generic-password \
-a "Signal" -s "Signal Safe Storage" \
-w "$SIGNAL_KEY" ~/Library/Keychains/login.keychain-db
Fallback: if you’d rather save the key to disk (safety net in case your terminal dies, or for safekeeping), run this on the old Mac instead:
# Save the Signal key to ~/migration-secrets.txt
printf 'Signal Safe Storage\tSignal\t%s\n' \
"$(security find-generic-password \
-s "Signal Safe Storage" \
-w ~/Library/Keychains/login.keychain-db)" \
> ~/migration-secrets.txt
# Don't let the whole world read it
chmod 600 ~/migration-secrets.txt
# Send it to the new Mac (or someplace else?)
scp ~/migration-secrets.txt "$USER"@"$NEWMAC":~/migration-secrets.txt
Then on the new Mac, restore from the file:
# Isolate the key
SIGNAL_KEY=$(grep "Signal Safe Storage" ~/migration-secrets.txt | cut -f3)
# Clear the local entry if present
security delete-generic-password \
-s "Signal Safe Storage" \
~/Library/Keychains/login.keychain-db 2>/dev/null
# Replace it with the Signal key
security add-generic-password \
-a "Signal" -s "Signal Safe Storage" \
-w "$SIGNAL_KEY" ~/Library/Keychains/login.keychain-db
# No need to leave secrets behind
rm ~/migration-secrets.txt
3. Launch
Open Signal. Close your eyes. Drumroll. Open your eyes: you should see your full conversation history with all media.
If it asks to link/register: QUIT IMMEDIATELY and rollback:
# Wipe it all away
rm -rf ~/Library/Application\ Support/Signal
security delete-generic-password \
-s "Signal Safe Storage" \
~/Library/Keychains/login.keychain-db 2>/dev/null
Then diagnose before retrying, as the database or key may be mismatched.
4. Enjoy
You may want to put that rsync command to good use and bring the rest of your
$HOME from $OLDMAC onto your new machine, including all the dotfiles &
documents, but don’t forget to --exclude Library in doing so — ~/Library
contains app sandboxes, caches, preferences, and databases tied to the local
macOS install. Syncing it over would clobber the new Mac’s fresh state with
stale or incompatible data (including the Signal migration you just did!),
which is exactly the clutter we’re trying to leave behind!
Most importantly, now may be as good a time as any to browse away, and go revisit those precious memories!