The one thing to understand first
Where physical replication ships raw WAL blocks, logical replication ships logical row changes — “insert this row”, “update that row” — reconstructed from the WAL by logical decoding. Because it operates at the row level via SQL, publisher and subscriber can differ in major version, architecture, and even schema, and you can replicate a subset of tables. This flexibility powers upgrades, selective data distribution, and integration pipelines.
Logical replication is not a copy of the database — it is a stream of row-level changes applied by a normal writable backend. That single fact explains every difference: cross-version, partial, conflict-prone, and DDL-blind.
Logical decoding: turning WAL into changes
The WAL was designed for physical recovery, not human-readable changes. Logical decoding (src/backend/replication/logical/) reads the WAL and, using catalog information, reconstructs each committed transaction as an ordered stream of row changes. An output plugin formats that stream; the built-in pgoutput plugin feeds native logical replication, while plugins like wal2json emit JSON for change-data-capture (CDC) consumers.