Self-host Mozilla Firefox Sync with docker-compose — syncstorage-rs and the tokenserver stack backed by MariaDB instead of Google Spanner.
This is a maintained fork of porelli/firefox-sync. It carries two fixes not yet merged upstream and publishes production-tested images — everything good here originates upstream; see Credits.
- Fresh deployments work — upstream's
db_init.shhardcodednodes.service = '1', but the migrations guaranteesync-1.5lands at id 3 on every fresh database, so no login could ever resolve a node (503 "unable to get a node"). The service id is now resolved dynamically — upstream PR #23. - syncstorage-rs 0.23+ builds correctly — upstream renamed
DATABASE_BACKENDtoSYNCSTORAGE_DATABASE_BACKEND/TOKENSERVER_DATABASE_BACKEND(defaulting to Spanner); the image workflow now passes both, so 0.23 images don't silently build for Spanner and panic on MySQL URLs — upstream PR #24. - Images published under ghcr.io/tmiland/firefox-sync — weekly rebuilds against the latest upstream tag, running in production.
| Service | Role |
|---|---|
syncstorage |
syncstorage-rs (MySQL backend). Since 0.23 the tokenserver is embedded and both migration sets run at startup |
syncstorage_db |
MariaDB for BSO storage (bookmarks, passwords, tabs, history…) |
tokenserver_db |
MariaDB for the tokenserver (OAuth token → node mapping, user allowlist) |
tokenserver_db_init |
one-shot init: waits for syncstorage to be healthy, then seeds services/nodes (dynamically resolved ids) and the MAX_USERS trigger — Exited (0) is success |
syncstorage listens internally on port 8000; a reverse proxy is expected in
front (nginx example in config/nginx).
Health probe: GET /__heartbeat__.
⚠️ The project is under development⚠️ Expect bugs, breaking changes and headache⚠️ This is not endorsed or supported by Mozilla in any way or form⚠️ Do not solely relay on this project to store your bookmarks, passwords and/or other important items
syncstorage-rs does NOT support account allowlisting. This means that ANY person that has network access to your server can use it.- This has been implemented with a SQL trigger workaround that prevents the token database to insert more rows when a new user tries to use the server. This is tested and prevents a new account from using your server if the number of MAX_USERS defined in your .env file is already reached.
- Possible alternative (better) solutions:
- implement the feature directly in syncstorage-rs
- add the entire rest of the Mozilla stack so that authentication is performed and validated locally
Mozilla's server side components are open source and Firefox allows to easily change the official endpoints.
Some documentation is provided to install each component on your own server but this is neither officially supported or very well maintained. Furthermore, there are no official Docker images that can be used to avoid installing everything manually; all the instructions and artifacts provided are focused on setting up a developer environment rather than a production self hosted service. For example, syncstorage-rs has a docker release on docker-hub but it is targeted to work exclusively with Google Spanner which is what Mozilla uses to provide the service.
Both images are rebuilt weekly against the latest tag from Mozilla's official repositories and published to ghcr.io/tmiland/firefox-sync (workflow: syncstorage-rs.yml):
syncstorage-rs-mysql-latest— Mozilla's container built withSYNCSTORAGE_DATABASE_BACKEND=mysqlandTOKENSERVER_DATABASE_BACKEND=mysql(both old and new arg names are passed, so pre-0.23 tags keep working). Code changes: none. (arm64: the upstream Dockerfile is patched to uselibmariadb-dev-compat.)syncstorage-rs-mysql-init-latest— MariaDB's container with the db_init.sh seeding script (dynamic service-id resolution). Code changes: none.
- clone this repository
- run
./prepare_environment.shto automatically prepare your.envfile and conf examples according with your variables (REPOSITORYdefaults toghcr.io/tmiland/firefox-sync) - setup your reverse proxy server
- if you use nginx, check the syncstorage-rs.conf as example
- start docker compose:
docker compose up -d - verify:
curl https://your-domain/__heartbeat__returns200andtokenserver_db_initshowsExited (0) - OPTIONAL: Install the systemd service (see firefox-sync.service) and enable it
- all the containers are already set to restart automatically; stopping Docker (for example when you shutdown your computer) will automatically stop all the services gracefully and restart them once Docker is starting again
Pre-requisite: if you already logged into your Firefox account you need to temporarily disconnect it
The below examples assume your server respond to this domain: firefox-sync.example.com
- point a browser tab to
about:configand search foridentity.sync.tokenserver.uri - change it from the default to
https://firefox-sync.example.com/1.0/sync/1.5 - log in to Firefox and start syncing.
- check logs pointing a browser tab to
about:sync-log
- go to App Menu
⋮>Settings>About Firefoxand click the logo 5 times. You should see adebug menu enablednotification - go back to the main setting menu and you will see
Sync Debugat the top, just under theSynchronize and save your databox. Tap on it - tap on
Custom Sync serverand set it tohttps://firefox-sync.example.com/1.0/sync/1.5 - log in to Firefox and start syncing.
- go to App Menu
≡>Settingsand tap 5 times on the version number (i.e.:Firefox 127.1 (42781)) towards the bottom - go back at the top of the main setting menu and you will see
Advanced Sync Settingsat the top, just under theSync and Save Data. Tap on it - activate
Use Custom FxA Content Serverand setCustom Account Content Server URItohttps://firefox-sync.example.com/1.0/sync/1.5 - log in to Firefox and start syncing.
- 503 "unable to get a node" on login — you're on an init image with the hardcoded service id. Pull the latest
syncstorage-rs-mysql-init-latest, re-rundocker compose up -dto recreate the init container, and (once) wipe the tokenserver DB volume so it re-seeds — see upstream PR #23. - Panic
Invalid database url: mysql://…on syncstorage-rs 0.23 — the image was built for Spanner (old build-args). Pull the latest image built by this fork's workflow — #24. - Heartbeat check — the health endpoint is
GET /__heartbeat__(200), not/1.0/sync/1.5/heartbeat.
- porelli for porelli/firefox-sync — this fork's base; fixes are proposed upstream as #23 and #24
- Mozilla for Firefox and opensourcing all their software, including the backend
- jeena for fxsync-docker which is the inspiration for this project
GPL-3.0 — inherited from upstream.