How to fix Skipping vacuum of "table" --- lock not available in PostgreSQL

PostgreSQLINTERMEDIATELOWUpdated Dec 28, 2025· 198 views

This message appears when PostgreSQL's autovacuum cannot acquire the required lock on a table. Usually harmless if occasional, but frequent occurrences indicate long-running transactions or conflicting operations blocking maintenance.

Quick fix

Fixes Skipping vacuum of "table" --- lock not available

SELECT schemaname, relname, last_vacuum, last_autovacuum 
FROM pg_stat_user_tables 
WHERE last_autovacuum IS NULL OR last_autovacuum < now() - interval '1 hour';
  1. Identify tables with frequent skipping
  2. Find blocking locks using pg_locks
  3. Close long-running transactions
  4. Disable TRUNCATE option for problematic tables (PostgreSQL 9.4+)
postgresql — error
SELECT schemaname, relname, last_vacuum, last_autovacuum
FROM pg_stat_user_tables
WHERE last_autovacuum IS NULL OR last_autovacuum < now() - interval '1 hour';
Skipping vacuum of "table" --- lock not available

What this error means

PostgreSQL autovacuum regularly runs VACUUM operations to reclaim space and maintain table health. The VACUUM command requires a ShareUpdateExclusiveLock on regular operations or AccessExclusiveLock on VACUUM FULL. When autovacuum attempts to lock a table but finds a conflicting lock already held, it skips that table with this message. This is a protective mechanism—autovacuum prefers to skip a table temporarily rather than wait indefinitely for locks. The log level is LOG (not WARNING or ERROR), making this a routine operational event.

Typical symptoms

  • VACUUM skipping message appears in PostgreSQL logs
  • Autovacuum tasks complete quickly but some tables are not processed
  • Happens frequently for specific tables during peak usage times
  • Tables grow larger than expected over time

Common causes

  1. Long-running transactions with open cursors holding locks on the table
  2. Concurrent DDL operations (ALTER TABLE, CREATE INDEX, TRUNCATE, CLUSTER) on the same table
  3. Another VACUUM FULL operation already running on that table
  4. VACUUM with TRUNCATE option enabled (default) cannot release space when locks conflict
  5. Hot standby replication with queries on standby servers holding locks while primary tries to vacuum

How to fix "Skipping vacuum of "table" --- lock not available"

1Identify tables with frequent skipping

Enable log_autovacuum_min_duration if not already set. Monitor PostgreSQL logs for which tables are frequently skipped. Use:

sql
SELECT schemaname, relname, last_vacuum, last_autovacuum 
FROM pg_stat_user_tables 
WHERE last_autovacuum IS NULL OR last_autovacuum < now() - interval '1 hour';
2Find blocking locks using pg_locks

While the message appears, query the system views to identify what holds locks:

sql
SELECT pid, usename, application_name, state, query 
FROM pg_stat_activity 
WHERE pid IN (
  SELECT pid FROM pg_locks 
  WHERE relation = 'your_table'::regclass AND NOT granted
);

This shows connections that are holding or waiting for locks.

3Close long-running transactions

Identify and close idle transactions holding locks. In your application:
- Use connection pooling (PgBouncer, pgpool) to prevent connection leaks
- Set idle_in_transaction_session_timeout (PostgreSQL 13+): ALTER SYSTEM SET idle_in_transaction_session_timeout = '5min';
- Close cursor-holding connections that are no longer needed

For already-stuck connections, carefully use:

sql
SELECT pg_terminate_backend(pid) 
FROM pg_stat_activity 
WHERE pid != pg_backend_pid() AND state = 'idle in transaction';
4Disable TRUNCATE option for problematic tables (PostgreSQL 9.4+)

If VACUUM TRUNCATE is causing lock conflicts, disable it:

sql
ALTER TABLE your_table SET (vacuum_truncate = off);

This prevents VACUUM from trying to return disk space to the OS, which requires an exclusive lock. Space is still reclaimed for reuse within PostgreSQL, just not returned to the OS.

5Use SKIP_LOCKED for manual vacuum (PostgreSQL 12+)

If you run manual VACUUM commands, use the SKIP_LOCKED option:

sql
VACUUM (SKIP_LOCKED) your_table;

This tells VACUUM to skip tables that have conflicting locks instead of waiting. Works for both VACUUM and ANALYZE.

6Review timeout and lock settings

Set appropriate timeouts to prevent vacuum from waiting forever:

sql
ALTER SYSTEM SET lock_timeout = '10s';
ALTER SYSTEM SET statement_timeout = '30s';
SELECT pg_reload_conf();

These limits prevent both user queries and maintenance tasks from blocking indefinitely.

Advanced notes

VACUUM FULL and TRUNCATE operations require exclusive locks (AccessExclusiveLock) and are more likely to conflict. Prefer regular VACUUM (ShareUpdateExclusiveLock) which allows concurrent reads and writes. On high-concurrency systems, increase autovacuum workers (autovacuum_max_workers) but reduce cost parameters (autovacuum_vacuum_cost_limit, autovacuum_vacuum_cost_delay) to make workers yield more frequently. For hot standby setups, queries on standby servers can block primary-side vacuum; use max_standby_streaming_delay to force standby queries to cancel if they hold locks too long. Consider partitioning large tables so autovacuum can process partitions independently.

Frequently asked questions

What does "Skipping vacuum of "table" --- lock not available" mean?
PostgreSQL autovacuum regularly runs VACUUM operations to reclaim space and maintain table health. The VACUUM command requires a ShareUpdateExclusiveLock on regular operations or AccessExclusiveLock on VACUUM FULL. When autovacuum attempts to lock a table but finds a conflicting lock already held, it skips that table with this message.
How do I fix "Skipping vacuum of "table" --- lock not available"?
This message appears when PostgreSQL's autovacuum cannot acquire the required lock on a table. Usually harmless if occasional, but frequent occurrences indicate long-running transactions or conflicting operations blocking maintenance.
What causes "Skipping vacuum of "table" --- lock not available"?
Long-running transactions with open cursors holding locks on the table Concurrent DDL operations (ALTER TABLE, CREATE INDEX, TRUNCATE, CLUSTER) on the same table Another VACUUM FULL operation already running on that table
Is the "Skipping vacuum of "table" --- lock not available" error serious?
It is a low-severity error — more of a blocker than a risk. The steps above resolve it.

Official resources & further reading