FATAL: could not write to file "pg_wal/xlogtemp.12345": No space left on device

PostgreSQL FATAL: could not write to pg_wal/xlogtemp — Disk Full Fix

Your Postgres disk is full and it can't write WAL. Free space fast or move pg_wal to another volume, then figure out what ate the disk.

Quick answer: The filesystem holding your Postgres data directory (specifically pg_wal) is out of space. Free space immediately or move pg_wal to a bigger volume, restart Postgres, then track down what filled the disk — usually a stuck replication slot, a broken archive_command, or an orphaned WAL buildup.

This error shows up the moment Postgres tries to write a WAL segment and the kernel says no. WAL is the write-ahead log, every committed transaction hits it before it touches the actual data files. If WAL can't be written, Postgres shuts down the whole cluster. It doesn't degrade gracefully. It just dies. The filename xlogtemp.12345 is a temporary segment Postgres creates while assembling a new 16MB (or 1GB on some builds) WAL file. That temp file gets renamed once it's complete. When you see this in the logs, the disk is already past 100% usage — often a long way past, because WAL files are big and a single allocation can blow through the last few hundred MB in one go.

I've seen this three ways: a runaway replication slot keeping WAL segments after the replica went away, an archive_command that's been silently failing for days, and plain old data growth on a partition nobody was watching. The slot and archive cases are the sneaky ones because the data directory itself isn't growing — just pg_wal.

Fix it now

  1. Confirm the disk is actually full. SSH in and run df -h. Find the mount point where your PGDATA lives (check with ps aux | grep postgres for the -D flag, or SHOW data_directory if you can still connect). Don't assume — sometimes it's a different partition holding pg_wal if you've symlinked it.

  2. Stop Postgres cleanly if it hasn't already. It usually crashes itself, but if it's limping along, don't let it keep spewing errors. systemctl stop postgresql or pg_ctl stop -D /var/lib/postgresql/16/main. Don't kill -9 it. You'll regret that.

  3. Free space on the volume. Quick wins, in order:

    • Delete old postgresql-*.log files in /var/log/postgresql/ — they can be hundreds of MB on chatty clusters.
    • Check /tmp and /var/tmp for junk Postgres or your app dumped.
    • Find large files anywhere on the partition: find / -xdev -type f -size +500M -exec ls -lh {} \;
    • If you have old base backups in pg_wal/archive_status siblings or a backup dir on the same volume, those are fair game once you've confirmed they're not needed.

    Don't touch anything inside PGDATA yet. You need the space to bring Postgres up so you can clean properly.

  4. Start Postgres. systemctl start postgresql. It should come up now. Watch tail -f /var/log/postgresql/postgresql-16-main.log for a minute to make sure it's not immediately re-filling.

  5. Diagnose what's holding WAL. Connect as the postgres user and run:

    SELECT slot_name, active, pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retained
    FROM pg_replication_slots;
    
    SELECT * FROM pg_stat_archiver;

    Any inactive slot holding gigabytes of WAL is your problem. Drop it if the replica is truly gone:

    SELECT pg_drop_replication_slot('slot_name_here');

    If pg_stat_archiver shows failed_count climbing and last_failed_time recent, your archive_command is broken. Fix the script (wrong path, permissions, full destination NAS — happens all the time) and it'll catch up.

  6. Force a WAL recycle. Once slots and archiving are sorted:

    SELECT pg_switch_wal();

    Then check pg_wal size with du -sh $PGDATA/pg_wal. It should start shrinking back toward min_wal_size (default 80MB).

If you can't free enough space on the same disk

Move pg_wal to a different volume. This is the right call when the data partition is genuinely near capacity and you're not about to resize it at 3am.

  1. Stop Postgres.
  2. Mount a new volume with plenty of space, e.g. /mnt/pgwal. Make sure it's owned by the postgres user and mode 0700.
  3. rsync -aH $PGDATA/pg_wal/ /mnt/pgwal/
  4. mv $PGDATA/pg_wal $PGDATA/pg_wal.old
  5. ln -s /mnt/pgwal $PGDATA/pg_wal
  6. Start Postgres. Verify with SELECT pg_current_wal_lsn();
  7. Once you've confirmed it's healthy for a day, delete pg_wal.old.

Don't bother with ALTER SYSTEM SET wal_keep_size = 0 as a fix. It only helps if you were explicitly keeping WAL for a replica, and modern Postgres uses replication slots anyway. Slots override wal_keep_size. The real fix is dropping dead slots or moving the volume.

Stop it happening again

Set up monitoring on the pg_wal directory size and the free space on every Postgres mount. Alert at 80%, page at 90%. Also alert on pg_replication_slots where active = false for more than an hour, and on pg_stat_archiver.failed_count increasing. Those three signals catch this exact failure before it takes the cluster down. And if you're using logical replication slots for CDC (Debezium, for example), know that an inactive slot can eat hundreds of GB in a weekend. I've watched it happen.

Related Errors in Database Errors
0XC019004D Fix STATUS_CANNOT_ABORT_TRANSACTIONS 0XC019004D in 5 Steps 9001 Fix SQL Server Error 9001: Database log is full 0X800F023A Fix SPAPI_E_PNP_REGISTRY_ERROR (0x800f023a) the Right Way 0X00000239 Fix Sync Required Error 0X00000239 Step by Step

Was this solution helpful?

EP
Erropedia Team
Tech Support Editors
The Erropedia editorial team researches and documents real-world tech errors from across Windows, Linux, macOS, networking, databases, cloud platforms, and more. Every solution is reviewed for accuracy and updated as software and systems evolve.