<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Dwaragesh D</title>
    <description>The latest articles on DEV Community by Dwaragesh D (@dwaradwara).</description>
    <link>https://dev.to/dwaradwara</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4111644%2F9985de93-b5f0-454a-a872-f383ef2b2637.png</url>
      <title>DEV Community: Dwaragesh D</title>
      <link>https://dev.to/dwaradwara</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dwaradwara"/>
    <language>en</language>
    <item>
      <title>When “Active” Doesn’t Mean Healthy: Debugging PostgreSQL ENOSPC Across a Multi-VM Linux Stack</title>
      <dc:creator>Dwaragesh D</dc:creator>
      <pubDate>Sat, 05 Sep 2026 22:08:10 +0000</pubDate>
      <link>https://dev.to/dwaradwara/when-active-doesnt-mean-healthy-debugging-postgresql-enospc-across-a-multi-vm-linux-stack-35k6</link>
      <guid>https://dev.to/dwaradwara/when-active-doesnt-mean-healthy-debugging-postgresql-enospc-across-a-multi-vm-linux-stack-35k6</guid>
      <description>&lt;p&gt;PostgreSQL was active.&lt;/p&gt;

&lt;p&gt;Nginx was active.&lt;/p&gt;

&lt;p&gt;PHP-FPM was active.&lt;/p&gt;

&lt;p&gt;The application still returned:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HTTP 503
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The actual root cause was a completely full filesystem backing a PostgreSQL tablespace.&lt;/p&gt;

&lt;p&gt;I built this incident deliberately in my Ubuntu/KVM support lab to test a common troubleshooting mistake:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Treating process state as proof of service health.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The incident became a useful example of why application health, dependency health, and process health need to be investigated separately.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lab architecture
&lt;/h2&gt;

&lt;p&gt;I used three Ubuntu VMs connected through a private libvirt network:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client
   |
   v
vm-web-01
192.168.100.10
Nginx + PHP-FPM
   |
   | TCP/5432
   v
vm-db-01
192.168.100.20
PostgreSQL 14
   |
   | node_exporter
   v
vm-monitor-01
192.168.100.30
Prometheus + Alertmanager
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The database VM also had a dedicated disk mounted at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/mnt/inc012-db
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That filesystem was used for the PostgreSQL tablespace, which meant I could create storage pressure without filling the VM's root filesystem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Establishing the baseline
&lt;/h2&gt;

&lt;p&gt;Before introducing the failure, I checked the application, database, filesystem, and monitoring state.&lt;/p&gt;

&lt;p&gt;The PostgreSQL tablespace filesystem was:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;8.03% used
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The application returned:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HTTP 200
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Database writes succeeded.&lt;/p&gt;

&lt;p&gt;Prometheus showed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DBDiskNearlyFull state=inactive health=ok
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gave me a known-good state to compare against during the incident.&lt;/p&gt;

&lt;h2&gt;
  
  
  Monitoring detected the problem first
&lt;/h2&gt;

&lt;p&gt;I then consumed storage on the dedicated database filesystem in a controlled way.&lt;/p&gt;

&lt;p&gt;When usage reached:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;88.49%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Prometheus changed the storage alert to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DBDiskNearlyFull firing
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But the application was still returning:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HTTP 200
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This was one of the most important observations in the test.&lt;/p&gt;

&lt;p&gt;The monitoring system had identified a capacity problem before the customer-facing service failed.&lt;/p&gt;

&lt;p&gt;That is exactly what useful monitoring should do: create an intervention window before an operational condition becomes an outage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Then the filesystem reached 100%
&lt;/h2&gt;

&lt;p&gt;I continued the controlled storage consumption until the dedicated filesystem reached:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;100%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The database VM's root filesystem remained healthy.&lt;/p&gt;

&lt;p&gt;PostgreSQL also continued reporting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;active
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So at a quick glance, the database server could appear healthy.&lt;/p&gt;

&lt;p&gt;It wasn't.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing the operation that actually mattered
&lt;/h2&gt;

&lt;p&gt;A connectivity test alone wasn't enough.&lt;/p&gt;

&lt;p&gt;PostgreSQL could still accept connections, but when I performed a write that required additional filesystem blocks, it failed with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ERROR: could not extend file
No space left on device
HINT: Check free disk space.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The PostgreSQL logs showed the same ENOSPC condition.&lt;/p&gt;

&lt;p&gt;That exposed the distinction I was looking for:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PostgreSQL process health
        !=
PostgreSQL write health
        !=
Application health
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A running database process does not necessarily mean the database can complete the operations the application depends on.&lt;/p&gt;

&lt;h2&gt;
  
  
  The user-facing failure appeared at the web tier
&lt;/h2&gt;

&lt;p&gt;The actual failure originated in database storage.&lt;/p&gt;

&lt;p&gt;But the visible symptom appeared somewhere else.&lt;/p&gt;

&lt;p&gt;The web endpoint changed from:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HTTP 200
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HTTP 503
{"status":"degraded","database":"write_failed"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At the same time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Nginx       active
PHP-FPM     active
PostgreSQL  active
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All three processes were alive while the application was degraded.&lt;/p&gt;

&lt;p&gt;If my investigation had stopped at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl is-active nginx
systemctl is-active postgresql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I could easily have concluded that the web and database layers were fine.&lt;/p&gt;

&lt;p&gt;They weren't.&lt;/p&gt;

&lt;h2&gt;
  
  
  My investigation path
&lt;/h2&gt;

&lt;p&gt;I worked down the dependency chain instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HTTP 503
   ↓
Check Nginx and PHP-FPM
   ↓
Processes active
   ↓
Check PostgreSQL reachability
   ↓
Database reachable
   ↓
Test an actual database write
   ↓
Write fails with ENOSPC
   ↓
Inspect PostgreSQL error
   ↓
Check tablespace filesystem
   ↓
Filesystem 100% full
   ↓
Correlate with Prometheus
   ↓
Storage alert already firing
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key shift was moving from:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Is the process running?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Can this dependency perform the operation the application requires?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That narrowed the failure to storage rather than networking, web-server availability, process crashes, or general VM capacity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recovery
&lt;/h2&gt;

&lt;p&gt;Because the disk pressure had been generated intentionally for the lab, I removed the temporary filler data from the isolated database filesystem.&lt;/p&gt;

&lt;p&gt;I did not restart PostgreSQL.&lt;/p&gt;

&lt;p&gt;After the storage was released, filesystem usage returned to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;8.27%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;PostgreSQL writes succeeded again.&lt;/p&gt;

&lt;p&gt;The application returned:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HTTP 200
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Prometheus returned the storage alert to inactive, and Alertmanager had no remaining active &lt;code&gt;DBDiskNearlyFull&lt;/code&gt; alert.&lt;/p&gt;

&lt;h2&gt;
  
  
  Before, failure and recovery
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Check&lt;/th&gt;
&lt;th&gt;Baseline&lt;/th&gt;
&lt;th&gt;Failure&lt;/th&gt;
&lt;th&gt;Recovery&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;DB filesystem&lt;/td&gt;
&lt;td&gt;8.03%&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;8.27%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PostgreSQL process&lt;/td&gt;
&lt;td&gt;Active&lt;/td&gt;
&lt;td&gt;Active&lt;/td&gt;
&lt;td&gt;Active&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PostgreSQL writes&lt;/td&gt;
&lt;td&gt;Successful&lt;/td&gt;
&lt;td&gt;ENOSPC&lt;/td&gt;
&lt;td&gt;Successful&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Application&lt;/td&gt;
&lt;td&gt;HTTP 200&lt;/td&gt;
&lt;td&gt;HTTP 503&lt;/td&gt;
&lt;td&gt;HTTP 200&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Prometheus alert&lt;/td&gt;
&lt;td&gt;Inactive&lt;/td&gt;
&lt;td&gt;Firing&lt;/td&gt;
&lt;td&gt;Inactive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Alertmanager&lt;/td&gt;
&lt;td&gt;Clear&lt;/td&gt;
&lt;td&gt;Active&lt;/td&gt;
&lt;td&gt;Clear&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The most interesting line is still:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PostgreSQL process
Active → Active → Active
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The process never stopped.&lt;/p&gt;

&lt;p&gt;The service still failed from the application's perspective.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I took away from the incident
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Process status is only one health signal
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;systemctl is-active&lt;/code&gt; tells me whether systemd considers a process active.&lt;/p&gt;

&lt;p&gt;It does not prove that the service can perform useful work.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Check the dependency the application actually uses
&lt;/h3&gt;

&lt;p&gt;For a PostgreSQL-backed application, TCP connectivity or &lt;code&gt;pg_isready&lt;/code&gt; is useful, but it may not be enough.&lt;/p&gt;

&lt;p&gt;The application needed database writes.&lt;/p&gt;

&lt;p&gt;So write capability was the meaningful test during this incident.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Monitoring should create time to respond
&lt;/h3&gt;

&lt;p&gt;The alert fired at 88.49% while the application was still healthy.&lt;/p&gt;

&lt;p&gt;That gap between warning and outage is operationally valuable.&lt;/p&gt;

&lt;p&gt;Capacity alerts should give engineers enough time to investigate and remediate before the filesystem reaches exhaustion.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Validate recovery through the entire stack
&lt;/h3&gt;

&lt;p&gt;Freeing disk space was not enough for me to call the incident resolved.&lt;/p&gt;

&lt;p&gt;I checked:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Filesystem capacity
→ PostgreSQL write
→ application HTTP response
→ Prometheus state
→ Alertmanager state
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only after all of those recovered did I consider the incident validated.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Restarting isn't automatically the fix
&lt;/h3&gt;

&lt;p&gt;PostgreSQL did not need a restart.&lt;/p&gt;

&lt;p&gt;The process wasn't broken.&lt;/p&gt;

&lt;p&gt;Its storage dependency was.&lt;/p&gt;

&lt;p&gt;Restarting it would have treated the symptom instead of the root cause.&lt;/p&gt;

&lt;h2&gt;
  
  
  Commands I would use during a similar investigation
&lt;/h2&gt;

&lt;p&gt;Check filesystem block capacity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;df&lt;/span&gt; &lt;span class="nt"&gt;-hT&lt;/span&gt; /mnt/inc012-db
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check inode capacity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;df&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; /mnt/inc012-db
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Find large consumers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo du&lt;/span&gt; &lt;span class="nt"&gt;-xhd1&lt;/span&gt; /mnt/inc012-db | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check PostgreSQL process state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl is-active postgresql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check PostgreSQL connectivity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pg_isready
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But after those checks, I would still test the actual database operation required by the application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final takeaway
&lt;/h2&gt;

&lt;p&gt;The entire incident can be summarized as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Prometheus warned first.
PostgreSQL stayed active.
Database writes failed.
The application returned HTTP 503.
The real root cause was exhausted database storage.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The biggest lesson for me was simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“The service is running” and “the service is healthy” are not the same statement.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This experiment was performed entirely in an isolated Ubuntu/KVM lab using controlled fault injection and manual troubleshooting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Full lab and incident report
&lt;/h2&gt;

&lt;p&gt;The full INC012 incident report and the rest of my 16-incident Ubuntu/KVM support lab are available here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/dwaradwara/ubuntu-kvm-support-lab" rel="noopener noreferrer"&gt;View the Ubuntu/KVM support lab on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/dwaradwara/ubuntu-kvm-support-lab/tree/main/incidents/INC012-full-stack-degradation" rel="noopener noreferrer"&gt;Read the full INC012 incident report&lt;/a&gt;&lt;/p&gt;

</description>
      <category>linux</category>
      <category>postgres</category>
      <category>devops</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
