{"id":108,"date":"2026-07-12T00:00:00","date_gmt":"2026-07-12T00:00:00","guid":{"rendered":"http:\/\/\/var\/www\/html\/?p=108"},"modified":"2026-07-21T01:02:35","modified_gmt":"2026-07-21T01:02:35","slug":"postgresql-pglog-disk-space-exhausted-database-lock-lockfile-on-debian-12-bookworm","status":"publish","type":"post","link":"https:\/\/butitworkedlocal.com\/postgresql-pglog-disk-space-exhausted-database-lock-lockfile-on-debian-12-bookworm\/","title":{"rendered":"Resolving PostgreSQL pg_log Disk Space Exhaustion &amp; Database Lock on Debian 12 Bookworm"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\"><strong>Fix PostgreSQL database locks and service failures on Debian 12 caused by full pg_log disk space. Learn to clear logs, free space, and prevent recurrence.<\/strong><\/p>\n\n\n<p>When your PostgreSQL database suddenly becomes unresponsive, applications fail to connect, or the entire web application ecosystem grinds to a halt, one of the most insidious culprits can be a full disk. Specifically, an overgrowth of PostgreSQL&#039;s transaction logs or <code>pg_log<\/code> files can consume all available space, leading to critical database failures, service unresponsiveness, and &quot;lockfile&quot; errors as PostgreSQL struggles to write essential operational data. This guide provides a highly technical, step-by-step resolution for this common issue on Debian 12 (Bookworm) systems.<\/p>\n<h3>Symptom &amp; Error Signature<\/h3>\n<p>Users typically experience one or more of the following symptoms:<\/p>\n<ul>\n<li>Web applications reporting database connection errors (e.g., &quot;could not connect to server: Connection refused&quot;, &quot;FATAL: remaining connection slots are reserved for non-replication superuser connections&quot;).<\/li>\n<li>The PostgreSQL service (often <code>postgresql@15-main.service<\/code> or similar) is reported as <code>failed<\/code> or <code>inactive<\/code> by <code>systemctl<\/code>.<\/li>\n<li>Attempts to start or restart PostgreSQL fail immediately.<\/li>\n<li>System logs (<code>journalctl<\/code>) show errors related to disk I\/O or inability to write.<\/li>\n<\/ul>\n<p>Here are typical error messages you might encounter:<\/p>\n<p><strong>From <code>journalctl -u postgresql<\/code> (or <code>journalctl -u postgresql@15-main.service<\/code> for specific versions):<\/strong><\/p>\n<pre><code>systemd[1]: postgresql@15-main.service: Start request repeated too quickly.\nsystemd[1]: postgresql@15-main.service: Failed with result &#039;exit-code&#039;.\nsystemd[1]: Failed to start PostgreSQL Cluster 15-main.\n...\npostgresql[12345]: 2024-06-25 10:30:00.123 UTC [12345] LOG: could not write lock file &quot;\/var\/run\/postgresql\/.s.PGSQL.5432.lock&quot;: No space left on device\npostgresql[12345]: 2024-06-25 10:30:00.123 UTC [12345] FATAL: could not create lock file &quot;\/var\/run\/postgresql\/.s.PGSQL.5432.lock&quot;: No space left on device\npostgresql[12345]: 2024-06-25 10:30:00.123 UTC [12345] LOG: database system is shut down\n<\/code><\/pre>\n<p><strong>From <code>dmesg<\/code> (kernel messages, indicating disk I\/O issues):<\/strong><\/p>\n<pre><code>[123456.789012] EXT4-fs (sda1): write access unavailable, fs is full\n[123456.789013] EXT4-fs (sda1): Remounting filesystem read-only\n<\/code><\/pre>\n<p><strong>From <code>df -h<\/code> (showing disk usage):<\/strong><\/p>\n<pre><code class=\"language-bash\">Filesystem      Size  Used Avail Use% Mounted on\n\/dev\/sda1        50G   50G     0 100% \/\nudev             10M    0   10M   0% \/dev\ntmpfs           7.8G  1.2M  7.8G   1% \/run\n<\/code><\/pre>\n<h3>Root Cause Analysis<\/h3>\n<p>The root cause of this issue is typically a filesystem reaching 100% capacity on the partition where PostgreSQL stores its data and\/or log files. This commonly happens on the root partition (<code>\/<\/code>) if <code>\/var<\/code> is not a separate mount, or on a dedicated <code>\/var<\/code> partition.<\/p>\n<p>Here&#039;s a breakdown of why this leads to the observed symptoms:<\/p>\n<ol>\n<li><strong>Excessive Log Growth:<\/strong> PostgreSQL, by default, writes detailed logs to files within a directory like <code>\/var\/log\/postgresql\/<\/code> or within the data directory itself (<code>\/var\/lib\/postgresql\/&lt;version&gt;\/main\/log\/<\/code>). Without proper log rotation (e.g., via <code>logrotate<\/code>), these log files can grow indefinitely, consuming gigabytes or even terabytes of disk space over time.<\/li>\n<li><strong>Lack of Free Space:<\/strong> Once the filesystem reaches 100% utilization, the operating system can no longer write any new data to that disk.<\/li>\n<li><strong>PostgreSQL&#039;s Inability to Operate:<\/strong> PostgreSQL requires free disk space for several critical operations:<ul>\n<li><strong>Writing Log Files:<\/strong> It cannot log new events, leading to internal errors.<\/li>\n<li><strong>Writing Transaction Logs (WAL):<\/strong> Crucial for data integrity and recovery. While WAL segments are usually pre-allocated, operational temporary files and checkpoints might require space.<\/li>\n<li><strong>Creating Lock Files:<\/strong> When PostgreSQL starts, it creates a lock file (e.g., <code>\/var\/run\/postgresql\/.s.PGSQL.5432.lock<\/code>) to prevent multiple instances from running on the same port. If it cannot create this file, it fails to start.<\/li>\n<li><strong>Temporary Files and Caching:<\/strong> Queries that require large sorts or joins might create temporary files on disk. If these cannot be written, queries will fail.<\/li>\n<\/ul>\n<\/li>\n<li><strong>Service Failure:<\/strong> The inability to write lock files or perform other critical disk operations prevents the PostgreSQL server from starting or causes it to shut down gracefully (or crash) to prevent data corruption. <code>systemd<\/code> will then report the service as failed.<\/li>\n<\/ol>\n<h3>Step-by-Step Resolution<\/h3>\n<p>Follow these steps carefully to recover your PostgreSQL service and prevent future occurrences.<\/p>\n<h4>1. Verify Disk Space and Identify Culprit<\/h4>\n<p>First, confirm that your disk is indeed full and identify which directory is consuming the most space.<\/p>\n<ol>\n<li><p><strong>Check overall disk usage:<\/strong><\/p>\n<pre><code class=\"language-bash\">df -h\n<\/code><\/pre>\n<p>Look for a partition (often <code>\/<\/code> or <code>\/var<\/code>) showing 100% <code>Use%<\/code>.<\/p>\n<\/li>\n<li><p><strong>Identify large directories:<\/strong>\nNavigate to the suspected full partition and use <code>du<\/code> to find the largest directories. Start broad and narrow down.<\/p>\n<pre><code class=\"language-bash\">sudo du -h \/var\/log | sort -rh | head -n 10\nsudo du -h \/var\/lib\/postgresql | sort -rh | head -n 10\n<\/code><\/pre>\n<p>You&#039;re likely to see <code>\/var\/log\/postgresql\/<\/code> or a subdirectory within <code>\/var\/lib\/postgresql\/<\/code> consuming significant space. For example:<\/p>\n<pre><code>50G     \/var\/log\/postgresql\n48G     \/var\/log\/postgresql\/postgresql-15-main.log.1\n<\/code><\/pre>\n<\/li>\n<\/ol>\n<h4>2. Stop PostgreSQL Service<\/h4>\n<p>Before manipulating PostgreSQL&#039;s log files or data directory, it is <strong>critical<\/strong> to stop the service to prevent data corruption.<\/p>\n<blockquote class=\"warning\"><p>Do NOT proceed to delete files if PostgreSQL is still running, especially not within its data directory (<code>\/var\/lib\/postgresql<\/code>). This can lead to irreversible data loss or corruption.<\/p>\n<\/blockquote>\n<pre><code class=\"language-bash\">sudo systemctl stop postgresql\n# Or for a specific version:\nsudo systemctl stop postgresql@15-main.service\n<\/code><\/pre>\n<p>Verify the service is stopped:<\/p>\n<pre><code class=\"language-bash\">sudo systemctl status postgresql\n<\/code><\/pre>\n<p>It should show <code>Active: inactive (dead)<\/code>.<\/p>\n<h4>3. Clear Excessive <code>pg_log<\/code> Files<\/h4>\n<p>Once the service is stopped, you can safely remove old log files.<\/p>\n<ol>\n<li><p><strong>Navigate to the PostgreSQL log directory:<\/strong>\nFor Debian, this is typically <code>\/var\/log\/postgresql\/<\/code>.<\/p>\n<pre><code class=\"language-bash\">cd \/var\/log\/postgresql\/\n<\/code><\/pre>\n<\/li>\n<li><p><strong>List files and confirm their size:<\/strong><\/p>\n<pre><code class=\"language-bash\">ls -lh\n<\/code><\/pre>\n<p>This will show you the massive log files, usually named <code>postgresql-&lt;version&gt;-main.log<\/code> or similar, possibly with suffixes like <code>.1<\/code>, <code>.gz<\/code>, etc.<\/p>\n<\/li>\n<li><p><strong>Delete old log files:<\/strong>\nStart by deleting the <em>oldest<\/em> and <em>largest<\/em> log files to free up space. You might need to delete iteratively until you have enough free space to restart the database.<\/p>\n<blockquote class=\"important\"><p>Do not delete the <em>current<\/em> log file (<code>postgresql-&lt;version&gt;-main.log<\/code>) or files still being written to (though this is less likely if the service is stopped). Focus on <code>.1<\/code>, <code>.2<\/code>, <code>.gz<\/code> files.<\/p>\n<p>Be cautious with <code>rm -rf<\/code>. Always verify the directory and files before execution.<\/p>\n<\/blockquote>\n<p>To delete log files older than, say, 7 days:<\/p>\n<pre><code class=\"language-bash\">sudo find . -name &quot;postgresql-*-main.log.*&quot; -mtime +7 -exec rm {} ;\n<\/code><\/pre>\n<p>To delete all but the most recent 2-3 log files (be careful with this, adjust <code>head -n -3<\/code> to suit):<\/p>\n<pre><code class=\"language-bash\"># List files sorted by modification time (oldest first)\nls -t postgresql-*-main.log.* | tail -n +4 | xargs sudo rm -v\n# The above command keeps the 3 newest logs. Adjust `+4` to keep `N-1` logs.\n# E.g., `tail -n +2` keeps 1 newest log.\n# To delete all but the current one and the most recent rotated one:\n# sudo rm postgresql-*-main.log.2* postgresql-*-main.log.3*\n<\/code><\/pre>\n<p>If you&#039;re desperate for space and can afford to lose older logs for recovery:<\/p>\n<pre><code class=\"language-bash\">sudo rm postgresql-*-main.log.*\n<\/code><\/pre>\n<\/li>\n<li><p><strong>Verify free space:<\/strong><\/p>\n<pre><code class=\"language-bash\">df -h\n<\/code><\/pre>\n<p>You should now see some available space. Aim for at least 1-2GB free to allow the database to start and operate normally.<\/p>\n<\/li>\n<\/ol>\n<h4>4. Restart PostgreSQL Service<\/h4>\n<p>With disk space freed, attempt to restart the database service.<\/p>\n<pre><code class=\"language-bash\">sudo systemctl start postgresql\n# Or for a specific version:\nsudo systemctl start postgresql@15-main.service\n<\/code><\/pre>\n<h4>5. Verify Database Operation<\/h4>\n<p>Check the status of the service and confirm your applications can connect.<\/p>\n<ol>\n<li><p><strong>Check service status:<\/strong><\/p>\n<pre><code class=\"language-bash\">sudo systemctl status postgresql\n<\/code><\/pre>\n<p>It should now show <code>Active: active (running)<\/code>.<\/p>\n<\/li>\n<li><p><strong>Check logs for errors:<\/strong><\/p>\n<pre><code class=\"language-bash\">sudo journalctl -u postgresql -f\n<\/code><\/pre>\n<p>Look for <code>LOG: database system is ready to accept connections<\/code>.<\/p>\n<\/li>\n<li><p><strong>Test application connectivity:<\/strong>\nAccess your web application or connect via <code>psql<\/code>:<\/p>\n<pre><code class=\"language-bash\">sudo -u postgres psql\n<\/code><\/pre>\n<p>If you get a <code>psql<\/code> prompt, the database is running. Type <code>q<\/code> to exit.<\/p>\n<\/li>\n<\/ol>\n<h4>6. Implement Log Rotation (Long-Term Fix)<\/h4>\n<p>To prevent this issue from recurring, configure <code>logrotate<\/code> for PostgreSQL. Debian&#039;s <code>postgresql-common<\/code> package usually sets up a basic <code>logrotate<\/code> configuration, but it might be overridden or misconfigured.<\/p>\n<ol>\n<li><p><strong>Inspect existing logrotate configuration:<\/strong>\nCheck <code>\/etc\/logrotate.d\/postgresql-common<\/code> or <code>\/etc\/logrotate.d\/postgresql<\/code>.<\/p>\n<pre><code class=\"language-bash\">sudo cat \/etc\/logrotate.d\/postgresql-common\n<\/code><\/pre>\n<p>A typical configuration might look like this:<\/p>\n<pre><code>\/var\/log\/postgresql\/*.log {\n        daily\n        missingok\n        rotate 7\n        compress\n        delaycompress\n        notifempty\n        create 0640 postgres adm\n        su postgres adm\n        postrotate\n                # Find the PIDs of running postgresql clusters and send them a SIGHUP\n                if [ -e \/etc\/postgresql\/15\/main\/postgresql.conf ] ; then\n                        pg_ctlcluster 15 main reload &gt; \/dev\/null || true\n                fi\n        endscript\n}\n<\/code><\/pre>\n<\/li>\n<li><p><strong>Adjust <code>logrotate<\/code> settings if necessary:<\/strong><\/p>\n<ul>\n<li><code>rotate 7<\/code>: Keeps 7 rotated logs. Adjust this number (e.g., <code>rotate 14<\/code> for two weeks) based on your disk space and retention needs.<\/li>\n<li><code>daily<\/code>, <code>weekly<\/code>, <code>monthly<\/code>: How often logs are rotated. <code>daily<\/code> is generally good for busy systems.<\/li>\n<li><code>compress<\/code>: Compresses old log files. Highly recommended.<\/li>\n<li><code>size 100M<\/code>: You can add <code>size &lt;bytes&gt;<\/code> (e.g., <code>size 100M<\/code>) to force rotation when a log file reaches a certain size, regardless of the <code>daily<\/code>\/<code>weekly<\/code> setting. This is very effective for high-volume logs.<\/li>\n<\/ul>\n<blockquote class=\"important\"><p>If you make changes, ensure they are compatible with existing logrotate options. A common issue is a missing <code>postrotate<\/code> script to signal PostgreSQL to reopen log files. Without it, PostgreSQL might continue writing to the old (renamed) log file. The <code>pg_ctlcluster ... reload<\/code> command correctly handles this.<\/p>\n<\/blockquote>\n<\/li>\n<li><p><strong>Manually test <code>logrotate<\/code>:<\/strong><\/p>\n<pre><code class=\"language-bash\">sudo logrotate -f \/etc\/logrotate.d\/postgresql-common\n<\/code><\/pre>\n<p>This forces a rotation immediately. Check <code>\/var\/log\/postgresql\/<\/code> afterwards to confirm new log files are created and old ones are rotated\/compressed.<\/p>\n<\/li>\n<\/ol>\n<h4>7. Configure PostgreSQL Logging (Proactive)<\/h4>\n<p>Fine-tuning PostgreSQL&#039;s logging behavior in <code>postgresql.conf<\/code> can also help manage disk space.<\/p>\n<ol>\n<li><p><strong>Edit <code>postgresql.conf<\/code>:<\/strong><\/p>\n<pre><code class=\"language-bash\">sudo nano \/etc\/postgresql\/15\/main\/postgresql.conf\n<\/code><\/pre>\n<\/li>\n<li><p><strong>Review and adjust these parameters:<\/strong><\/p>\n<ul>\n<li><code>log_destination<\/code>: Set to <code>stderr<\/code> or <code>csvlog<\/code> (if you want structured logs). <code>stderr<\/code> is standard for syslog\/journal.<\/li>\n<li><code>logging_collector = on<\/code>: (Default) This sends log messages to files managed by PostgreSQL itself. If <code>off<\/code>, logs go to syslog, which then needs syslog&#039;s own rotation. For <code>\/var\/log\/postgresql<\/code> management, keep it <code>on<\/code>.<\/li>\n<li><code>log_directory = &#039;pg_log&#039;<\/code>: (Relative to data directory) or <code>\/var\/log\/postgresql<\/code> (absolute path).<\/li>\n<li><code>log_filename = &#039;postgresql-%Y-%m-%d_%H%M%S.log&#039;<\/code>: This creates unique log files per start time, which <code>logrotate<\/code> can then manage.<\/li>\n<li><code>log_rotation_age = 1d<\/code>: Rotates logs daily. This works in conjunction with <code>logrotate<\/code>.<\/li>\n<li><code>log_rotation_size = 0<\/code>: Disables size-based rotation <em>by PostgreSQL itself<\/em> if <code>logrotate<\/code> is handling it. Set a value (e.g., <code>100MB<\/code>) if you want PostgreSQL to rotate <em>before<\/em> logrotate. It&#039;s often better to let <code>logrotate<\/code> handle it consistently.<\/li>\n<li><code>log_min_duration_statement = 1000<\/code>: Logs all statements taking longer than 1 second (1000ms). Adjust this value to <code>0<\/code> to log all statements (VERY VERBOSE, use with caution on production), or increase it to <code>5000<\/code> (5 seconds) to reduce log volume.<\/li>\n<li><code>log_statement = &#039;none&#039;<\/code>: Logs no statements. Can be <code>ddl<\/code>, <code>mod<\/code>, <code>all<\/code>. <code>none<\/code> is recommended for most production environments unless debugging.<\/li>\n<li><code>log_checkpoints = on<\/code>: Logs each checkpoint. Useful for performance monitoring.<\/li>\n<li><code>log_connections = on<\/code>: Logs successful client connections.<\/li>\n<li><code>log_disconnections = on<\/code>: Logs end of client sessions.<\/li>\n<\/ul>\n<blockquote class=\"important\"><p>Setting <code>log_rotation_age<\/code> or <code>log_rotation_size<\/code> directly in <code>postgresql.conf<\/code> provides a fallback or additional rotation layer. However, <code>logrotate<\/code> is generally preferred for system-wide log management. Ensure your <code>postgresql.conf<\/code> settings don&#039;t conflict or create redundant files. If <code>logrotate<\/code> is robustly configured, you can set <code>log_rotation_age = 0<\/code> and <code>log_rotation_size = 0<\/code> in <code>postgresql.conf<\/code> to let <code>logrotate<\/code> handle it exclusively.<\/p>\n<\/blockquote>\n<\/li>\n<li><p><strong>Restart PostgreSQL after <code>postgresql.conf<\/code> changes:<\/strong><\/p>\n<pre><code class=\"language-bash\">sudo systemctl restart postgresql\n<\/code><\/pre>\n<\/li>\n<\/ol>\n<h4>8. Consider Dedicated Disk\/Partition (Advanced)<\/h4>\n<p>For high-traffic production databases, it&#039;s often prudent to separate PostgreSQL&#039;s data and log directories onto dedicated partitions or even separate physical disks. This isolates I\/O and prevents a full log partition from impacting the entire system or vice-versa.<\/p>\n<ol>\n<li><p><strong>Mount a new disk\/partition:<\/strong>\nCreate a new filesystem and mount it, e.g., to <code>\/mnt\/pg_data<\/code> or <code>\/mnt\/pg_logs<\/code>.\nEdit <code>\/etc\/fstab<\/code> to ensure it mounts automatically on boot.<\/p>\n<\/li>\n<li><p><strong>Move PostgreSQL data or logs:<\/strong><\/p>\n<ul>\n<li><strong>Moving logs:<\/strong> Change <code>log_directory<\/code> in <code>postgresql.conf<\/code> to point to the new location (e.g., <code>\/mnt\/pg_logs<\/code>). Remember to create the directory and set correct permissions (<code>chown postgres:postgres \/mnt\/pg_logs<\/code>).<\/li>\n<li><strong>Moving data directory:<\/strong> This is more involved. You would stop PostgreSQL, copy <code>\/var\/lib\/postgresql\/<\/code> to the new location (e.g., <code>\/mnt\/pg_data<\/code>), update <code>data_directory<\/code> in <code>postgresql.conf<\/code>, and potentially update the systemd service file or <code>pg_ctlcluster<\/code> configuration.<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n<p>This comprehensive approach will not only resolve the immediate disk space and lockfile issues but also establish robust logging and maintenance practices to prevent recurrence.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Fix PostgreSQL database locks and service failures on Debian 12 caused by full pg_log disk space. Learn to clear logs, free space, and prevent recurrence.<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[187],"tags":[100,282,76,40,124,281,150,56,6],"class_list":["post-108","post","type-post","status-publish","format-standard","hentry","category-database","tag-bookworm","tag-database-lock","tag-debian-12","tag-devops","tag-disk-space","tag-pg_log","tag-postgresql","tag-systemd","tag-troubleshooting"],"_links":{"self":[{"href":"https:\/\/butitworkedlocal.com\/wp-json\/wp\/v2\/posts\/108","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/butitworkedlocal.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/butitworkedlocal.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/butitworkedlocal.com\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/butitworkedlocal.com\/wp-json\/wp\/v2\/comments?post=108"}],"version-history":[{"count":2,"href":"https:\/\/butitworkedlocal.com\/wp-json\/wp\/v2\/posts\/108\/revisions"}],"predecessor-version":[{"id":356,"href":"https:\/\/butitworkedlocal.com\/wp-json\/wp\/v2\/posts\/108\/revisions\/356"}],"wp:attachment":[{"href":"https:\/\/butitworkedlocal.com\/wp-json\/wp\/v2\/media?parent=108"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/butitworkedlocal.com\/wp-json\/wp\/v2\/categories?post=108"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/butitworkedlocal.com\/wp-json\/wp\/v2\/tags?post=108"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}