If you want a fully open-source, self-hostable setup where the client writes locally first and then does bidirectional sync when online, the most straightforward choice is:
Best “end-to-end” fit: PouchDB (client) + Apache CouchDB (server)
- Client DB: PouchDB runs in the browser (typically backed by IndexedDB).
- Server DB: CouchDB.
- Sync: Built-in, multi-master replication designed specifically for offline-first; you sync PouchDB ↔ CouchDB (optionally live/continuous) and handle conflicts via document revisions. (PouchDB)
Why it’s usually the default recommendation
- It matches your requirements most literally: local writes first, then push/pull when connected, with the database and sync protocol being the core product. (PouchDB)
- It’s a well-trodden architecture for PWAs.
Tradeoffs
- You’re adopting a document model (JSON docs, no joins). Complex relational querying takes more planning (Mango queries / views).
- You need to decide a conflict strategy (e.g., last-write-wins per field, manual merge UI, or domain-specific resolution).
Best if you want to keep “any backend DB”: RxDB (client) + your server endpoints
- Client DB: RxDB (runs in the browser; can use IndexedDB/OPFS depending on setup).
- Server DB: Whatever you want (Postgres/Mongo/etc.).
- Sync: RxDB provides an offline-first “git-like” replication protocol; you implement a small set of server endpoints, with conflict handling primarily on the client side. (RxDB)
Why pick this
- You get an offline-first local DB and don’t have to adopt CouchDB.
- Works well when you already have an established backend stack and want to add local-first sync without replacing your server database. (RxDB)
Tradeoffs
- More engineering on the server side than CouchDB/PouchDB, because you must build/operate the replication-compatible endpoints correctly.
If you want Postgres-first “sync down” (and write through your API): ElectricSQL
- Server: Electric is an open-source Postgres sync engine that focuses on syncing data out of Postgres (partial replication, delivery). (GitHub)
- Client: You still use a local store, but writes typically go to your API/Postgres (it’s described as a “read-path” sync engine in its README). (GitHub)
Why pick this
- Great when Postgres remains the source of truth, but you want fast local reads + reactive UX with controlled subsets of data replicated to clients.
Tradeoffs
- Not the simplest “true bidirectional replication layer” in the CouchDB sense; you’ll still design the write path.
Close-but-not-fully-open-source: PowerSync
-
Client SDKs are open-source, but the server-side sync service is source-available under the Functional Source License (FSL) (not OSI open-source). (powersync.com)
-
Mentioning only because it’s explicitly positioned for offline/local-first sync and self-hosting exists, but it may not meet your “open-sourced” requirement.
Rule of thumb
- Want the least moving parts with fully open-source DB + sync: CouchDB + PouchDB.
- Want local-first but must keep Postgres/Mongo/etc. on the backend: RxDB.
- Want Postgres-centric partial replication and you’re fine with write-through APIs: ElectricSQL.
If you tell me whether your server-side data model is fundamentally document-shaped or relational, and whether you need fine-grained conflict merges (field-level / CRDT-like) vs simpler resolution, I can narrow it to a single recommended architecture.