What PostgreSQL wal_level Changes, and What It Does Not
What PostgreSQL wal_level Changes, and What It Does Not
About Remac: Remac is a high-performance, database-agnostic transaction log processor. It turns native database transaction logs into governed data streams, replicas, audit records, recovery data, and replayable events.
PostgreSQL is Remac's first implemented source. Logical transaction log processing starts with a source setting that provides enough information for logical decoding: wal_level = logical.
An operator changes wal_level to logical, restarts PostgreSQL, and waits for a change stream. PostgreSQL starts, application writes continue, but no events appear. That result can be correct.
wal_level controls the information that PostgreSQL writes to its write-ahead log. A working logical stream still needs a consumer, access, capacity, a replication slot, an output format, and a defined data scope.
wal_level changes the contents of WAL
PostgreSQL uses write-ahead logging to recover data-file changes after a crash. The wal_level setting controls how much information PostgreSQL adds to that log.
Current PostgreSQL documentation defines three levels.
| Level | Information written to WAL | Main boundary |
|---|---|---|
minimal | Information required for crash recovery or recovery after an immediate shutdown | It does not contain enough information for point-in-time recovery or streaming binary replication. |
replica | Everything in minimal, plus enough information for WAL archiving and replication | This is the default level. It does not add the information required for logical decoding. |
logical | Everything in replica, plus information required to extract logical change sets | It makes logical decoding possible. It does not create or operate a change stream. |
Each higher level includes the information from the levels below it. This means logical still supports the uses enabled by replica. It adds logical-decoding information to the WAL that PostgreSQL already writes.
The extra information can increase WAL volume. PostgreSQL states that the increase can be larger when many tables use REPLICA IDENTITY FULL and the workload contains many UPDATE and DELETE operations. The actual effect depends on the schema and workload, so measure it on representative traffic.
The new value takes effect after a server restart
PostgreSQL applies wal_level only when the server starts. A configuration-file change and a configuration reload are not enough.
Check the active value with:
SHOW wal_level;
The pg_settings view can also show whether a changed configuration value still needs a restart:
SELECT
name,
setting,
source,
sourcefile,
pending_restart
FROM pg_settings
WHERE name = 'wal_level';
setting is the active value. A pending_restart value of true means a configuration-file change has not taken effect because the server still needs a restart. PostgreSQL can hide sourcefile from users who do not have permission to read all settings.
This check avoids an ambiguous state. The file can contain logical while the running server still uses replica.
logical enables decoding, but does not create a stream
Logical decoding converts WAL records into a form that an external consumer can interpret. PostgreSQL identifies each logical stream with a replication slot. An output plugin controls the format sent to the consumer.
Neither object appears because wal_level changed. A logical-decoding setup also needs enough max_replication_slots capacity for its slots. Streaming-protocol consumers need enough max_wal_senders capacity, while consumers that call the logical-decoding SQL functions do not use a WAL-sender connection.
The stream itself needs a logical replication slot, a permitted output plugin that the server can load, and a consumer that reads changes and handles restarts. Its connection role needs the permissions required by the selected interface and a matching pg_hba.conf rule.
PostgreSQL's built-in logical replication adds its own configuration. A publication defines the tables and operation types in its change set. A subscription connects a subscriber to that publication. These publication and subscription objects are part of built-in logical replication. Other logical-decoding consumers can use the replication protocol or SQL interface without a PostgreSQL subscription.
Logical replication connections name a database. In the database field of pg_hba.conf, the value replication matches physical replication connections and does not match logical replication connections. A logical connection needs a rule that matches its actual database, all, or another documented database pattern.
This distinction matters during diagnosis. If SHOW wal_level returns logical, PostgreSQL has the required WAL information. A missing stream then points to the remaining configuration or to the consumer path.
Use slot state to narrow the problem
After the active setting is confirmed, inspect the logical slots that already exist. This core query uses columns present in PostgreSQL 14 through 18, the supported releases when this article was reviewed:
SELECT
slot_name,
plugin,
active,
restart_lsn,
confirmed_flush_lsn,
wal_status
FROM pg_replication_slots
WHERE slot_type = 'logical'
ORDER BY slot_name;
PostgreSQL 17 added invalidation_reason. PostgreSQL 17 and 18 users can add that column to the query. PostgreSQL 16 provides conflicting, which reports whether a logical slot was invalidated by a recovery conflict. PostgreSQL 14 and 15 provide neither column. Check the documentation for the running server version before adding a version-specific field to an incident query.
An empty result means the cluster has no logical slot. PostgreSQL can write the required WAL information, but no persistent logical stream has been provisioned through a slot.
For an existing slot, active shows whether the slot is being used at the time of the query. An inactive slot can be expected during maintenance or an outage, so this value needs operational context.
restart_lsn is the oldest WAL that the slot's consumer might still need. It is the slot's retained-WAL boundary. confirmed_flush_lsn records the position through which a logical consumer has confirmed receiving data. Observe both positions while the workload changes because they answer different questions and do not have to match.
A confirmed position that does not advance can indicate a disconnected or stalled consumer, but it can also mean that no relevant transactions entered that slot's stream. A wal_status value of lost means the slot is no longer usable. On PostgreSQL 17 and later, invalidation_reason can identify why PostgreSQL invalidated a slot. A wal_removed reason means required WAL was removed. A wal_level_insufficient reason can invalidate a logical slot on a standby when the primary reduces wal_level below logical. On a primary with an existing logical slot, lowering wal_level can prevent PostgreSQL from starting instead of producing a running cluster with that invalidated slot. Restoring a setting does not recreate missing history for a slot that was invalidated. The recovery plan must establish a valid new starting point.
If the slot is active and advancing but the destination still receives no expected events, continue past the source setting. Check the selected data scope, output format, consumer processing, destination response, and any filters that are part of the declared stream.
wal_level does not select rows or define replica identity
The setting applies to the PostgreSQL cluster. It does not select the tables, columns, operations, or rows that a consumer should receive.
For PostgreSQL publications, the publication defines the included tables and can limit operation types. On PostgreSQL 15 and later, row filters and column lists can narrow that scope further. Other logical-decoding consumers define their scope outside wal_level.
Replica identity is a separate table-level decision. PostgreSQL output plugins can access each new row from an INSERT and each new row version from an UPDATE. The available old row for an UPDATE or DELETE depends on the table's replica identity.
A primary key is the default replica identity when a table has one. A suitable unique index can serve the same purpose. REPLICA IDENTITY FULL records the whole old row as the identity and can increase WAL volume. It can also make subscriber row lookup inefficient.
Built-in logical replication adds a stricter write-time rule. A published table needs a valid replica identity for published UPDATE and DELETE operations. If the table has no usable identity, PostgreSQL rejects the operation on the publisher. INSERT operations can still proceed. Raw logical-decoding output has a different boundary: the old row information available to an output plugin depends on the configured replica identity.
Setting wal_level to logical does not repair a missing replica identity. Check the required operations and replica identity table by table before the stream starts.
It also does not define WAL retention
A replication slot tells PostgreSQL which WAL a consumer can still need. That slot is created and managed separately from wal_level.
An inactive or slow slot can retain WAL even when no consumer is connected. PostgreSQL warns that retained WAL can fill the storage allocated to pg_wal. max_slot_wal_keep_size can limit slot retention, but reaching that limit can remove WAL that a lagging consumer still needs. The consumer might then require a new starting point or a new initial copy.
The safe setting depends on WAL generation rate, expected outage duration, available source storage, and the recovery procedure when required WAL is gone. wal_level = logical answers none of those questions.
WAL archiving is separate as well. A level of replica or logical writes enough information for continuous archiving. On PostgreSQL 14, archive_mode and a working archive_command enable the archive path. PostgreSQL 15 and later can use archive_library instead of archive_command. A higher wal_level does not send completed WAL segments to archive storage.
It does not provide end-to-end delivery guarantees
PostgreSQL records a logical slot's position and supplies changes to a consumer. The complete consumer path must still define important behavior beyond the source.
PostgreSQL documents a possible repeat after a crash. A logical slot can return to an earlier persisted log sequence number and send recent changes again. A client must prevent repeated data from causing an incorrect result.
The source setting also cannot prove that a destination accepted an event. It cannot prove arrival order or safe resumption after a destination failure. Those properties belong to the complete transaction log processing path and its declared policy.
This is where Remac's product boundary begins to matter. wal_level = logical establishes a PostgreSQL source prerequisite. Remac governs the processing work between selected database changes and the systems that must use them under the Remac Contract. The source setting makes the information available. It does not replace the contract for completeness, ordering, delivery, resumability, consistency, liveness, and recoverability.
Treat the setting as one source prerequisite
Before enabling a logical consumer, confirm the active wal_level and whether PostgreSQL needs a restart. Check slot capacity, and check WAL-sender capacity when the consumer uses the streaming replication protocol. Verify connection access, the output plugin, selected data scope, and replica identity.
Decide whether slot WAL retention should have a finite cap. Base that decision on WAL generation rate, available source storage, tolerated consumer outage, and the recovery procedure if PostgreSQL removes required WAL. The default value of -1 permits slots to retain an unlimited amount of WAL. A finite value protects source storage by accepting the risk that a lagging slot can lose required history.
A successful restart proves only that PostgreSQL started. Verify the active value with SHOW wal_level. After the consumer starts, watch slot activity, retained WAL, source storage, connection health, and consumer progress. The active source setting does not prove that the full data path is healthy.
The distinction is simple and operationally useful. wal_level = logical gives PostgreSQL enough WAL information for logical decoding. Every other part of a dependable stream must still be declared, configured, and verified.
References
- PostgreSQL 18
wal_level - PostgreSQL 18 logical decoding
- PostgreSQL 18 streaming replication protocol
- PostgreSQL 18
pg_hba.conf - PostgreSQL 18 publication and replica identity
- PostgreSQL 14
CREATE PUBLICATION - PostgreSQL 14 WAL configuration
- PostgreSQL 15 release notes
pg_replication_slotsin PostgreSQL 14, 15, 16, 17, and 18- PostgreSQL 18 replication settings
- PostgreSQL 18
pg_settings
External reference note: The external technical information and linked references in this article were current when we published it. External systems, documentation, and defaults can change after publication.
Join the Remac waitlist for product updates.