// notes
Firefox profile lost after profile-sync-daemon resync
After a Firefox restart the extensions vanished while bookmarks came back. How psd moved the profile into an empty fuse-overlayfs mount and how to restore it
After a restart Firefox threw the account out: bookmarks and history came back, extensions did not. Sync is not to blame — the browser opened an empty profile that profile-sync-daemon had swapped for a symlink into RAM via fuse-overlayfs, a temporary layer on top of the real profile. Below: how to spot this and bring the directory back from disk
Symptom
An empty extension list with bookmarks still alive is not a Sync failure. Sync carries bookmarks, history, passwords and open tabs, but it does not reinstall add-ons and does not keep their files
Key clue: bookmarks came back, extensions did not. That is the signature of a swapped profile, not a sync failure — Firefox opened a different, empty directory
The profile is still where profiles.ini points, but the folder itself turns out to be a symlink
$ ls -la ~/.config/mozilla/firefox/ | grep default-release
lrwxrwxrwx 1 user user 78 ... <profile>.default-release -> /run/user/$UID/psd/$USER-firefox-<profile>.default-releaseThe symlink leads into /run/user/$UID/psd, that is, into tmpfs — RAM that is wiped on reboot. The path ~/.config/mozilla/firefox is this distribution's layout: on other builds the profile lives in ~/.mozilla/firefox, so check profiles.ini first
Diagnosis
Five checks answer what happened
# profile folder from profiles.ini; replace xxxxxxxx with yours
PROFILE=xxxxxxxx.default-release
P="$HOME/.config/mozilla/firefox/$PROFILE"
# 1. where profiles.ini points
cat "$HOME/.config/mozilla/firefox/profiles.ini"
# 2. is the profile a symlink?
ls -la "$HOME/.config/mozilla/firefox/"
test -e "$(readlink "$P")" && echo OK || echo "broken symlink"
# 3. is the overlay mount alive
mountpoint -q "/run/user/$UID/psd/$USER-firefox-$PROFILE" \
&& echo mounted || echo "not mounted"
# 4. who did it and when
journalctl --user --user-unit psd-resync.service --since today --no-pager
# 5. when the profile was "created"
cat "$P/times.json"The sign of a swap is the created field in times.json: on a fresh profile it matches the time of the failure rather than the real creation date
A <profile>.default-release-backup folder usually sits next to it. If it holds places.sqlite, logins.json and extensions/, nothing is lost
Cause
profile-sync-daemon (psd) moves the browser profile into RAM to write less to disk. In fuse-overlayfs mode it does not copy files but assembles an overlay mount: the lower layer (lowerdir, read-only) is the real profile, the upper one (upperdir) is a temporary folder with changes, and a merged directory is visible on top of both
On the first run the service refused to work
First time running psd so please edit ~/.config/psd/psd.conf to your liking and run againpsd.service never performed the initial sync, so the profile stayed a regular folder. An hour later the psd-resync.timer fired and started psd-resync.service, which reported firefox sync successful in the journal. According to the code of /usr/bin/psd, the do_sync_for function does three things
PROFILE=xxxxxxxx.default-release
P="$HOME/.config/mozilla/firefox/$PROFILE"
T="/run/user/$UID/psd/$USER-firefox-$PROFILE"
mv "$P" "$P-backup"
fuse-overlayfs -o lowerdir="$P-backup",upperdir="$P-back-ovfs",workdir="$P-back-ovfs-work" "$T"
ln -s "$T" "$P"The mount did not survive. psd-resync.service is declared as Type=oneshot — a unit that exits right after it starts. Once the unit exits, systemd kills its whole cgroup, the group of processes, fuse-overlayfs included. The symlink stayed but points into an empty folder in tmpfs
Firefox started, saw an empty directory and created a new clean profile. Hence created in times.json equal to the moment of the failure. After signing into the account, Sync uploaded bookmarks and history, but the extensions never appeared
The real profile had been lying in -backup all along and stayed intact: Firefox kept the files open, and renaming a folder does not affect open descriptors. The profile kept being written until the browser was closed. PRAGMA quick_check confirms database integrity — a fast SQLite structure check without a full data scan
sqlite3 "file:places.sqlite?mode=ro" 'PRAGMA quick_check;' # okThe history in -backup is noticeably larger than in the new profile
Why psd missed the breakage: it checks mountpoint -q "$TMP" right after mounting, and the check passes. Nobody verifies that the mount will live until the end of the session — hence sync successful with a dead overlay
Fix
Order matters: edits with Firefox running or the mount alive risk data loss
Close Firefox completely, otherwise it keeps writing into the empty profile in RAM
Take a snapshot of the profile before any edits
PROFILE=xxxxxxxx.default-release
P="$HOME/.config/mozilla/firefox/$PROFILE"
cp -a --reflink=auto "$P-backup" "$HOME/firefox-profile-snapshot-$(date +%Y%m%d-%H%M)"Next comes a fork based on the mount state. If the mountpoint -q check from diagnosis succeeded, stop the service first via stop: its ExecStop is psd unsync, which merges the upper layer back into the profile, and only then disable autostart
systemctl --user stop psd.service
systemctl --user disable --now psd.service psd-resync.timer
systemctl --user reset-failed 'psd*'stop before disable is not a typo: disable --now stops the unit too, but the explicit order guarantees psd unsync gets to finish. If the mount is already dead, as in our case, stop achieves nothing and disable --now with reset-failed is enough
The profile is restored by hand
PROFILE=xxxxxxxx.default-release
P="$HOME/.config/mozilla/firefox/$PROFILE"
cd "$HOME/.config/mozilla/firefox"
unlink "$P"
mv --no-target-directory "$P-backup" "$P"Clear the stale locks: a killed Firefox process left them behind
PROFILE=xxxxxxxx.default-release
P="$HOME/.config/mozilla/firefox/$PROFILE"
rm -f "$P"/lock "$P"/.parentlockBefore deleting leftovers, look at what is inside. The upper layer (upperdir, the <profile>.default-release-back-ovfs folder) may hold records newer than -backup — for example if Firefox saved something between syncs
PROFILE=xxxxxxxx.default-release
P="$HOME/.config/mozilla/firefox/$PROFILE"
B="$P-back-ovfs"
du -sh "$B" 2>/dev/null
ls -la "$B"
cp -a --reflink=auto "$B" "$HOME/firefox-profile-back-ovfs-snapshot-$(date +%Y%m%d-%H%M)"After the snapshot, delete the empty leftovers, with a guard against an empty $UID
PROFILE=xxxxxxxx.default-release
P="$HOME/.config/mozilla/firefox/$PROFILE"
[ -n "$UID" ] && [ -d "/run/user/$UID/psd" ] && rm -rf "/run/user/$UID/psd/$USER-firefox-$PROFILE"
rm -rf "$P-back-ovfs"Start Firefox: profiles.ini needs no edit, the path is the same, the directory itself came back
Check
PROFILE=xxxxxxxx.default-release
P="$HOME/.config/mozilla/firefox/$PROFILE"
[ -L "$P" ] && echo "still a symlink" || echo "regular on-disk directory"
test -d "$P/extensions" && echo "extensions folder present" || echo "no such folder"
find "$P/extensions" -name '*.xpi' | wc -l # as many as before the failure
sqlite3 "file:$P/places.sqlite?mode=ro" 'select count(*) from moz_places;'Do this with the browser closed. If extensions/ is there and the files match what you had before the failure, the profile came back whole
Pitfalls
database is lockedwhen readingplaces.sqlitemeans Firefox is running: that is normal, not a failure, and the database must be checked with the browser closed- Do not delete
-backupand-back-ovfstoo early:-backupis the profile itself, and the upper layer may still hold fresher records. Comparelogins.json,key4.dbandcert9.dbfirst psd unsyncwith a live mount runsrsyncfromTMP/toBACK_OVFS/with--delete-after(removes from the receiver what the source lacks), then fromBACK_OVFS/toBACKUP/. If the overlay is dead that path will not work, so a snapshot before editing is mandatory- Sync does not bring add-ons back (see the box above): only the
extensions/folder together withextensions.jsondoes - The first psd enable requires manual config confirmation: the service exits silently while the timer stays active and later fires into the void — that is exactly how the breakage started
Takeaway
The key to the diagnosis is the mismatch between returned bookmarks and empty extensions. The profile is not lost if a -backup folder is next to it: close the browser, with a live mount stop the service first, then disable --now, remove the broken symlink and put the directory back in place
Turning psd back on is not needed until you understand why fuse-overlayfs does not survive the exit of a oneshot unit. Look at cgroup and KillMode — the rule by which systemd decides which unit processes to kill on exit. A profile cache in RAM saves disk writes and is not worth the risk of staying without a profile
Related notes
pkill -f kills itself: the pattern matches the command line
pkill -f and pgrep -f match the pattern against the whole command line, including the bash -c wrapper. Why the command kills itself and which filters help
Nix on CachyOS: install impossible — run it rootless
Nix will not install when the root is read-only, sudo is broken and /nix is baked into ELF. Three limits and a rootless run with mount namespace and chroot
Linux: “command not found” — PATH, ifconfig, pip
command not found on Linux: how to trace a binary through PATH, why sudo and login shells see a different path, and what replaces ifconfig and pip