Skip to content

Why Amazon RDS and Aurora PostgreSQL report "No space left on device" when storage looks free

11 minute read
Content level: Expert
2

This article explains why Amazon RDS for PostgreSQL and Amazon Aurora PostgreSQL can return "No space left on device" while CloudWatch still shows free storage

Overview

You may encounter a situation where your Amazon RDS for PostgreSQL or Amazon Aurora PostgreSQL instance reports an error similar to:

ERROR: could not open file "current_logfiles.tmp": No space left on device

or

ERROR: could not create file "base/16aa6/11261aaa71_vm": No space left on device

However, when you check CloudWatch metrics, available storage space still shows free capacity.

This article explains why this discrepancy occurs, how it differs between RDS PostgreSQL and Aurora PostgreSQL, and how to diagnose and resolve the issue.


How Storage Differs: RDS PostgreSQL vs Aurora PostgreSQL

AspectRDS PostgreSQLAurora PostgreSQL
Data storageEBS volume (gp2/gp3/io1/io2) with ext4 filesystemAurora distributed storage (no traditional filesystem)
Inode limit applicable?Yes — ext4 has a fixed inode countNo — Aurora storage does not use inodes for data files
"No space left" with free spaceInode exhaustion or ext4 directory index fullLocal storage full (temp files/logs)
Key CloudWatch metricFreeStorageSpace (EBS bytes only — does NOT track inodes)FreeLocalStorage

Part 1: RDS PostgreSQL — Inode Exhaustion

What Are Inodes?

Every file on a Linux ext4 filesystem requires an inode (index node). It is a fixed-size metadata structure that stores file attributes and pointers to data blocks. The inode count is set at filesystem creation and is proportional to the volume size. When all inodes are consumed, no new files can be created even if disk space remains available.

Key points:

  • Each file consumes exactly one inode, regardless of file size
  • The inode count is fixed and cannot be increased without scaling storage (which grows the filesystem and adds inodes proportionally)
  • CloudWatch FreeStorageSpace reports disk bytes only — it does NOT monitor inode availability

How PostgreSQL Uses Files

PostgreSQL uses a file-per-relation storage model. Each database object creates multiple files on disk:

ComponentFile SuffixPurpose
Main data fork(none)Actual row data or index entries
Free Space Map_fsmTracks free space within pages
Visibility Map_vmTracks all-visible pages for index-only scans
TOAST tableseparate relfilenodeStores oversized column values
TOAST indexseparate relfilenodeB-tree index on TOAST table
Segments (>1 GB).1, .2, .3...Each 1 GB data chunk is a separate file

Temporary files and log files count too. On RDS PostgreSQL, temp files (from sorts/hashes that spill past work_mem, in 'base/pgsql_tmp') and server log files ('postgresql.log', retained per rds.log_retention_period) live on the same EBS ext4 volume as your data — so they consume both FreeStorageSpace bytes and inodes. Temp-file inode usage is transient (files are removed when the query finishes), so persistent inode exhaustion is still driven mainly by permanent relations (tables, indexes, TOAST, partitions); retained log files are longer-lived.

Diagnosing Inode Exhaustion (RDS PostgreSQL)

Check via Database Insights (Advanced) or Enhanced Monitoring

Database Insights Advanced and Enhanced Monitoring expose OS-level filesystem counters including inode usage:

MetricConsole LabelWhat It Shows
usedFilePercentUsed InodesThe percentage of available files in use.
maxFilesMax InodesThe maximum number of files that can be created for the file system.
usedFilesUsed%The number of files in the file system.
usedPercentUsed FilesystemThe percentage of the file-system disk space in use.

How to check in the RDS Console:

Option A — Enhanced Monitoring (Amazon RDS console)

  1. Open the Amazon RDS console → Databases.
  2. Choose your DB instance (for a cluster, select the specific writer/reader instance — OS metrics are per instance, not per cluster).
  3. Open the Monitoring tab.
  4. Use the Monitoring dropdown (top-right of the tab) and select Enhanced monitoring.
  5. Select Manage graphs.
  6. Scroll to the File system metric group and select:
  • usedFilePercent — the percentage of inodes in use. This is your primary signal — 99–100% = inode exhaustion.
  • usedFiles — the raw count of inodes in use.
  • usedPercent — the percentage of disk bytes used. In an inode-exhaustion case this stays low (for example 40–50%) — that's the paradox.

Option B — Database Insights (Amazon CloudWatch console)

  1. Open the CloudWatch Database Insights console → in the left nav.
  2. Select your database (filter by cluster/instance), then open the specific DB instance.
  3. Go to Database Telemetry and then click on Create widget.
  4. Under 'OS metrics', expand the File system group and select the same fields "Used File Percent", "Used Files" and "Used Percent" (Parameters Without rootfilesys would be for /rdsdbdata).
  5. Then click on Create Widget and monitor the metrics.

Confirmation pattern:

Used Inodes (usedFilePercent) ≈ 99-100%   ← INODES EXHAUSTED
Used Filesystem (usedPercent) ≈ 40-50%    ← Plenty of disk bytes free

Note: Database Insights Standard does not include these OS-level metrics. You need either Database Insights Advanced or Enhanced Monitoring enabled.

Check RDS Events

RDS emits low inodes events when usage exceeds thresholds either through AWS console or using below AWS CLI command:

aws rds describe-events \
  --source-identifier <your-instance-id> \
  --source-type db-instance \
  --duration 10080

Look for messages like:

The free inodes count for DB Instance: <instance-name> is low at 5% of total

Identify What Is Consuming Inodes

Connect to each database and count relations:

-- Total relation count in the current database
SELECT count(*) AS total_relations FROM pg_class;

-- Breakdown by object type
SELECT
  CASE relkind
    WHEN 'r' THEN 'table'
    WHEN 'i' THEN 'index'
    WHEN 'S' THEN 'sequence'
    WHEN 't' THEN 'TOAST table'
    WHEN 'v' THEN 'view'
    WHEN 'm' THEN 'materialized view'
    WHEN 'p' THEN 'partitioned table'
    WHEN 'I' THEN 'partitioned index'
    ELSE relkind::text
  END AS object_type, count(*) AS count
FROM pg_class GROUP BY relkind ORDER BY count DESC;

-- List all databases (repeat the above for each)
SELECT datname FROM pg_database WHERE datistemplate = false;

Check for ext4 Directory Index Full Scenario

If errors are intermittent (some file creates succeed, others fail), you may be hitting the ext4 hash tree (htree) directory limit — a per-directory cap that is separate from total inode count.

Characteristics:

  • Occurs when a single database directory has millions of files
  • usedFilePercent may still be below 100%
  • Errors are intermittent, not consistent
  • Reach out to AWS Support if needed

Resolution (RDS PostgreSQL)

Immediate: Scale Storage

Increasing allocated storage grows the filesystem, adding inodes proportionally:

aws rds modify-db-instance \
  --db-instance-identifier <your-instance> \
  --allocated-storage <new-size-in-GB> \
  --apply-immediately

Important: Storage scaling is a one-way operation (cannot decrease). After scaling, the storage-full state clears and the instance becomes operational again.

Reduce File Count

-- Drop unused databases
DROP DATABASE <unused_database>;

-- Find unused indexes (candidates for removal)
SELECT schemaname, indexrelname, idx_scan
FROM pg_stat_user_indexes WHERE idx_scan = 0;

-- Check for inactive replication slots
SELECT slot_name, active FROM pg_replication_slots WHERE NOT active;
SELECT pg_drop_replication_slot('<slot_name>');

Long-Term Recommendations

  • Implement cleanup policies for ephemeral databases (CI/CD workloads)
  • Reduce partition count where possible (each partition = separate set of files)
  • Avoid heavy CREATE/DROP temporary table patterns (use TRUNCATE instead)
  • Tune work_mem to reduce temp file generation

Prevention & Monitoring

ActionPurpose
Enable Database Insights Advanced or Enhanced MonitoringGet visibility into inode usage (usedFilePercent)
Alarm on usedFilePercent > 80%Early warning before exhaustion
Provision storage for file count, not just data sizeMore storage = more inodes
Implement database cleanup policiesPrevent unbounded relation growth
Reduce log retention (rds.log_retention_period)Reduces the number of log files
Set temp_file_limit parameterCap temp file generation per session
log_temp_files + pg_ls_tmpdir()For diagnosing which sessions spill temp files
Increase work_memReduce temp file spills

Part 2: Aurora PostgreSQL — Local Storage Exhaustion

Inode exhaustion does NOT apply to Aurora PostgreSQL data files. Aurora uses a distributed storage layer without a traditional ext4 filesystem.

However, Aurora PostgreSQL CAN report "No space left on device" when local storage fills up with temporary files or logs.

What Causes "No Space Left" on Aurora PostgreSQL?

CauseKey Metric
Temp files from large sort/hash operations filling local storageFreeLocalStorage → 0
Excessive PostgreSQL logging filling local storageFreeLocalStorage → 0

Diagnosing (Aurora PostgreSQL)

  1. Check FreeLocalStorage in CloudWatch or Database Insights — if it dropped to zero at the time of error, local storage exhaustion is confirmed.
  2. Check temp file usage:
SELECT datname, temp_files, temp_bytes FROM pg_stat_database WHERE temp_files > 0;
  1. Check Database Insights / Enhanced Monitoring — OS metrics show local filesystem usage breakdown.

Resolution (Aurora PostgreSQL)

ActionEffect
Kill long-running queries generating temp filesImmediate relief
Scale to larger instance classMore local storage
Increase work_memReduces temp file spills
Set temp_file_limit parameterPrevents runaway queries from filling local storage

Prevention & Monitoring

ActionPurpose
Alarm on FreeLocalStorage < 5 GBEarly warning for local storage
Enable Database Insights AdvancedOS-level local filesystem visibility
Set temp_file_limit parameterCap temp file generation per session
Increase work_memReduce temp file spills
Monitor Performance Insights wait events IO:BufFileWriteIdentify queries spilling to disk
Reduce log retention (rds.log_retention_period)Reduces the number of files
log_temp_files + pg_ls_tmpdir()For diagnosing which sessions spill temp files
Use Aurora Optimized ReadsMoves temp files to local NVMe

Frequently Asked Questions

Q: Does inode exhaustion affect Aurora PostgreSQL?

A: No. Aurora uses a distributed storage layer without a traditional filesystem or inode limit for data files. "No space left" on Aurora is caused by local storage (temp files/logs) filling up.

Q: Does VACUUM free inodes?

A: Generally no — and not in the way you'd need to recover from inode exhaustion. Standard (non-FULL) VACUUM reclaims dead-tuple space inside existing files so pages can be reused; it doesn't reduce the file (relation) count. It can only shrink a relation's file footprint in the narrow case where it truncates all-empty trailing pages, which occasionally removes a trailing 1 GB segment file — but this rarely moves the inode count meaningfully.

VACUUM FULL is different: it rewrites the table (and its indexes) into a new relfilenode and drops the old files. That doesn't reduce the number of relations either, and while it runs it temporarily needs space for both the old and new copies — so it's not a remedy for inode exhaustion (and it takes an ACCESS EXCLUSIVE lock).

To actually reduce the inode count, you must reduce the number of relations: DROP TABLE, DROP INDEX, DROP DATABASE, or reduce partition count. If you're already out of inodes/space, scale storage first to get the instance operational, then drop unneeded objects.

Q: Will a reboot fix inode exhaustion?

A: No. A reboot does not free inodes. You must either scale storage or drop database objects.

Q: Can I check inode usage in Database Insights?

A: Yes, but only with Database Insights Advanced (which includes Enhanced Monitoring OS metrics). Navigate to Monitoring → OS Metrics → Filesystem to view usedFilePercent for the /rdsdbdata mount point.


References

AWS
SUPPORT ENGINEER

published 2 months ago313 views