Tag: postgresql

  • PostgreSQL pg_hba.conf Connection Authorization Failed on CentOS Stream / Rocky Linux

    Troubleshoot and resolve 'connection authorization failed' errors in PostgreSQL on CentOS Stream and Rocky Linux by correctly configuring pg_hba.conf and related settings.

    When working with PostgreSQL on CentOS Stream or Rocky Linux, encountering a "connection authorization failed" error indicates that the database server successfully received your connection request but explicitly denied it based on its access control rules. This guide provides a comprehensive, expert-level approach to diagnose and resolve this common issue, ensuring your applications and users can connect securely.

    Symptom & Error Signature

    The primary symptom is an inability to connect to your PostgreSQL database, typically from a client application, a command-line psql utility, or another server. You will usually see a FATAL error message.

    Typical psql command line error:

    $ psql -h your_db_host -U your_db_user -d your_db_name
    psql: FATAL:  connection authorization failed for user "your_db_user"
    psql: FATAL:  no pg_hba.conf entry for host "your_client_ip", user "your_db_user", database "your_db_name", no encryption

    Common application error (e.g., Python with psycopg2):

    # Example output from a Python application attempting to connect
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    psycopg2.OperationalError: FATAL:  connection authorization failed for user "web_app_user"
    FATAL:  no pg_hba.conf entry for host "192.168.1.100", user "web_app_user", database "webapp_db"

    PostgreSQL server log entries (found in /var/lib/pgsql/data/log/postgresql-*.log or journalctl -u postgresql-1X):

    202X-XX-XX XX:XX:XX UTC [12345] LOG:  connection received: host=192.168.1.100 port=54321
    202X-XX-XX XX:XX:XX UTC [12345] FATAL:  no pg_hba.conf entry for host "192.168.1.100", user "web_app_user", database "webapp_db", no encryption
    ```

    Root Cause Analysis

    The "connection authorization failed" error almost exclusively points to an incorrect or missing entry in PostgreSQL's Host-Based Authentication (HBA) configuration file, pg_hba.conf. This file controls which hosts are allowed to connect, which users they can connect as, which databases they can access, and what authentication method is required.

    The underlying reasons typically fall into one of these categories:

    1. Missing pghba.conf Entry: The most common cause. There is no rule in pghba.conf that matches the incoming connection's parameters (source IP, user, database).
    2. Incorrect pg_hba.conf Entry: An existing entry is present, but one or more of its fields (e.g., source IP, user, database, authentication method) do not precisely match the connection attempt.
    3. Incorrect Order of Rules: pg_hba.conf rules are processed sequentially from top to bottom. The first rule that matches the connection attempt is used. If a broad, less secure rule appears before a more specific, secure rule, it might inadvertently allow or deny connections in unexpected ways.
    4. Incorrect Authentication Method: The pg_hba.conf entry specifies an authentication method (e.g., scram-sha-256, md5, trust, peer, ident) that doesn't match the client's provided credentials or the server's configured user password.
    5. * scram-sha-256: The modern, recommended secure password-based authentication.
    6. * md5: An older, less secure password-based authentication, still widely used.
    7. * trust: Allows anyone to connect without a password (highly insecure for non-local connections).
    8. * peer: Used for local connections where the operating system user matches the database user.
    9. * ident: Similar to peer, relies on an ident server on the client for authentication.
    10. listenaddresses Misconfiguration: While this usually results in "connection refused," if listenaddresses in postgresql.conf is set to localhost or 127.0.0.1 and a remote client tries to connect, the connection will not even reach the pg_hba.conf stage for remote IP addresses. It's essential to ensure PostgreSQL is listening on the correct network interfaces (e.g., * for all, or specific IP addresses).
    11. Incorrect Database/User Permissions: Even if pghba.conf allows the connection, the user might not have CONNECT privileges on the requested database or USAGE on specific schemas, leading to application errors after authentication. This is different from the pghba.conf error but often confused.

    Step-by-Step Resolution

    Follow these steps carefully to diagnose and resolve the pg_hba.conf connection authorization error.

    1. Locate pg_hba.conf and postgresql.conf

    First, you need to find the correct configuration files. The location can vary slightly depending on the PostgreSQL version and installation method.

    # Log in as the postgres user (or use sudo) to execute psql commands
    sudo -u postgres psql -c 'SHOW hba_file;'
    sudo -u postgres psql -c 'SHOW config_file;'

    Common locations on CentOS Stream / Rocky Linux for PostgreSQL 12-16:

    • pghba.conf: /var/lib/pgsql/data/pghba.conf (for older versions/manual setup) or /var/lib/pgsql/1X/data/pg_hba.conf (where 1X is your PostgreSQL major version, e.g., 15).
    • postgresql.conf: /var/lib/pgsql/data/postgresql.conf or /var/lib/pgsql/1X/data/postgresql.conf.

    [!NOTE] On modern CentOS/Rocky systems, PostgreSQL is often installed via dnf, and the data directory is version-specific (e.g., /var/lib/pgsql/15/data).

    2. Backup Original Configuration Files

    Before making any changes, always back up your configuration files.

    PG_VERSION=$(sudo -u postgres psql -t -P format=unaligned -c 'SHOW hba_file;' | cut -d'/' -f5) # Extracts '15' from '/var/lib/pgsql/15/data/pg_hba.conf'

    sudo cp ${PGCONFIGDIR}/pghba.conf ${PGCONFIGDIR}/pghba.conf.bak.$(date +%F-%H%M) sudo cp ${PGCONFIGDIR}/postgresql.conf ${PGCONFIGDIR}/postgresql.conf.bak.$(date +%F-%H%M) `

    3. Understand pg_hba.conf Syntax

    Each line in pg_hba.conf defines an access rule. Comments start with #. Blank lines are ignored. A rule typically follows this format:

    TYPE DATABASE USER ADDRESS METHOD [OPTIONS]

    • TYPE: Specifies the connection type.
    • * local: Connections via Unix-domain sockets (local access only).
    • * host: Connections via TCP/IP (both IPv4 and IPv6).
    • * hostssl: TCP/IP connections only if SSL is used.
    • hostnossl: TCP/IP connections only if SSL is not* used.
    • DATABASE: Which database(s) this rule applies to. Can be all, a specific database name, or replication (for streaming replication).
    • USER: Which user(s) this rule applies to. Can be all, a specific user name, or a group name prefixed with +.
    • ADDRESS: The client's IP address range or host.
    • * 127.0.0.1/32 or localhost: Only from the local machine (IPv4).
    • * ::1/128: Only from the local machine (IPv6).
    • * 0.0.0.0/0: All IPv4 addresses (highly insecure for most authentication methods).
    • * 192.168.1.0/24: A specific network range.
    • * 10.0.0.10/32: A single specific IP address.
    • METHOD: The authentication method. scram-sha-256 (recommended), md5, trust, peer, ident, gssapi, ssi.
    • OPTIONS: Additional options specific to the authentication method.

    4. Edit pg_hba.conf to Allow Connections

    Using the information from the error message (client IP, user, database), add or modify an entry in pg_hba.conf. Open the file with your preferred text editor (e.g., vi or nano).

    sudo vi ${PG_CONFIG_DIR}/pg_hba.conf

    Common Scenarios and Solutions:

    Scenario 1: Allow a specific application user from a specific IP address (most common and recommended). Add this line at the end of your pg_hba.conf file, or logically group it with other host entries:

    # TYPE  DATABASE        USER            ADDRESS                 METHOD
    host    webapp_db       web_app_user    192.168.1.100/32        scram-sha-256
    ```
    *   Replace `webapp_db` with your database name.
    *   Replace `web_app_user` with your database username.
    *   Replace `192.168.1.100/32` with the *exact IP address* of the client connecting to PostgreSQL. Use `/32` for a single IPv4 address or `/128` for a single IPv6 address. For a network, use the appropriate CIDR (e.g., `192.168.1.0/24`).

    Scenario 2: Allow all users from localhost for a specific database (for local applications/CLI tools).

    # TYPE  DATABASE        USER            ADDRESS                 METHOD
    host    your_db_name    all             127.0.0.1/32            scram-sha-256
    host    your_db_name    all             ::1/128                 scram-sha-256

    Scenario 3: Allow local connections using peer authentication (recommended for local postgres user). This is typically already present and allows the Linux postgres user to connect to PostgreSQL as the postgres database user via Unix sockets without a password.

    # TYPE  DATABASE        USER            ADDRESS                 METHOD
    local   all             postgres                                peer

    [!WARNING] Avoid using trust for remote connections (host) as it allows anyone to connect without any authentication. Only use trust for local connections in highly controlled environments or for specific, temporary debugging.

    host all all 0.0.0.0/0 md5 – This rule is highly insecure as it allows all users from any IP to connect to any database using a password. Only use 0.0.0.0/0 if you have very strict firewall rules in place, and even then, consider restricting it.

    5. Verify listen_addresses in postgresql.conf

    While pg_hba.conf handles authorization, postgresql.conf determines where PostgreSQL listens for connections. If PostgreSQL isn't listening on the correct network interface, remote connections will result in "connection refused," not "authorization failed." However, it's a common point of confusion.

    Open postgresql.conf:

    sudo vi ${PG_CONFIG_DIR}/postgresql.conf

    Find the listen_addresses parameter and ensure it's configured correctly:

    # What IP address(es) to listen on; '*' means all IP interfaces.
    # In a production environment, it is best to be explicit.
    #listen_addresses = 'localhost'         # (change requires restart)
    listen_addresses = '*'                  # Listen on all available interfaces
    #listen_addresses = '192.168.1.50,localhost' # Listen on specific IPs and localhost

    [!IMPORTANT] Changing listen_addresses requires a restart of the PostgreSQL service, not just a reload.

    6. Reload or Restart PostgreSQL

    After modifying pghba.conf, you must reload PostgreSQL for the changes to take effect. If you changed listenaddresses in postgresql.conf, a full restart is required.

    Reload (for pg_hba.conf changes):

    # Get the PostgreSQL service name (e.g., postgresql-15)

    sudo systemctl reload ${PG_SERVICE} `

    Restart (for postgresql.conf changes or if reload doesn't work):

    sudo systemctl restart ${PG_SERVICE}

    [!NOTE] systemctl reload is generally preferred as it doesn't drop existing connections. However, if issues persist or if listen_addresses was changed, a restart is necessary.

    7. Check Firewall Rules (firewalld)

    While less likely to cause an "authorization failed" error (which implies the connection reached PostgreSQL), firewall rules can prevent connections entirely, leading to "connection refused." It's a good practice to verify if you're troubleshooting any connection issue.

    PostgreSQL typically listens on port 5432. Ensure this port is open on your CentOS Stream/Rocky Linux server.

    # Check current firewall status

    If port 5432 is not listed, add it (for public zone, adjust if needed) sudo firewall-cmd –zone=public –add-port=5432/tcp –permanent sudo firewall-cmd –reload `

    8. Verify PostgreSQL User and Password

    Ensure the database user exists and has the correct password set, matching the authentication method in pg_hba.conf.

    # Connect as the postgres superuser

    List users and their attributes (look for your user) du

    If the user doesn't exist, create it: CREATE USER webappuser WITH PASSWORD 'averystrong_password' VALID UNTIL '2028-01-01';

    If the password needs to be set/reset (especially for scram-sha-256): ALTER USER webappuser WITH PASSWORD 'newstrongpassword';

    Grant connect privileges to the database (if not already done) GRANT CONNECT ON DATABASE webappdb TO webapp_user;

    Quit psql q `

    [!IMPORTANT] PostgreSQL 10+ defaults to scram-sha-256 for new password hashes. If your pghba.conf uses md5 and the user password was created more recently, there might be a mismatch. You can explicitly set the password using ALTER USER ... WITH PASSWORD ... and ensure pghba.conf matches.

    9. Test the Connection

    After making all changes and reloading/restarting PostgreSQL, attempt to connect again from your client or application.

    # From the client machine or server itself
    psql -h your_db_host -U your_db_user -d your_db_name

    If successful, you should be prompted for a password (if using scram-sha-256 or md5) and then connect to the database. If the error persists, carefully review the PostgreSQL logs for the exact FATAL message and re-check each step, paying close attention to IP addresses, user names, database names, and authentication methods in your pg_hba.conf file.

  • Troubleshooting: Laravel `artisan migrate` SQLSTATE Base Table or View Not Found on macOS Local Environment

    Resolve the 'SQLSTATE base table or view not found' error during Laravel migrations on macOS. This guide covers common causes and fixes for local development setups.

    Introduction

    Encountering a SQLSTATE base table or view not found error while running php artisan migrate in your Laravel project on a macOS local development environment can be frustrating. This error typically indicates that your Laravel application is unable to locate or interact with expected database tables, such as the migrations table or other application-specific tables, despite seemingly connecting to the database. This guide will walk you through the common causes and provide a systematic approach to resolve this issue, ensuring your development workflow remains smooth.

    Symptom & Error Signature

    When you execute php artisan migrate in your terminal, you might see output similar to this:

    php artisan migrate

    20230101000000createuserstable …………………………………………………………………………. RUNNING 20230101000000createuserstable …………………………………………………………………………. FAILED

    IlluminateDatabaseQueryException

    SQLSTATE[42S02]: Base table or view not found: 1146 Table 'yourdatabasename.users' doesn't exist (SQL: create table users (id bigint unsigned not null autoincrement primary key, name varchar(255) not null, email varchar(255) not null, emailverifiedat timestamp null, password varchar(255) not null, remembertoken varchar(100) null, createdat timestamp null, updatedat timestamp null) default character set utf8mb4 collate utf8mb4unicodeci)

    at vendor/laravel/framework/src/Illuminate/Database/Connection.php:826 822| // If an exception occurs while attempting to run a query, we'll format the error 823| // message to include the bindings with the query, which will make it much 824| // easier to debug whilst still getting the actual exception instance. 825| catch (Exception $e) { > 826| throw new QueryException( 827| $query, $this->prepareBindings($bindings), $e 828| ); 829| } 830| `

    The key part of the error message is SQLSTATE[42S02]: Base table or view not found. This usually occurs when Laravel attempts to create the migrations table (to track executed migrations) or your first application table (like users) and cannot find it or the database itself isn't correctly set up or targeted.

    Root Cause Analysis

    This error, specifically SQLSTATE[42S02]: Base table or view not found, primarily indicates that while your Laravel application successfully connected to a database server, it could not find a specific database or table it was trying to access or create. The most common underlying reasons for this on a macOS local environment include:

    1. Incorrect Database Configuration: The .env file contains incorrect database credentials, host, port, or database name, leading Laravel to connect to the wrong database server, a non-existent database, or a database where the specified table truly doesn't exist.
    2. Database Service Not Running: The MySQL or PostgreSQL database server (or its Docker container) is not currently running on your macOS machine. Laravel might fail to establish a connection, or worse, connect to a different local database instance that is running (e.g., a default system-level database or another project's database).
    3. Database Not Created: The database specified in your .env file (e.g., DBDATABASE=yourapp_db) has not been manually created on your database server. Laravel's migration command doesn't create the database itself; it only creates tables within an existing database.
    4. Stale Configuration Cache: Laravel's configuration might be cached, causing it to use outdated database credentials even after you've updated your .env file.
    5. Insufficient Database User Privileges: The database user specified in .env (e.g., DB_USERNAME=root) lacks the necessary permissions to create tables or access the specified database.
    6. Conflicting Database Services: Multiple database services (e.g., Homebrew MySQL and a Dockerized MySQL instance) might be running simultaneously, leading to confusion about which database server Laravel is connecting to.
    7. Docker/Laravel Sail Specific Issues: If using Docker, the database container might not be healthy, or its internal hostname/port mapping might be misconfigured.

    Step-by-Step Resolution

    Follow these steps to diagnose and resolve the SQLSTATE base table or view not found error.

    1. Verify Database Service Status

    Ensure your database server (MySQL, PostgreSQL, or Docker container) is actively running.

    For Homebrew-installed MySQL/PostgreSQL:

    # To check MySQL status
    brew services list | grep mysql

    To check PostgreSQL status brew services list | grep postgresql # Expected output: postgresql started yourusername /path/to/postgresql.plist `

    If the service is not started, start it:

    brew services start mysql
    # OR
    brew services start postgresql

    For Docker/Laravel Sail:

    # Navigate to your Laravel project root

    Check running Docker containers docker ps -a # OR if using docker compose directly docker compose ps -a `

    Look for containers named similar to yourprojectname-mysql-1 or yourprojectname-pgsql-1. Ensure their STATUS is Up. If they are stopped or exited:

    # For Laravel Sail

    For generic Docker Compose docker compose up -d `

    [!IMPORTANT] Always ensure your database service is running before attempting any database operations with Laravel.

    2. Inspect .env Configuration

    The .env file is crucial for local development. Double-check every database-related entry.

    # Open your .env file in a text editor
    nano .env

    Review the DBCONNECTION, DBHOST, DBPORT, DBDATABASE, DBUSERNAME, and DBPASSWORD variables.

    Example for MySQL:

    DB_CONNECTION=mysql
    DB_HOST=127.0.0.1      # Or localhost. If using Docker, it might be the service name (e.g., mysql or db)
    DB_PORT=3306           # Standard MySQL port
    DB_DATABASE=your_app_db_name # IMPORTANT: This must be an existing database
    DB_USERNAME=root       # Or your database user
    DB_PASSWORD=            # Or your database user's password

    Example for PostgreSQL:

    DB_CONNECTION=pgsql
    DB_HOST=127.0.0.1      # Or localhost. If using Docker, it might be the service name (e.g., pgsql or db)
    DB_PORT=5432           # Standard PostgreSQL port
    DB_DATABASE=your_app_db_name # IMPORTANT: This must be an existing database
    DB_USERNAME=postgres   # Or your database user
    DB_PASSWORD=secret      # Or your database user's password

    [!IMPORTANT] Pay close attention to DBHOST. For Docker/Laravel Sail, DBHOST should typically be the service name defined in docker-compose.yml (e.g., mysql or db), not 127.0.0.1 unless port mapping is explicitly used for external access. For Valet/Herd, 127.0.0.1 or localhost is usually correct.

    3. Confirm Database Existence and Accessibility

    Laravel's artisan migrate command expects the database specified in DB_DATABASE to already exist. It does not create the database itself.

    Connect via mysql client (for MySQL):

    mysql -u DB_USERNAME -p -h DB_HOST -P DB_PORT
    # Example: mysql -u root -p -h 127.0.0.1 -P 3306
    # Enter password when prompted.

    Once connected, list existing databases:

    SHOW DATABASES;

    If your DB_DATABASE is not listed, create it:

    CREATE DATABASE your_app_db_name CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

    Then exit the client:

    EXIT;

    Connect via psql client (for PostgreSQL):

    psql -U DB_USERNAME -h DB_HOST -p DB_PORT
    # Example: psql -U postgres -h 127.0.0.1 -p 5432
    # Enter password when prompted.

    Once connected, list existing databases:

    l

    If your DB_DATABASE is not listed, create it:

    CREATE DATABASE your_app_db_name;

    Then exit the client:

    q

    4. Clear Laravel Configuration Caches

    Stale cached configuration can prevent Laravel from reading your updated .env file correctly. Always clear caches after making changes to .env or configuration files.

    php artisan cache:clear
    php artisan config:clear
    php artisan route:clear
    php artisan view:clear
    composer dump-autoload

    [!IMPORTANT] Running php artisan config:clear is particularly important after modifying the .env file, as Laravel's configuration cache can override .env variables.

    5. Verify Database User Privileges

    Ensure the database user (e.g., root, postgres, or a custom user) specified in your .env has sufficient privileges to create, alter, and drop tables on the database.

    For MySQL (connected as a privileged user like root):

    -- Grant all privileges to 'your_username' on 'your_app_db_name'
    GRANT ALL PRIVILEGES ON your_app_db_name.* TO 'your_username'@'localhost' IDENTIFIED BY 'your_password';
    FLUSH PRIVILEGES;
    ```

    For PostgreSQL (connected as a privileged user like postgres):

    -- Grant all privileges on database 'your_app_db_name' to 'your_username'
    GRANT ALL PRIVILEGES ON DATABASE your_app_db_name TO your_username;

    6. Re-run Migrations

    After performing the above checks and corrections, attempt to run your migrations again.

    php artisan migrate

    If you previously ran some migrations on a different or corrupted database, and you are starting fresh, you might consider:

    > [!WARNING]

    php artisan migrate:fresh –seed # Drops all tables, runs migrations, and then runs seeders `

    7. Specific Environment Considerations (Docker/Laravel Sail, Valet/Herd)

    For Laravel Sail (Docker):

    • If you're using Sail, DB_HOST in your .env should usually be mysql (for MySQL) or pgsql (for PostgreSQL), as defined in your docker-compose.yml.
    • Ensure the Sail containers are healthy: sail ps. If issues persist, try rebuilding and restarting:
    • `bash
    • sail down –rmi all -v # Removes containers, images, and volumes (data loss for DB)
    • sail up -d
    • sail artisan migrate # Use sail artisan instead of php artisan
    • `

    For Laravel Valet/Herd:

    • Valet and Herd manage services (PHP, Nginx, MySQL/PostgreSQL) via Homebrew. Ensure they are running as described in step 1.
    • Sometimes, a simple restart helps:
    • `bash
    • valet restart
    • # OR
    • herd restart
    • `

    8. Check for Other Processes on Database Port

    Ensure no other applications are using the default database port (e.g., 3306 for MySQL, 5432 for PostgreSQL). This can lead to connection issues.

    # For MySQL (port 3306)

    For PostgreSQL (port 5432) sudo lsof -i :5432 `

    If another process is listening, identify it and stop it, or configure your database (and Laravel) to use a different port.

    By systematically working through these steps, you should be able to identify and resolve the SQLSTATE base table or view not found error, allowing your Laravel migrations to run successfully on your macOS local development environment.

  • Resolving PostgreSQL pg_log Disk Space Exhaustion & Database Lock on Debian 12 Bookworm

    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.

    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's transaction logs or pg_log files can consume all available space, leading to critical database failures, service unresponsiveness, and "lockfile" 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.

    Symptom & Error Signature

    Users typically experience one or more of the following symptoms:

    • Web applications reporting database connection errors (e.g., "could not connect to server: Connection refused", "FATAL: remaining connection slots are reserved for non-replication superuser connections").
    • The PostgreSQL service (often [email protected] or similar) is reported as failed or inactive by systemctl.
    • Attempts to start or restart PostgreSQL fail immediately.
    • System logs (journalctl) show errors related to disk I/O or inability to write.

    Here are typical error messages you might encounter:

    From journalctl -u postgresql (or journalctl -u [email protected] for specific versions):

    systemd[1]: [email protected]: Start request repeated too quickly.
    systemd[1]: [email protected]: Failed with result 'exit-code'.
    systemd[1]: Failed to start PostgreSQL Cluster 15-main.
    ...
    postgresql[12345]: 2024-06-25 10:30:00.123 UTC [12345] LOG: could not write lock file "/var/run/postgresql/.s.PGSQL.5432.lock": No space left on device
    postgresql[12345]: 2024-06-25 10:30:00.123 UTC [12345] FATAL: could not create lock file "/var/run/postgresql/.s.PGSQL.5432.lock": No space left on device
    postgresql[12345]: 2024-06-25 10:30:00.123 UTC [12345] LOG: database system is shut down

    From dmesg (kernel messages, indicating disk I/O issues):

    [123456.789012] EXT4-fs (sda1): write access unavailable, fs is full
    [123456.789013] EXT4-fs (sda1): Remounting filesystem read-only

    From df -h (showing disk usage):

    Filesystem      Size  Used Avail Use% Mounted on
    /dev/sda1        50G   50G     0 100% /
    udev             10M    0   10M   0% /dev
    tmpfs           7.8G  1.2M  7.8G   1% /run

    Root Cause Analysis

    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 (/) if /var is not a separate mount, or on a dedicated /var partition.

    Here's a breakdown of why this leads to the observed symptoms:

    1. Excessive Log Growth: PostgreSQL, by default, writes detailed logs to files within a directory like /var/log/postgresql/ or within the data directory itself (/var/lib/postgresql/<version>/main/log/). Without proper log rotation (e.g., via logrotate), these log files can grow indefinitely, consuming gigabytes or even terabytes of disk space over time.
    2. Lack of Free Space: Once the filesystem reaches 100% utilization, the operating system can no longer write any new data to that disk.
    3. PostgreSQL's Inability to Operate: PostgreSQL requires free disk space for several critical operations:
    4. * Writing Log Files: It cannot log new events, leading to internal errors.
    5. * Writing Transaction Logs (WAL): Crucial for data integrity and recovery. While WAL segments are usually pre-allocated, operational temporary files and checkpoints might require space.
    6. * Creating Lock Files: When PostgreSQL starts, it creates a lock file (e.g., /var/run/postgresql/.s.PGSQL.5432.lock) to prevent multiple instances from running on the same port. If it cannot create this file, it fails to start.
    7. * Temporary Files and Caching: Queries that require large sorts or joins might create temporary files on disk. If these cannot be written, queries will fail.
    8. Service Failure: 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. systemd will then report the service as failed.

    Step-by-Step Resolution

    Follow these steps carefully to recover your PostgreSQL service and prevent future occurrences.

    1. Verify Disk Space and Identify Culprit

    First, confirm that your disk is indeed full and identify which directory is consuming the most space.

    1. Check overall disk usage:
    2. `bash
    3. df -h
    4. `
    5. Look for a partition (often / or /var) showing 100% Use%.
    1. Identify large directories:
    2. Navigate to the suspected full partition and use du to find the largest directories. Start broad and narrow down.
    3. `bash
    4. sudo du -h /var/log | sort -rh | head -n 10
    5. sudo du -h /var/lib/postgresql | sort -rh | head -n 10
    6. `
    7. You're likely to see /var/log/postgresql/ or a subdirectory within /var/lib/postgresql/ consuming significant space. For example:
    8. `
    9. 50G /var/log/postgresql
    10. 48G /var/log/postgresql/postgresql-15-main.log.1
    11. `

    2. Stop PostgreSQL Service

    Before manipulating PostgreSQL's log files or data directory, it is critical to stop the service to prevent data corruption.

    [!WARNING] Do NOT proceed to delete files if PostgreSQL is still running, especially not within its data directory (/var/lib/postgresql). This can lead to irreversible data loss or corruption.

    sudo systemctl stop postgresql
    # Or for a specific version:
    sudo systemctl stop [email protected]

    Verify the service is stopped: `bash sudo systemctl status postgresql ` It should show Active: inactive (dead).

    3. Clear Excessive pg_log Files

    Once the service is stopped, you can safely remove old log files.

    1. Navigate to the PostgreSQL log directory:
    2. For Debian, this is typically /var/log/postgresql/.
    3. `bash
    4. cd /var/log/postgresql/
    5. `
    1. List files and confirm their size:
    2. `bash
    3. ls -lh
    4. `
    5. This will show you the massive log files, usually named postgresql-<version>-main.log or similar, possibly with suffixes like .1, .gz, etc.
    1. Delete old log files:
    2. Start by deleting the oldest and largest log files to free up space. You might need to delete iteratively until you have enough free space to restart the database.

    [!IMPORTANT] > Do not delete the current log file (postgresql-<version>-main.log) or files still being written to (though this is less likely if the service is stopped). Focus on .1, .2, .gz files. > > Be cautious with rm -rf. Always verify the directory and files before execution.

    To delete log files older than, say, 7 days: `bash sudo find . -name "postgresql--main.log." -mtime +7 -exec rm {} ; ` To delete all but the most recent 2-3 log files (be careful with this, adjust head -n -3 to suit): `bash # List files sorted by modification time (oldest first) ls -t postgresql--main.log. | tail -n +4 | xargs sudo rm -v # The above command keeps the 3 newest logs. Adjust +4 to keep N-1 logs. # E.g., tail -n +2 keeps 1 newest log. # To delete all but the current one and the most recent rotated one: # sudo rm postgresql--main.log.2 postgresql--main.log.3 ` If you're desperate for space and can afford to lose older logs for recovery: `bash sudo rm postgresql--main.log. `

    1. Verify free space:
    2. `bash
    3. df -h
    4. `
    5. You should now see some available space. Aim for at least 1-2GB free to allow the database to start and operate normally.

    4. Restart PostgreSQL Service

    With disk space freed, attempt to restart the database service.

    sudo systemctl start postgresql
    # Or for a specific version:
    sudo systemctl start [email protected]

    5. Verify Database Operation

    Check the status of the service and confirm your applications can connect.

    1. Check service status:
    2. `bash
    3. sudo systemctl status postgresql
    4. `
    5. It should now show Active: active (running).
    1. Check logs for errors:
    2. `bash
    3. sudo journalctl -u postgresql -f
    4. `
    5. Look for LOG: database system is ready to accept connections.
    1. Test application connectivity:
    2. Access your web application or connect via psql:
    3. `bash
    4. sudo -u postgres psql
    5. `
    6. If you get a psql prompt, the database is running. Type q to exit.

    6. Implement Log Rotation (Long-Term Fix)

    To prevent this issue from recurring, configure logrotate for PostgreSQL. Debian's postgresql-common package usually sets up a basic logrotate configuration, but it might be overridden or misconfigured.

    1. Inspect existing logrotate configuration:
    2. Check /etc/logrotate.d/postgresql-common or /etc/logrotate.d/postgresql.
    3. `bash
    4. sudo cat /etc/logrotate.d/postgresql-common
    5. `
    6. A typical configuration might look like this:
    7. `
    8. /var/log/postgresql/*.log {
    9. daily
    10. missingok
    11. rotate 7
    12. compress
    13. delaycompress
    14. notifempty
    15. create 0640 postgres adm
    16. su postgres adm
    17. postrotate
    18. # Find the PIDs of running postgresql clusters and send them a SIGHUP
    19. if [ -e /etc/postgresql/15/main/postgresql.conf ] ; then
    20. pg_ctlcluster 15 main reload > /dev/null || true
    21. fi
    22. endscript
    23. }
    24. `
    1. Adjust logrotate settings if necessary:
    2. * rotate 7: Keeps 7 rotated logs. Adjust this number (e.g., rotate 14 for two weeks) based on your disk space and retention needs.
    3. * daily, weekly, monthly: How often logs are rotated. daily is generally good for busy systems.
    4. * compress: Compresses old log files. Highly recommended.
    5. * size 100M: You can add size <bytes> (e.g., size 100M) to force rotation when a log file reaches a certain size, regardless of the daily/weekly setting. This is very effective for high-volume logs.
    6. > [!IMPORTANT]
    7. > If you make changes, ensure they are compatible with existing logrotate options. A common issue is a missing postrotate script to signal PostgreSQL to reopen log files. Without it, PostgreSQL might continue writing to the old (renamed) log file. The pg_ctlcluster ... reload command correctly handles this.
    1. Manually test logrotate:
    2. `bash
    3. sudo logrotate -f /etc/logrotate.d/postgresql-common
    4. `
    5. This forces a rotation immediately. Check /var/log/postgresql/ afterwards to confirm new log files are created and old ones are rotated/compressed.

    7. Configure PostgreSQL Logging (Proactive)

    Fine-tuning PostgreSQL's logging behavior in postgresql.conf can also help manage disk space.

    1. Edit postgresql.conf:
    2. `bash
    3. sudo nano /etc/postgresql/15/main/postgresql.conf
    4. `
    1. Review and adjust these parameters:
    • log_destination: Set to stderr or csvlog (if you want structured logs). stderr is standard for syslog/journal.
    • * logging_collector = on: (Default) This sends log messages to files managed by PostgreSQL itself. If off, logs go to syslog, which then needs syslog's own rotation. For /var/log/postgresql management, keep it on.
    • * logdirectory = 'pglog': (Relative to data directory) or /var/log/postgresql (absolute path).
    • * logfilename = 'postgresql-%Y-%m-%d%H%M%S.log': This creates unique log files per start time, which logrotate can then manage.
    • * logrotationage = 1d: Rotates logs daily. This works in conjunction with logrotate.
    • logrotationsize = 0: Disables size-based rotation by PostgreSQL itself if logrotate is handling it. Set a value (e.g., 100MB) if you want PostgreSQL to rotate before* logrotate. It's often better to let logrotate handle it consistently.
    • * logminduration_statement = 1000: Logs all statements taking longer than 1 second (1000ms). Adjust this value to 0 to log all statements (VERY VERBOSE, use with caution on production), or increase it to 5000 (5 seconds) to reduce log volume.
    • * log_statement = 'none': Logs no statements. Can be ddl, mod, all. none is recommended for most production environments unless debugging.
    • * log_checkpoints = on: Logs each checkpoint. Useful for performance monitoring.
    • * log_connections = on: Logs successful client connections.
    • * log_disconnections = on: Logs end of client sessions.

    [!IMPORTANT] > Setting logrotationage or logrotationsize directly in postgresql.conf provides a fallback or additional rotation layer. However, logrotate is generally preferred for system-wide log management. Ensure your postgresql.conf settings don't conflict or create redundant files. If logrotate is robustly configured, you can set logrotationage = 0 and logrotationsize = 0 in postgresql.conf to let logrotate handle it exclusively.

    1. Restart PostgreSQL after postgresql.conf changes:
    2. `bash
    3. sudo systemctl restart postgresql
    4. `

    8. Consider Dedicated Disk/Partition (Advanced)

    For high-traffic production databases, it's often prudent to separate PostgreSQL'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.

    1. Mount a new disk/partition:
    2. Create a new filesystem and mount it, e.g., to /mnt/pgdata or /mnt/pglogs.
    3. Edit /etc/fstab to ensure it mounts automatically on boot.
    1. Move PostgreSQL data or logs:
    2. * Moving logs: Change logdirectory in postgresql.conf to point to the new location (e.g., /mnt/pglogs). Remember to create the directory and set correct permissions (chown postgres:postgres /mnt/pg_logs).
    3. * Moving data directory: This is more involved. You would stop PostgreSQL, copy /var/lib/postgresql/ to the new location (e.g., /mnt/pgdata), update datadirectory in postgresql.conf, and potentially update the systemd service file or pg_ctlcluster configuration.

    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.

  • Troubleshooting PostgreSQL shared_buffers Memory Allocation Crash on Ubuntu 20.04 LTS

    Resolve critical PostgreSQL startup failures on Ubuntu 20.04 due to misconfigured shared_buffers exceeding system or kernel shared memory limits.

    When managing high-performance PostgreSQL databases, optimizing memory usage through parameters like shared_buffers is crucial. However, misconfiguring this setting can lead to a critical PostgreSQL service crash upon startup, presenting as a "Cannot allocate memory" error specifically related to shared memory segments. This guide will walk you through diagnosing and resolving this issue on Ubuntu 20.04 LTS systems.

    Symptom & Error Signature

    The most common symptom is that your PostgreSQL service fails to start, leading to applications being unable to connect to the database. When checking the service status or logs, you'll encounter FATAL errors indicating a failure to allocate shared memory.

    You can observe this by checking your PostgreSQL logs or journalctl:

    sudo systemctl status postgresql
    ● postgresql.service - PostgreSQL RDBMS
         Loaded: loaded (/lib/systemd/system/postgresql.service; enabled; vendor preset: enabled)
         Active: failed (Result: exit-code) since Mon 2023-10-27 10:00:05 UTC; 5s ago
        Process: 12345 ExecStart=/usr/lib/postgresql/12/bin/pg_ctlcluster --skip-systemctl-redirect 12 main start (code=exited, status=1/FAILURE)

    Oct 27 10:00:05 webserver systemd[1]: Starting PostgreSQL RDBMS… Oct 27 10:00:05 webserver pg_ctlcluster[12345]: FATAL: could not create shared memory segment: Cannot allocate memory Oct 27 10:00:05 webserver pg_ctlcluster[12345]: DETAIL: Failed system call was shmget(key=5432001, size=16777216000, 0x1FF). Oct 27 10:00:05 webserver pg_ctlcluster[12345]: HINT: This error usually means that PostgreSQL's request for a shared memory segment exceeded available system memory or kernel limits. Oct 27 10:00:05 webserver pg_ctlcluster[12345]: The PostgreSQL process would require 16777216000 bytes of shared memory. Oct 27 10:00:05 webserver pgctlcluster[12345]: Check for other PostgreSQL instances running. If not, try reducing the request size (e.g., by reducing sharedbuffers or max_connections), or check system limitations. Oct 27 10:00:05 webserver pg_ctlcluster[12345]: LOG: database system is shut down Oct 27 10:00:05 webserver systemd[1]: postgresql.service: Main process exited, code=exited, status=1/FAILURE Oct 27 10:00:05 webserver systemd[1]: postgresql.service: Failed with result 'exit-code'. Oct 27 10:00:05 webserver systemd[1]: Failed to start PostgreSQL RDBMS. `

    Or by examining the PostgreSQL log file directly (e.g., for PostgreSQL 12):

    sudo tail -n 50 /var/log/postgresql/postgresql-12-main.log
    2023-10-27 10:00:05.123 UTC [12345] LOG:  starting PostgreSQL 12.16 (Ubuntu 12.16-0ubuntu0.20.04.1) on x86_64-pc-linux-gnu, compiled by gcc (Ubuntu 9.4.0-1ubuntu1~20.04.2) 9.4.0, 64-bit
    2023-10-27 10:00:05.123 UTC [12345] LOG:  listening on IPv4 address "127.0.0.1", port 5432
    2023-10-27 10:00:05.123 UTC [12345] LOG:  listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
    2023-10-27 10:00:05.124 UTC [12345] FATAL:  could not create shared memory segment: Cannot allocate memory
    2023-10-27 10:00:05.124 UTC [12345] DETAIL:  Failed system call was shmget(key=5432001, size=16777216000, 0x1FF).
    2023-10-27 10:00:05.124 UTC [12345] HINT:  This error usually means that PostgreSQL's request for a shared memory segment exceeded available system memory or kernel limits.  The PostgreSQL process would require 16777216000 bytes of shared memory.  Check for other PostgreSQL instances running. If not, try reducing the request size (e.g., by reducing shared_buffers or max_connections), or check system limitations.
    2023-10-27 10:00:05.124 UTC [12345] LOG:  database system is shut down

    Root Cause Analysis

    The FATAL: could not create shared memory segment: Cannot allocate memory error, specifically with the shmget system call failure, indicates that PostgreSQL attempted to allocate a shared memory segment that either exceeded the physical memory available on the system or, more commonly, violated the Linux kernel's System V IPC (Interprocess Communication) shared memory limits.

    PostgreSQL uses a significant portion of its memory for shared_buffers, which acts as a cache for database pages. This memory is allocated from the kernel's shared memory pool. The Linux kernel has two primary parameters that govern System V shared memory:

    1. kernel.shmmax: This parameter defines the maximum size (in bytes) of a single shared memory segment that the kernel will allow. If PostgreSQL's shared_buffers (which is typically one large shared memory segment) configured value exceeds kernel.shmmax, the allocation will fail.
    2. kernel.shmall: This parameter defines the total amount of shared memory (in pages) that the system can use at once. If the sum of all shared memory segments requested by all processes (including PostgreSQL) exceeds kernel.shmall multiplied by the system's PAGESIZE, allocation will fail. On x86-64 systems, PAGESIZE is typically 4096 bytes (4KB).

    The crash typically occurs when: * You've set shared_buffers in postgresql.conf to a value too high for your system's physical RAM. * The shared_buffers value, even if reasonable for RAM, exceeds the current kernel.shmmax limit. * The overall shared memory footprint requested (primarily shared_buffers) exceeds the total allowed by kernel.shmall.

    Step-by-Step Resolution

    Follow these steps to diagnose and resolve the PostgreSQL shared buffers memory allocation crash.

    1. Verify PostgreSQL Service Status and Examine Logs

    First, confirm that the PostgreSQL service is indeed failing and check the logs for the exact error message.

    # Check service status

    View recent PostgreSQL specific logs (PostgreSQL 12) sudo journalctl -u postgresql –no-pager | grep -i "fatal"

    Alternatively, check the main PostgreSQL log file sudo tail -n 100 /var/log/postgresql/postgresql-12-main.log `

    Confirm the presence of the FATAL: could not create shared memory segment: Cannot allocate memory error. Note the size mentioned in the DETAIL line, as this is the amount PostgreSQL tried to allocate.

    2. Identify Current shared_buffers Configuration

    Locate your postgresql.conf file and check the configured shared_buffers value. For PostgreSQL 12 on Ubuntu 20.04, it's typically located at /etc/postgresql/12/main/postgresql.conf.

    # Display the shared_buffers line from your config
    grep -E '^shared_buffers' /etc/postgresql/12/main/postgresql.conf

    3. Determine Current Kernel Shared Memory Limits

    Check the current values of kernel.shmmax and kernel.shmall on your system.

    # View current kernel shared memory maximum segment size

    View current kernel shared memory total pages sudo sysctl kernel.shmall

    Get system page size (usually 4096 bytes or 4KB) getconf PAGESIZE `

    [!IMPORTANT] The HINT in the PostgreSQL error message will often provide the exact byte size that PostgreSQL attempted to allocate. This is your target value for kernel.shmmax. For kernel.shmall, you'll need to divide that byte size by your system's PAGE_SIZE (e.g., 4096).

    4. Option A: Reduce shared_buffers (Recommended First Approach)

    If your shared_buffers value is excessively high (e.g., more than 25-30% of your total system RAM on a dedicated DB server, or even less if running other services), reducing it is often the simplest and safest fix.

    1. Edit postgresql.conf:
    2. `bash
    3. sudo nano /etc/postgresql/12/main/postgresql.conf
    4. `
    5. Adjust shared_buffers:
    6. Find the shared_buffers line and set it to a more conservative value. A common recommendation is 25% of your system's total RAM. For example, if you have 8GB of RAM, 2GB would be a good starting point. If you have 4GB RAM, 1GB is appropriate.
        # Example: Reduce shared_buffers to 2GB
        shared_buffers = 2GB
        ```
        > [!WARNING]
    1. Save and Exit:
    2. Press Ctrl+X, then Y to save, and Enter to confirm the filename.
    1. Restart PostgreSQL:
    2. `bash
    3. sudo systemctl restart postgresql
    4. `
    1. Verify Status:
    2. `bash
    3. sudo systemctl status postgresql
    4. `
    5. If successful, the service should now be active (running).

    5. Option B: Increase Kernel Shared Memory Limits (If Reduction is Not Desirable)

    If you have ample physical RAM and genuinely require a larger shared_buffers value (e.g., 4GB or more on systems with 16GB+ RAM), you can increase the kernel's shared memory limits. This requires calculating appropriate shmmax and shmall values.

    1. Determine Required Values:
    2. Let's assume you want to set shared_buffers = 4GB.
    • kernel.shmmax: This should be at least the value of shared_buffers in bytes.
    • 4 GB = 4 1024 1024 * 1024 = 4294967296 bytes
    • So, kernel.shmmax = 4294967296
    • kernel.shmall: This is calculated as the desired total shared memory in bytes, divided by the system's page size, then rounded up. Assuming PAGE_SIZE = 4096 bytes:
    • 4294967296 bytes / 4096 bytes/page = 1048576 pages
    • So, kernel.shmall = 1048576
    1. Create/Edit a sysctl.d Configuration File:
    2. It's best practice to create a new .conf file in /etc/sysctl.d/ rather than modifying /etc/sysctl.conf directly.
        sudo nano /etc/sysctl.d/99-postgresql-shm.conf
    1. Add/Modify Parameters:
    2. Add the calculated values to the file.
        # PostgreSQL shared memory settings
        kernel.shmmax = 4294967296
        kernel.shmall = 1048576
    1. Save and Exit:
    2. Press Ctrl+X, then Y to save, and Enter to confirm the filename.
    1. Apply New Kernel Parameters:
    2. `bash
    3. sudo sysctl -p /etc/sysctl.d/99-postgresql-shm.conf
    4. `
    5. You should see output confirming the new values:
    6. `
    7. kernel.shmmax = 4294967296
    8. kernel.shmall = 1048576
    9. `
    10. These changes are persistent across reboots due to being in /etc/sysctl.d/.
    1. Verify New Kernel Parameters:
    2. `bash
    3. sudo sysctl kernel.shmmax kernel.shmall
    4. `
    1. Adjust shared_buffers in postgresql.conf (if not already done):
    2. If you increased kernel limits to support a larger shared_buffers value, ensure postgresql.conf reflects this.
        sudo nano /etc/postgresql/12/main/postgresql.conf
        ```
    1. Restart PostgreSQL:
    2. `bash
    3. sudo systemctl restart postgresql
    4. `
    1. Verify Status:
    2. `bash
    3. sudo systemctl status postgresql
    4. `
    5. The service should now be active (running).

    [!IMPORTANT] Always ensure that the sum of sharedbuffers and other memory-intensive PostgreSQL settings (like workmem, maintenanceworkmem, walbuffers, and memory for maxconnections) does not exceed your system's physical RAM to prevent excessive swapping and performance degradation. As a general rule for shared_buffers, start with 25% of system RAM and never exceed 50% without very specific reasons and careful monitoring.

  • Resolving PostgreSQL ‘Disk Space Exhausted’ and Database Lock Errors on macOS

    Troubleshoot and fix PostgreSQL 'pg_log disk space exhausted' errors and database lockfiles on macOS local environments, preventing database startup failures.

    Introduction

    Encountering a PostgreSQL database that refuses to start or accept connections, often accompanied by cryptic log messages pointing to disk space issues and orphaned lock files, can be a frustrating experience, especially in a local development environment. This guide addresses a common scenario on macOS where the pg_log directory consumes all available disk space, leading to PostgreSQL startup failures and database lock-related errors. We'll walk through a highly technical and accurate resolution process, ensuring your development database is back online and preventing future recurrences.

    Symptom & Error Signature

    When disk space on your macOS machine (particularly the partition where PostgreSQL stores its data and logs) becomes exhausted, PostgreSQL will fail to perform critical operations, including writing to its log files, creating temporary files, or even updating its postmaster.pid lockfile.

    You might observe the following symptoms:

    • PostgreSQL service fails to start via brew services start postgresql.
    • Applications unable to connect to the database.
    • Error messages in your terminal when attempting to start PostgreSQL or in the database's log files.

    Typical error signatures in the PostgreSQL logs (located, for example, at /usr/local/var/postgres/pg_log if installed via Homebrew) might include:

    2024-07-08 10:30:05.123 PDT [12345] FATAL:  could not write to file "pg_wal/xlog/000000010000000000000001": No space left on device
    2024-07-08 10:30:05.123 PDT [12345] HINT:  Check free disk space.
    2024-07-08 10:30:05.123 PDT [12345] LOG:  terminating any other active server processes
    2024-07-08 10:30:05.123 PDT [12345] WARNING:  could not remove old temporary file "base/pgsql_tmp/pgsql_tmp_12345.0": No space left on device
    2024-07-08 10:30:05.123 PDT [12345] LOG:  could not write to log file: No space left on device
    2024-07-08 10:30:05.123 PDT [12345] LOG:  database system is shut down
    2024-07-08 10:30:05.123 PDT [12345] FATAL:  could not create lock file "/tmp/.s.PGSQL.5432.lock": No space left on device

    Or, if a previous crash due to disk exhaustion left a stale lock file:

    2024-07-08 10:35:10.456 PDT [67890] FATAL:  lock file "postmaster.pid" already exists
    2024-07-08 10:35:10.456 PDT [67890] HINT:  Is another postmaster (PID 67889) already running in data directory "/usr/local/var/postgres"?

    Root Cause Analysis

    The primary root cause is the exhaustion of disk space, specifically on the volume hosting your PostgreSQL data directory and its pg_log subdirectory. Modern PostgreSQL installations, especially in development environments, often default to verbose logging levels. Over time, these logs can grow significantly, consuming gigabytes or even terabytes of disk space if not managed properly.

    When disk space runs out:

    1. Log File Write Failures: PostgreSQL cannot write new entries to its log files, leading to errors and potential shutdown.
    2. WAL Segment Failures: Write-Ahead Log (WAL) segments cannot be created, which is critical for transaction integrity and recovery. This directly leads to database crashes.
    3. Temporary File Failures: Operations requiring temporary disk space (e.g., large sorts, hash joins) will fail.
    4. Lock File Issues:
    5. * The postmaster.pid file, which serves as a lock to prevent multiple PostgreSQL instances from starting on the same data directory, might fail to be created or deleted correctly during a crash, leaving a stale lock.
    6. * Socket lock files (e.g., in /tmp) may also fail to be created, preventing client connections.

    On macOS, PostgreSQL is commonly installed via Homebrew. The default data directory for a Homebrew installation is typically /usr/local/var/postgres/, and the logs are usually found within pg_log inside this directory.

    Step-by-Step Resolution

    Follow these steps carefully to resolve the disk space issue, remove stale lock files, and get your PostgreSQL database back online.

    1. Verify Disk Space Usage

    First, confirm that your disk is indeed full.

    df -h

    This command will display disk space usage for all mounted file systems. Look for the percentage used in the Use% column. If your primary drive (often / or /System/Volumes/Data) is at 95% or higher, disk space is likely the culprit.

    2. Identify Large Files and PostgreSQL Log Directory

    Next, locate where the disk space is being consumed. We'll specifically check the PostgreSQL data directory. For Homebrew installations, this is commonly /usr/local/var/postgres.

    # Check overall size of PostgreSQL data directory

    Check the size of the pg_log directory du -sh /usr/local/var/postgres/pg_log

    List largest files within pg_log to identify culprits (e.g., sorted by size, largest first) find /usr/local/var/postgres/pg_log -type f -print0 | xargs -0 du -h | sort -rh | head -n 10 `

    You will likely see that pg_log accounts for a significant portion of the disk usage.

    3. Stop PostgreSQL Service

    It's crucial to stop PostgreSQL gracefully before manipulating its data or log files to prevent data corruption.

    brew services stop postgresql

    [!IMPORTANT] Ensure the service is fully stopped. You can verify this by running ps aux | grep -i postgres and checking that no PostgreSQL processes are running. If processes persist, you might need to use kill -9 <PID> for each process, though this should be a last resort.

    4. Clean Up pg_log Files

    Now that PostgreSQL is stopped, you can safely remove or archive old log files. Do not delete the pg_log directory itself, only its contents.

    [!WARNING] Before deleting, consider archiving log files to an external drive or cloud storage if you need them for auditing or debugging purposes. Deleting them permanently makes them unrecoverable.

    Option A: Archive (Recommended for forensic analysis)

    # Create a temporary backup directory

    Move all log files older than, for example, 7 days into the backup directory # Adjust '-mtime +7' to your desired retention period find /usr/local/var/postgres/pglog -name "*.log" -type f -mtime +7 -exec mv {} ~/pglog_backup/ ;

    Or, if you need to free up space immediately and have a large number of recent logs, # move ALL current log files (excluding the directory itself) to a backup. # Be careful not to move the directory itself or hidden files like .DS_Store. mv /usr/local/var/postgres/pglog/*.log ~/pglog_backup/ `

    Option B: Delete (Use with caution!)

    # Delete all log files older than, for example, 7 days
    # Adjust '-mtime +7' to your desired retention period

    Or, to delete ALL current log files (excluding the directory itself) rm /usr/local/var/postgres/pg_log/*.log `

    After cleaning, verify that disk space has been freed up:

    df -h

    5. Check and Remove Stale postmaster.pid Lockfile

    If PostgreSQL crashed, it might have left behind a postmaster.pid file in its data directory, which prevents a new instance from starting.

    # Navigate to the PostgreSQL data directory

    Check if postmaster.pid exists ls postmaster.pid

    If it exists, remove it rm postmaster.pid `

    [!IMPORTANT] Only remove postmaster.pid after you are absolutely certain no PostgreSQL process is running. Removing it while PostgreSQL is active can lead to severe data corruption.

    6. Start PostgreSQL Service

    With disk space cleared and any stale lock files removed, attempt to start PostgreSQL again.

    brew services start postgresql

    Verify its status:

    brew services list

    You should see postgresql listed as started.

    7. Verify Database Access

    Once PostgreSQL is running, test connectivity.

    psql postgres

    This should connect you to the default postgres database. Type q to exit. Your applications should now also be able to connect.

    8. Configure Log Retention and Rotation

    To prevent this issue from recurring, implement a strategy for log retention.

    For macOS Local Environments (Homebrew):

    A simple cron job can periodically clean up old logs.

    1. Open your crontab for editing:
    2. `bash
    3. crontab -e
    4. `
    1. Add a line like the following to delete logs older than 30 days (adjust -mtime +30 as needed) every day at 2 AM:
    2. `cron
    3. 0 2 find /usr/local/var/postgres/pg_log -name ".log" -type f -mtime +30 -delete > /dev/null 2>&1
    4. `

    [!NOTE] > Ensure that the user whose crontab you're editing has the necessary permissions to delete files in the pg_log directory.

    1. Save and exit (:wq in vi/vim).

    For Production Linux Environments (Ubuntu/Debian, Systemd):

    While this guide focuses on macOS, it's crucial for DevOps engineers to be aware of production-grade solutions. On Linux, logrotate is the standard tool for managing log files.

    1. Check existing configuration: PostgreSQL often ships with a logrotate configuration file.
    2. `bash
    3. cat /etc/logrotate.d/postgresql-common
    4. `
    1. Example logrotate configuration (/etc/logrotate.d/your-postgresql):
    2. `nginx
    3. /var/log/postgresql/*.log {
    4. daily
    5. rotate 7
    6. compress
    7. delaycompress
    8. missingok
    9. notifempty
    10. create 0640 postgres adm
    11. su postgres adm
    12. postrotate
    13. # Reload postgresql service to signal log rotation
    14. # This depends on your PostgreSQL version and how it handles log rotation signals.
    15. # Some versions might require pg_ctl reload or a SIGHUP to the postmaster.
    16. # For systemd-managed services, a simple reload is often sufficient.
    17. test -e /usr/share/postgresql/9.X/pg_ctlcluster &&
    18. /usr/bin/pg_ctlcluster 9.X main reload > /dev/null || true
    19. # Or for simpler setups:
    20. # /usr/bin/find /var/log/postgresql/ -type f -name "*.log" -exec kill -HUP cat /var/run/postgresql/9.X-main.pid ; || true
    21. endscript
    22. }
    23. `
    24. > [!NOTE]
    25. > Adjust paths (/var/log/postgresql), PostgreSQL version (9.X), and the postrotate command to match your specific setup. The postrotate command ensures PostgreSQL releases its old log file handle and starts writing to a new one after rotation.

    9. Monitor Disk Usage

    Regularly monitor your disk space, especially in development environments where log settings might be more verbose. Tools like df -h and du -sh are your first line of defense. Consider setting up monitoring alerts if this is a recurring issue or in a shared development server context.

  • Troubleshooting PostgreSQL Performance: Slow Queries, Locks, Index Scans, VACUUM Issues, and Dead Tuples

    Diagnose and resolve common PostgreSQL performance bottlenecks including transaction locks, inefficient index usage, VACUUM issues, and dead tuple accumulation. Optimize your database for speed and stability.

    As an experienced systems administrator, you've likely encountered the elusive and frustrating scenario where your PostgreSQL database, once a paragon of speed, begins to buckle under pressure. Applications become unresponsive, API requests timeout, and users report excruciatingly slow page loads. This guide delves into a common, multifaceted performance issue encompassing slow queries, transaction locks, inefficient index usage (especially index scans), autovacuum struggles, and the accumulation of "dead tuples" leading to table bloat. Understanding and systematically addressing these interconnected problems is crucial for restoring database health and application responsiveness.

    Symptom & Error Signature

    The symptoms are often observed at the application level before database logs reveal the underlying issues. Users might experience:

    • Application Timeouts: Web applications or services fail with HTTP 504 Gateway Timeout (Nginx), database connection timeouts, or query execution timeouts.
    • Slow Application Responses: Pages load slowly, data retrieval is sluggish.
    • High Server Load: Elevated CPU utilization, I/O wait, and memory consumption on the database server.
    • Increased Database Connection Pool Usage: More connections stay open longer, potentially exhausting the pool.

    Typical observations from monitoring and logs:

    Nginx Error Log (Example Timeout):

    2023/10/26 14:35:01 [error] 12345#12345: *67890 upstream timed out (110: Connection timed out) while reading response header from upstream, client: 192.168.1.100, server: example.com, request: "GET /api/v1/data HTTP/1.1", upstream: "http://unix:/var/run/php/php8.2-fpm.sock:/api/v1/data", host: "example.com"

    PostgreSQL Log Entries (Possible clues):

    LOG:  duration: 15432.123 ms  statement: SELECT id, data, status FROM large_table WHERE created_at < '2023-01-01' ORDER BY id DESC LIMIT 100 OFFSET 10000;
    LOG:  statement: SELECT pg_backend_pid();
    WARNING:  autovacuum: VACUUM of table "mydb.public.huge_table": index "huge_table_pkey" would wrap around

    pgstatactivity showing blocked queries:

    SELECT pid, state, backend_type, query_start, query, wait_event_type, wait_event
    FROM pg_stat_activity
    WHERE state = 'waiting' AND wait_event_type = 'Lock';

    Output might show:

      pid  |  state  |  backend_type  |         query_start         |             query             | wait_event_type | wait_event
    -------+---------+----------------+-----------------------------+-------------------------------+-----------------+------------
     12345 | waiting | client backend | 2023-10-26 14:35:05.123456+00 | UPDATE my_table SET status=1... | Lock            | tuple
     12346 | active  | client backend | 2023-10-26 14:35:00.000000+00 | UPDATE my_table SET value=... |                 |

    pgstatuser_tables indicating high dead tuples:

    SELECT relname, n_live_tup, n_dead_tup, last_autovacuum, last_autoanalyze
    FROM pg_stat_user_tables
    ORDER BY n_dead_tup DESC LIMIT 5;

    Output might show:

        relname    | n_live_tup | n_dead_tup |       last_autovacuum       |      last_autoanalyze
    ---------------+------------+------------+-----------------------------+-----------------------------
     large_data    | 15000000   | 5000000    | 2023-10-26 08:00:00.000000+00 | 2023-10-26 09:00:00.000000+00
     another_table | 200000     | 100000     | 2023-10-26 10:30:00.000000+00 | 2023-10-26 10:45:00.000000+00

    Root Cause Analysis

    The issue "PostgreSQL slow queries locks index scan vacuum dead tuples table" points to a complex interplay of factors:

    1. Slow Queries:
    2. Inefficient Query Plans: Lack of suitable indexes, outdated statistics (ANALYZE not run), or poorly written SQL (e.g., SELECT , excessive JOINs, subqueries that could be optimized).
    3. * Resource Contention: Queries waiting for locks or I/O.
    4. * Bloat: Queries need to scan more data blocks due to dead tuples.
    1. Locks:
    2. * Long-Running Transactions: Transactions that stay open for extended periods, especially UPDATE or DELETE operations on many rows, can hold exclusive or row-level locks, blocking other queries.
    3. * Application Design Flaws: Holding transactions open while performing external API calls or user interactions.
    4. * Deadlocks: Two or more transactions mutually waiting for locks held by each other. PostgreSQL typically detects and resolves these by aborting one transaction, but it's a symptom of contention.
    1. Index Scan vs. Index-Only Scan:
    2. * An Index Scan means PostgreSQL uses an index to find the row (tuple ID/TID), then has to visit the actual table page to retrieve the full row data and check its visibility (MVCC rules). This involves more disk I/O and CPU than an Index-Only Scan.
    3. * An Index-Only Scan retrieves all required data directly from the index, avoiding a trip to the table. This is much faster.
    4. Dead Tuples Impact: When a table (or its corresponding index pages) has many dead tuples, PostgreSQL's MVCC (Multi-Version Concurrency Control) mechanism has to do more work. Even if an index could* provide all the necessary columns for an Index-Only Scan, if the heap (table) pages are heavily bloated with dead tuples, PostgreSQL might choose an Index Scan instead, or the Index-Only Scan might fail to find a visible tuple version in the index without a heap fetch, falling back to a regular Index Scan or performing additional heap lookups. This is often reflected as high "rows removed by MVCC" in EXPLAIN ANALYZE output.
    5. * Missing/Inefficient Indexes: Incorrectly chosen or absent indexes force sequential scans or less optimal index scans.
    1. VACUUM Issues & Dead Tuples:
    2. * Dead Tuples: PostgreSQL's MVCC design means UPDATE and DELETE operations don't immediately remove old row versions (dead tuples). Instead, they are marked for deletion. These dead tuples consume disk space and can bloat tables and indexes.
    3. * Autovacuum Lag: The autovacuum daemon is responsible for cleaning up dead tuples and updating table statistics. If autovacuum cannot keep up with the rate of UPDATE/DELETE operations, dead tuples accumulate.
    4. * Autovacuum Configuration: Default autovacuum settings might be too conservative for high-transaction workloads. Factors like autovacuumvacuumscalefactor, autovacuumvacuumthreshold, autovacuumvacuumcostdelay, and autovacuumvacuumcost_limit can hinder autovacuum's efficiency.
    5. * Table Bloat: The physical size of tables and indexes grows beyond what is strictly necessary due to accumulated dead tuples. This leads to:
    6. * More disk I/O to read relevant data.
    7. * Reduced cache hit rates.
    8. * Slower sequential and index scans.
    9. * Increased backup sizes.
    10. * Potential for transaction ID wraparound if not addressed, leading to database shutdown.

    These issues create a vicious cycle: slow queries lead to longer-running transactions, increasing lock contention, which then exacerbates autovacuum's ability to clean up dead tuples, leading to more bloat, which in turn makes queries even slower.

    Step-by-Step Resolution

    Solving these issues requires a systematic approach, combining monitoring, configuration tuning, and query optimization.

    1. Monitor and Diagnose the Current State

    Before making any changes, accurately identify the primary bottlenecks.

    • Active and Waiting Queries:
    • `sql
    • — Top 20 active queries by duration
    • SELECT pid, datname, usename, clientaddr, applicationname, backend_start,
    • state, waiteventtype, waitevent, querystart,
    • (now() – querystart) AS queryduration, query
    • FROM pgstatactivity
    • WHERE state = 'active'
    • ORDER BY query_duration DESC
    • LIMIT 20;

    — Queries currently waiting for locks SELECT pa.pid AS blocked_pid, pa.query AS blocked_query, pa.state AS blocked_state, pa.querystart AS blockedquery_start, pl.locktype, pl.mode, pb.pid AS blocking_pid, pb.query AS blocking_query, pb.state AS blocking_state, pb.querystart AS blockingquery_start FROM pgcatalog.pglocks pl JOIN pgcatalog.pgstat_activity pa ON pl.pid = pa.pid LEFT JOIN pgcatalog.pgstat_activity pb ON pl.granted = false AND pl.pid = pb.pid WHERE pa.waiteventtype = 'Lock' AND pa.state = 'waiting'; `

    • Identify Bloat and Autovacuum Status:
    • `sql
    • — Top 10 tables by dead tuples
    • SELECT
    • relname,
    • pgsizepretty(pgrelationsize(relid)) AS table_size,
    • nlivetup,
    • ndeadtup,
    • CASE
    • WHEN nlivetup > 0 THEN round(ndeadtup::numeric / nlivetup * 100)
    • ELSE 0
    • END AS deadtupratio_pct,
    • last_autovacuum,
    • last_autoanalyze
    • FROM pgstatuser_tables
    • ORDER BY ndeadtup DESC
    • LIMIT 10;

    — Check autovacuum activity SELECT pid, age(backendxid) AS xidage, currentsetting('autovacuumfreezemaxage') AS freezemaxage, query, state, query_start FROM pgstatactivity WHERE backend_type = 'autovacuum worker'; `

    • Enable pgstatstatements: This extension tracks execution statistics of all queries executed by a server.
    • `sql
    • — On Ubuntu/Debian, edit postgresql.conf:
    • sudo vim /etc/postgresql/$(pgversionnum)/main/postgresql.conf
    • # Add/uncomment:
    • sharedpreloadlibraries = 'pgstatstatements'
    • pgstatstatements.max = 10000 # Increase if you have many unique queries
    • pgstatstatements.track = all # track all statements
    • `
    • > [!IMPORTANT]
    • > Changes to sharedpreloadlibraries require a PostgreSQL service restart.
    • `bash
    • sudo systemctl restart postgresql.service
    • `
    • Then, connect to your database and enable the extension:
    • `sql
    • CREATE EXTENSION IF NOT EXISTS pgstatstatements;

    — Top 10 slowest queries by total time SELECT query, calls, totalexectime, meanexectime, (totalexectime / calls) AS avgexectime, rows, blocks_hit, blocks_read FROM pgstatstatements ORDER BY totalexectime DESC LIMIT 10; `

    2. Optimize Slow Queries and Indexing

    Focus on the queries identified in step 1.

    • Analyze Query Plans: Use EXPLAIN ANALYZE to understand why a query is slow. Pay attention to:
    • * rows removed by MVCC: Indicates dead tuples impacting visibility checks, hinting at VACUUM issues.
    • * Index Scan vs Index Only Scan: If Index Scan is used and an Index Only Scan is possible (all columns are in the index), it suggests bloat or visibility map issues.
    • * High cost values for certain operations (e.g., sequential scan on a large table).
    • `sql
    • EXPLAIN (ANALYZE, BUFFERS, FORMAT YAML)
    • SELECT id, data FROM largetable WHERE status = 'active' AND createdat > '2023-10-01' ORDER BY id LIMIT 100;
    • `
    • Look for Seq Scan on large tables where an index could be used, or Index Scan performing many heap fetches.
    • Create or Refine Indexes: Based on EXPLAIN ANALYZE, add indexes that cover the WHERE, ORDER BY, and JOIN clauses. For Index Only Scan potential, consider covering indexes (PostgreSQL 11+).
    • `sql
    • — Example for a query using status and created_at
    • CREATE INDEX CONCURRENTLY idxlargetablestatuscreated_at
    • ON largetable (status, createdat);

    — Example for a covering index (if 'data' is also needed frequently) CREATE INDEX CONCURRENTLY idxlargetablestatuscreatedatdata ON largetable (status, createdat) INCLUDE (data); ` > [!IMPORTANT] > Always use CREATE INDEX CONCURRENTLY for production environments to avoid exclusive locks that block DML operations. It takes longer but allows concurrent work.

    • Rewrite Inefficient Queries:
    • Avoid SELECT where only a few columns are needed.
    • * Break down complex queries into simpler ones if possible.
    • * Review JOIN conditions.
    • * Ensure appropriate use of LIMIT/OFFSET (large offsets can be slow). Consider cursor-based pagination for very large datasets.

    3. Address Lock Contention

    • Identify Blocking Sessions:
    • `sql
    • SELECT
    • blockinglocks.pid AS blockingpid,
    • blockingactivity.usename AS blockinguser,
    • blockingactivity.applicationname AS blocking_app,
    • blockingactivity.query AS blockingquery,
    • blockedlocks.pid AS blockedpid,
    • blockedactivity.usename AS blockeduser,
    • blockedactivity.applicationname AS blocked_app,
    • blockedactivity.query AS blockedquery,
    • blockedactivity.waitevent_type,
    • blockedactivity.waitevent
    • FROM pgcatalog.pglocks AS blocked_locks
    • JOIN pgcatalog.pgstatactivity AS blockedactivity
    • ON blockedactivity.pid = blockedlocks.pid
    • JOIN pgcatalog.pglocks AS blocking_locks
    • ON blockinglocks.locktype = blockedlocks.locktype
    • AND blockinglocks.database IS NOT DISTINCT FROM blockedlocks.database
    • AND blockinglocks.relation IS NOT DISTINCT FROM blockedlocks.relation
    • AND blockinglocks.page IS NOT DISTINCT FROM blockedlocks.page
    • AND blockinglocks.tuple IS NOT DISTINCT FROM blockedlocks.tuple
    • AND blockinglocks.classid IS NOT DISTINCT FROM blockedlocks.classid
    • AND blockinglocks.objid IS NOT DISTINCT FROM blockedlocks.objid
    • AND blockinglocks.objsubid IS NOT DISTINCT FROM blockedlocks.objsubid
    • AND blockinglocks.pid != blockedlocks.pid
    • JOIN pgcatalog.pgstatactivity AS blockingactivity
    • ON blockingactivity.pid = blockinglocks.pid
    • WHERE NOT blocked_locks.granted;
    • `
    • Review Application Transaction Logic:
    • * Ensure transactions are as short-lived as possible. Commit frequently.
    • Avoid performing external I/O (API calls, file system operations) within* an open transaction.
    • * Use explicit BEGIN; ... COMMIT; or ROLLBACK; rather than relying solely on auto-commit for critical operations.
    • Set Statement and Lock Timeouts: Prevent indefinitely running queries or locks.
    • `sql
    • — In postgresql.conf
    • statement_timeout = '60s' # Terminate any statement that takes longer than 60s
    • lock_timeout = '5s' # Abort if a lock cannot be acquired within 5s
    • `
    • These can also be set per session or per transaction.
    • Consider Connection Pooling: Tools like PgBouncer can manage connections efficiently, reducing overhead and improving contention handling by allowing applications to quickly get a fresh connection for each transaction.
    • Kill Blocking Sessions (Last Resort): If a session is irreversibly stuck and causing severe blocking, you may need to terminate it.
    • `sql
    • SELECT pgterminatebackend(pid);
    • `
    • > [!WARNING]
    • > Killing backend processes can lead to incomplete transactions and data inconsistencies if not handled carefully by the application. Use with extreme caution and understanding of the implications.

    4. Tune Autovacuum and Manage Dead Tuples

    Effective autovacuum configuration is paramount for preventing bloat and maintaining performance.

    • PostgreSQL Configuration (postgresql.conf):
    • `ini
    • # Ensure autovacuum is enabled
    • autovacuum = on
    • logautovacuummin_duration = 0 # Log all autovacuum actions for debugging (set to -1 for production once tuned)

    Number of autovacuum worker processes autovacuummaxworkers = 3 # Default is 3, increase for busy systems (e.g., 5-8)

    Delay between autovacuum runs (lower for faster cleanup) autovacuumnaptime = 1min # How often to check for work

    Autovacuum cost-based delay (lower for faster, more aggressive vacuum) autovacuumvacuumcost_delay = 10ms # Default 2ms (PostgreSQL 16), 10ms (older). Lower means more aggressive I/O. autovacuumanalyzecost_delay = 10ms

    Cost limit (higher for more work per vacuum, but more I/O) autovacuumvacuumcost_limit = 2000 # Default 200. Increase for more powerful I/O systems (e.g., 500-1000 or higher)

    Autovacuum thresholds (adjust these carefully) # autovacuumvacuumscale_factor = 0.2 # Default. Percentage of table size for VACUUM trigger. # autovacuumanalyzescale_factor = 0.1 # Default. Percentage of table size for ANALYZE trigger. # autovacuumvacuumthreshold = 50 # Default. Min tuples for VACUUM trigger. # autovacuumanalyzethreshold = 50 # Default. Min tuples for ANALYZE trigger. ` > [!IMPORTANT] > Restart PostgreSQL service after changing postgresql.conf. `bash sudo systemctl restart postgresql.service `

    • Per-Table Autovacuum Settings: For highly volatile tables, you can override global settings.
    • `sql
    • ALTER TABLE large_data SET (
    • autovacuumvacuumscale_factor = 0.05, — Trigger vacuum sooner (5% dead tuples)
    • autovacuumvacuumthreshold = 1000, — Minimum 1000 dead tuples
    • autovacuumanalyzescale_factor = 0.02, — Analyze sooner (2% changes)
    • autovacuumanalyzethreshold = 500
    • );
    • `
    • Manual VACUUM/ANALYZE: If autovacuum lags significantly, a manual run might be necessary.
    • `sql
    • — To clean up dead tuples and update statistics (non-blocking)
    • VACUUM (ANALYZE, VERBOSE) large_data;
    • — To re-scan all indexes and rebuild visibility map (can be slow but non-blocking)
    • VACUUM (FULL, ANALYZE, VERBOSE) largedata; — Careful: VACUUM FULL IS blocking. Use pgrepack instead for large tables.
    • `
    • REINDEX Bloated Indexes: Indexes can also get bloated.
    • `sql
    • — Rebuild a specific index concurrently (non-blocking)
    • REINDEX INDEX CONCURRENTLY idxlargetablestatuscreated_at;

    — Rebuild all indexes for a table concurrently (non-blocking) REINDEX TABLE CONCURRENTLY large_data; ` > [!IMPORTANT] > REINDEX CONCURRENTLY is the preferred method as it avoids exclusive locks.

    • Address Table Bloat with pgrepack: For severe table bloat, VACUUM FULL requires an exclusive lock and downtime. pgrepack is a powerful tool that can perform online defragmentation without exclusive locks.
    • 1. Install pg_repack:
    • `bash
    • # First, find your PostgreSQL major version (e.g., 16)
    • pgversionnum=$(psql -V | grep -oE '[0-9]+.[0-9]+' | head -1 | cut -d'.' -f1)
    • sudo apt update
    • sudo apt install postgresql-$pgversionnum-repack
    • `
    • 2. Enable extension in your database:
    • `sql
    • CREATE EXTENSION IF NOT EXISTS pg_repack;
    • `
    • 3. Run pg_repack:
    • `bash
    • # To repack a specific table
    • pgrepack -d yourdatabasename -t yourtable_name
    • # To repack all tables in a database
    • pgrepack -d yourdatabase_name
    • # To repack specific index
    • pgrepack -d yourdatabasename -i yourindex_name
    • `
    • > [!WARNING]
    • > pg_repack creates a copy of the table, requires sufficient disk space (typically 2x the table size temporarily), and can add I/O load during the operation. Monitor carefully.

    5. Database & System-Level Configuration

    Review overall PostgreSQL and OS settings.

    • shared_buffers: Controls how much memory PostgreSQL uses for caching data pages. A common recommendation is 25% of total system RAM, but can go up to 40% on dedicated DB servers.
    • `ini
    • # In postgresql.conf
    • shared_buffers = 4GB # Example for a 16GB RAM server
    • `
    • work_mem: Amount of memory used by internal sort operations and hash tables before writing to temporary disk files. Set this carefully, as it's per operation per session.
    • `ini
    • # In postgresql.conf
    • work_mem = 64MB # Adjust based on your workload, common values 16MB-256MB
    • `
    • maintenanceworkmem: Memory dedicated to maintenance operations like VACUUM, REINDEX, CREATE INDEX. Set this higher than work_mem for faster maintenance.
    • `ini
    • # In postgresql.conf
    • maintenanceworkmem = 1GB # Example for larger databases
    • `
    • effectivecachesize: The planner's estimate of the total amount of memory available for disk caching by the operating system and within PostgreSQL itself. Helps in making good planning decisions.
    • `ini
    • # In postgresql.conf
    • effectivecachesize = 12GB # Example for a 16GB RAM server with 4GB shared_buffers
    • `
    • WAL Settings:
    • `ini
    • # In postgresql.conf
    • wal_buffers = 16MB # Default 16MB, increase if needed for heavy writes
    • maxwalsize = 4GB # Controls how often checkpoints occur
    • checkpoint_timeout = 30min # Controls how often checkpoints occur
    • `
    • Operating System Tuning (Ubuntu/Debian):
    • * Swappiness: Set vm.swappiness to a low value (e.g., 1-10) to minimize swapping database pages to disk.
    • `bash
    • sudo sysctl vm.swappiness=10
    • echo 'vm.swappiness = 10' | sudo tee -a /etc/sysctl.conf
    • `
    • * Huge Pages: Can improve performance by reducing TLB misses for large memory allocations. Configuration is more complex and depends on your kernel and PostgreSQL version.
    • * Disk I/O: Ensure your storage system (SSD, appropriate RAID levels) provides adequate read/write performance for your workload.

    6. Application-Level Optimizations

    Beyond database configurations, application code plays a vital role.

    • Batch Operations: Instead of single-row INSERTs/UPDATEs in a loop, use INSERT ... VALUES (...), (...), ...; or UPDATE ... WHERE id IN (...); for multiple rows.
    • Caching: Implement application-level caching (e.g., Redis, Memcached) for frequently accessed, slow-changing data.
    • Read Replicas: For read-heavy workloads, consider offloading reads to a PostgreSQL read replica.
    • Asynchronous Processing: Use message queues or background jobs for non-critical, long-running operations that don't need immediate results.
  • Troubleshooting: PostgreSQL Connection Limit Reached, Database Pool Full, & pg_hba Issues

    Resolve 'PostgreSQL connection limit reached' errors by optimizing `max_connections`, implementing pooling, and reviewing `pg_hba.conf`.

    Introduction

    As an experienced Systems Administrator, few errors are as frustrating and impactful as your application failing to connect to its database. The "PostgreSQL connection limit reached" error, often accompanied by "database pool full," indicates that your PostgreSQL server has hit its maximum allowed concurrent connections. This critical state prevents new database connections, effectively rendering your application—or parts of it—unresponsive or completely offline. While the primary culprit is typically the maxconnections setting, issues can be compounded by inefficient application connection handling, long-running queries, or even subtly related pghba.conf misconfigurations causing connection storm retries. This guide will walk you through a highly technical, step-by-step resolution process to diagnose, mitigate, and prevent this issue.

    Symptom & Error Signature

    When the PostgreSQL connection limit is reached, users typically experience slow application response times, failed requests, or "Service Unavailable" messages. On the backend, you'll observe errors in your application logs and PostgreSQL server logs.

    Typical Application Log Errors:

    ActiveRecord::ConnectionTimeoutError: could not obtain a connection from the pool within 5.000 seconds (waited 5.000 seconds)
    SQLSTATE[08006] [7] FATAL: remaining connection slots are reserved for non-replication superuser connections
    Error: connect ETIMEDOUT 127.0.0.1:5432

    PostgreSQL Server Log Errors (e.g., /var/log/postgresql/postgresql-14-main.log):

    FATAL:  remaining connection slots are reserved for non-replication superuser connections
    LOG:  could not accept new connection: Too many open files
    FATAL:  sorry, too many clients already

    Root Cause Analysis

    The "connection limit reached" error stems from the PostgreSQL server refusing new connections because it has reached its configured max_connections limit. The underlying reasons are multifaceted:

    1. Insufficient maxconnections: The most direct cause is that the maxconnections parameter in postgresql.conf is set too low for the current workload, number of application processes, or expected concurrent users.
    2. Application Connection Leaks: The application code is not properly closing database connections, leading to an accumulation of idle-in-transaction or active connections that are no longer needed but remain open, consuming slots.
    3. Inefficient Application-Level Connection Pooling: The application's internal connection pool is misconfigured, too small, or not being effectively utilized, causing it to rapidly request new connections instead of reusing existing ones.
    4. Lack of External Connection Pooling (e.g., PgBouncer): Without an external pooler, each application process or worker maintains its own set of connections, leading to an explosion of database connections as application worker count scales.
    5. Long-Running or Inefficient Queries: Poorly optimized queries or missing indexes can cause transactions to take an excessively long time, holding open connections for extended periods and preventing their release.
    6. Traffic Spikes or DoS Attacks: Sudden, massive increases in user traffic or malicious attempts can overwhelm the database with connection requests.
    7. Resource Contention: While not a direct cause, insufficient server resources (CPU, RAM, I/O) can slow down query processing, causing connections to remain open longer and effectively reducing the effective number of concurrent connections the server can handle before hitting the limit.
    8. pghba.conf Interaction (Indirect): While pghba.conf controls authentication and authorization, a misconfiguration here can indirectly exacerbate the issue. For example, if an application continuously tries to connect with invalid credentials due to a pghba.conf rule denying access, its aggressive retry logic might rapidly consume available connection slots if the maxconnections is already high enough to allow these failed attempts but the application doesn't back off. It's less common but worth checking alongside postgresql.conf.

    Step-by-Step Resolution

    Addressing this issue requires a systematic approach, starting with immediate mitigation and moving towards long-term architectural solutions.

    1. Immediate Mitigation: Identify & Terminate Zombie Connections

    The first step is to free up connection slots to bring your application back online.

    1. Connect to PostgreSQL as a Superuser:
    2. If possible, connect using the postgres superuser or another role configured with the NOSUPERUSER attribute, as maxconnections - superuserreserved_connections slots are always reserved for superusers.
        psql -U postgres -h localhost
        ```
    1. Identify Active Connections:
    2. Once connected, query pgstatactivity to see all active connections. Look for state='idle in transaction' or unusually long-running queries (state='active').
        SELECT pid, datname, usename, client_addr, application_name, backend_start, state, state_change, query_start, query
        FROM pg_stat_activity
        WHERE datname = 'your_database_name'
        ORDER BY query_start ASC;
        ```
    1. Terminate Problematic Connections:
    2. Carefully identify and terminate idle or rogue connections. Avoid terminating critical system processes.
        SELECT pg_terminate_backend(pid) FROM pg_stat_activity
        WHERE datname = 'your_database_name' AND state IN ('idle in transaction', 'active') AND pid <> pg_backend_pid()
        AND query_start < NOW() - INTERVAL '5 minutes'; -- Example: terminate queries older than 5 minutes

    [!WARNING] > Terminating backend processes can disrupt ongoing operations. Ensure you understand which connections you are terminating. Prioritize idle in transaction first, then long-running active queries.

    2. Adjust max_connections in postgresql.conf

    Increasing max_connections is a common first reaction, but it should be done carefully, considering available system resources.

    1. Locate postgresql.conf:
    2. The location varies, but common paths on Ubuntu are /etc/postgresql/<version>/main/postgresql.conf.
        sudo find / -name postgresql.conf 2>/dev/null
        # Example output: /etc/postgresql/14/main/postgresql.conf
    1. Edit postgresql.conf:
    2. Open the file with a text editor.
        sudo nano /etc/postgresql/14/main/postgresql.conf

    Find the max_connections parameter. Its default is often 100.

        # Connections
        max_connections = 100		# (change requires restart)
        #superuser_reserved_connections = 3	# (change requires restart)
    1. Determine an Appropriate Value:
    2. * Rule of Thumb: Start by considering your application's connection pool size per instance, multiplied by the number of application instances/workers. Add a buffer for administrative tasks and other services.
    3. * Resource Considerations: Each connection consumes RAM (e.g., workmem, maintenanceworkmem, internal buffers). A high maxconnections on a server with limited RAM can lead to excessive swapping and performance degradation.
    4. * Estimate RAM usage per connection: Typically 1-5MB, but can be higher.
    5. Total RAM (GB) 0.75 / (estimated RAM per connection) can give you a rough upper bound.
    6. * Start Incrementally: If you have 16GB RAM, and your application uses 20 connections, you might start with max_connections = 200-300 and monitor. Avoid excessively large values like 1000+ unless you have substantial RAM (64GB+) and a robust connection pooler.
        max_connections = 250   # Example: Adjust based on your server's resources and application needs

    [!IMPORTANT] > Increasing max_connections requires a PostgreSQL restart, not just a reload.

    1. Restart PostgreSQL:
        sudo systemctl restart postgresql
        sudo systemctl status postgresql

    3. Implement or Optimize Application-Level Connection Pooling

    Efficient connection pooling is paramount for high-traffic applications.

    1. Application Framework Pooling:
    2. Most modern application frameworks (Rails, Django, Node.js ORMs) have built-in connection pooling. Ensure it's configured correctly.
    • Ruby on Rails (config/database.yml):
            production:
              adapter: postgresql
              encoding: unicode
              pool: 25 # Number of connections per application process
              database: your_database_name
              username: your_db_user
              password: <%= ENV['DATABASE_PASSWORD'] %>
              host: localhost
              port: 5432
            ```
    • Node.js (pg module):
            const { Pool } = require('pg');
            const pool = new Pool({
              user: 'your_db_user',
              host: 'localhost',
              database: 'your_database_name',
              password: 'your_db_password',
              port: 5432,
              max: 20, // Max number of clients in the pool
              idleTimeoutMillis: 30000, // How long a client is allowed to remain idle before being closed
              connectionTimeoutMillis: 2000, // How long to wait for a connection to be established
            });
    1. External Connection Pooling (PgBouncer):
    2. For microservice architectures or applications with many worker processes, an external connection pooler like PgBouncer is highly recommended. It acts as a proxy, multiplexing many client connections into a smaller, fixed number of server connections.
    1. Install PgBouncer:
            sudo apt update
            sudo apt install pgbouncer
    1. Configure PgBouncer (/etc/pgbouncer/pgbouncer.ini):
            ; /etc/pgbouncer/pgbouncer.ini
            [databases]

    [pgbouncer] listen_addr = 0.0.0.0 ; Or specific IP like 127.0.0.1 if only local app connects listen_port = 6432 ; New port for applications to connect to auth_type = md5 auth_file = /etc/pgbouncer/userlist.txt pool_mode = session ; transaction or statement for finer control defaultpoolsize = 20 ; Max client connections to the PostgreSQL server maxclientconn = 1000 ; Max client connections to PgBouncer ; … other settings `

    1. Configure userlist.txt (/etc/pgbouncer/userlist.txt):
            "your_db_user" "your_db_password"
            "postgres" "postgres_password"
            ```
    1. Adjust Application to Connect to PgBouncer:
    2. Update your application's database connection string to point to PgBouncer's listenport (e.g., 6432) and listenaddr.
    1. Restart PgBouncer:
            sudo systemctl restart pgbouncer
            sudo systemctl status pgbouncer

    [!IMPORTANT] > When using PgBouncer, applications connect to PgBouncer's port, and PgBouncer then connects to PostgreSQL. This means you effectively set maxconnections on PostgreSQL to match defaultpool_size in PgBouncer, plus reserved superuser connections. Your application's pool size can then be set much higher, as it's pooling connections to PgBouncer, not PostgreSQL directly.

    4. Optimize Queries and Indexes

    Slow queries keep connections open longer, contributing to connection exhaustion.

    1. Identify Slow Queries:
    2. Use pgstatstatements (ensure sharedpreloadlibraries = 'pgstatstatements' in postgresql.conf and CREATE EXTENSION pgstatstatements; in your database).
        SELECT query, calls, total_exec_time, mean_exec_time
        FROM pg_stat_statements
        ORDER BY total_exec_time DESC
        LIMIT 10;

    Also check pgstatactivity for current long-running queries.

    1. Analyze and Optimize:
    2. Use EXPLAIN ANALYZE to understand query execution plans.
        EXPLAIN ANALYZE SELECT * FROM users WHERE email = '[email protected]';
        ```
    1. Create Missing Indexes:
    2. Based on EXPLAIN ANALYZE output, create indexes on frequently queried columns.
        CREATE INDEX idx_users_email ON users (email);

    [!WARNING] > Creating indexes on large tables can lock the table. Use CREATE INDEX CONCURRENTLY for production environments to avoid downtime.

        CREATE INDEX CONCURRENTLY idx_users_email ON users (email);

    5. Review pg_hba.conf for Authentication Issues

    While not a direct cause of maxconnections being reached, a misconfigured pghba.conf can lead to repeated, failed connection attempts that consume resources or mask the true connection issue.

    1. Locate pg_hba.conf:
    2. Typically in the same directory as postgresql.conf, e.g., /etc/postgresql/14/main/pg_hba.conf.
        sudo nano /etc/postgresql/14/main/pg_hba.conf
    1. Verify Rules:
    2. Ensure that the pg_hba.conf entries for your application's database user and IP address are correct and use the expected authentication method (e.g., md5, scram-sha-256).

    Local connections: local all all peer # IPv4 local connections: host all all 127.0.0.1/32 scram-sha-256 # IPv6 local connections: host all all ::1/128 scram-sha-256 # Allow connections from application server on a specific subnet host yourdatabasename yourdbuser 192.168.1.0/24 scram-sha-256 ` An auth_type mismatch or incorrect IP/user combination can lead to connection refusals. If applications retry these failures aggressively, it can look like connection exhaustion.

    1. Reload PostgreSQL for pg_hba.conf Changes:
    2. Unlike postgresql.conf, most pg_hba.conf changes only require a reload.
        sudo systemctl reload postgresql

    6. Monitor System Resources

    Ensure your database server has adequate CPU, RAM, and I/O capacity. Resource bottlenecks can cause queries to run slowly, exacerbating connection issues.

    1. Monitor CPU & Load:
    2. `bash
    3. htop
    4. uptime
    5. `
    6. Monitor Memory:
    7. `bash
    8. free -h
    9. `
    10. Look for high swap usage, indicating RAM exhaustion.
    11. Monitor Disk I/O:
    12. `bash
    13. iotop
    14. `
    15. Identify if disk operations are a bottleneck.
    16. PostgreSQL Specific Metrics:
    17. Use tools like pgactivity or integrate with monitoring systems (Prometheus + Grafana with nodeexporter and postgres_exporter) to track connection count, active queries, cache hit ratios, etc.

    7. Consider Database Scaling

    If consistent resource pressure or connection limits persist despite optimization, it may be time to scale your database.

    1. Vertical Scaling: Upgrade the database server with more CPU, RAM, and faster storage. This is often the simplest first step for moderate growth.
    2. Horizontal Scaling (Read Replicas): Offload read-heavy queries to read replicas. This distributes the read workload, reducing the connection burden on the primary server.
    3. Sharding/Partitioning: For extremely large datasets or high write loads, consider sharding your database, though this significantly increases application complexity.

    By diligently following these steps, you can effectively troubleshoot, resolve, and prevent PostgreSQL connection limit issues, ensuring the stability and performance of your web hosting environment.

  • Troubleshooting PostgreSQL ‘Connection to server on socket pg_hba.conf failed’ Error

    Resolve the common PostgreSQL 'connection to server on socket pg_hba.conf failed' error. This guide covers server status, UNIX socket paths, and critical pg_hba.conf authentication issues for local connections.

    This comprehensive guide delves into resolving the "PostgreSQL connection to server on socket pghba.conf failed" error, a common roadblock for developers and system administrators. This specific error indicates that while the PostgreSQL server might be running and its UNIX domain socket is accessible, the server's authentication rules, defined in pghba.conf, are preventing the connection from being established. Understanding and correctly configuring pg_hba.conf for local socket connections is crucial for securing and ensuring the operability of your PostgreSQL instances.

    Symptom & Error Signature

    Users typically encounter this error when attempting to connect to a local PostgreSQL database via the psql command-line client or when an application (e.g., a web application, background worker) tries to establish a connection. The exact error message can vary slightly but will prominently feature the socket path and a rejection related to pg_hba.conf.

    Typical psql output:

    psql: error: connection to server on socket "/var/run/postgresql/.s.PGSQL.5432" failed: FATAL:  pg_hba.conf rejects connection for host "local", user "youruser", database "yourdb"

    Application Log Output Example (e.g., a Python application using psycopg2):

    2026-06-27 10:30:05,123 ERROR [app.py] Database connection failed:
    psycopg2.OperationalError: connection to server on socket "/var/run/postgresql/.s.PGSQL.5432" failed: FATAL:  pg_hba.conf rejects connection for host "local", user "web_app_user", database "webapp_db"

    This error explicitly indicates that the connection attempt reached the PostgreSQL server via the specified UNIX socket, but the server subsequently denied access based on its host-based authentication rules.

    Root Cause Analysis

    The "PostgreSQL connection to server on socket pghba.conf failed" error primarily stems from misconfigurations within the pghba.conf file for local UNIX domain socket connections. However, related issues like the server not running or incorrect socket paths can sometimes manifest similarly or be prerequisites for troubleshooting.

    Here's a breakdown of the underlying reasons:

    1. Misconfigured pg_hba.conf for local connections:
    2. * Incorrect TYPE: The rule intended for the connection might be missing a local entry, or an inappropriate host entry is being relied upon for socket connections. local type rules specifically govern connections made via UNIX domain sockets.
    3. * Incorrect DATABASE or USER: The rule might specify a database or user name that does not match the one being used by the connecting client. For example, a rule for database="mydb", user="myuser" will reject connections from database="otherdb", user="myuser".
    4. * Inappropriate METHOD: The authentication method specified (peer, md5, trust, ident, etc.) does not match how the client is attempting to authenticate or is not correctly configured on the system.
    5. * peer requires the connecting operating system user to have the same name as the PostgreSQL database user.
    6. * md5 requires a password to be supplied and for the database user to have a password set.
    7. * trust allows anyone to connect without a password and is generally insecure for production.
    8. * ident requires an ident server to be running (less common now).
    9. * Rule Order: pg_hba.conf rules are processed in order. A broader, less restrictive rule placed before a more specific, restrictive rule can inadvertently cause rejections or unintended access.
    1. PostgreSQL Service Not Running: While the pghba.conf part of the error suggests the server is running (otherwise, it couldn't evaluate pghba.conf), it's a fundamental prerequisite for any connection. If the server isn't running, the error would typically be "Connection refused" or "No such file or directory" for the socket. However, it's always the first diagnostic step.
    1. Incorrect UNIX Socket Path: PostgreSQL listens on a specific UNIX domain socket path (default: /var/run/postgresql/.s.PGSQL.5432 on Debian/Ubuntu). If the client application is configured to connect to a different path, or if the server itself is configured to use a non-default path, the connection will fail. This usually results in a "No such file or directory" error for the socket file, but an indirect pg_hba.conf failure can occur if a proxy or wrapper is involved.

    Step-by-Step Resolution

    Follow these steps to diagnose and resolve the "PostgreSQL connection to server on socket pg_hba.conf failed" error.

    1. Verify PostgreSQL Service Status

    Ensure the PostgreSQL service is actively running on your system. This is a critical first step for any connection issue.

    sudo systemctl status postgresql

    Expected Output (running):

    ● postgresql.service - PostgreSQL RDBMS
         Loaded: loaded (/lib/systemd/system/postgresql.service; enabled; vendor preset: enabled)
         Active: active (exited) since Fri 2026-06-27 09:00:00 UTC; 1h ago
        Process: 1234 ExecStart=/bin/true (code=exited, status=0/SUCCESS)
       Main PID: 1234 (code=exited, status=0/SUCCESS)
          Tasks: 0 (limit: 4616)
         Memory: 0B

    … (details about individual cluster processes) ` If the status is inactive or failed, start the service and enable it to start on boot:

    sudo systemctl start postgresql
    sudo systemctl enable postgresql

    [!IMPORTANT] The postgresql.service unit on Debian/Ubuntu is often a wrapper that manages multiple PostgreSQL cluster versions. The active (exited) status is normal for this wrapper, as it delegates to individual cluster services like [email protected]. To check a specific version's status, use sudo systemctl status postgresql@<version>-<cluster_name>. E.g., sudo systemctl status postgresql@14-main.

    2. Locate pghba.conf and unixsocket_directories

    Before making changes, you need to know the exact location of your pg_hba.conf file and which directories PostgreSQL is configured to use for its UNIX domain sockets.

    Connect as the postgres superuser (or any user with appropriate permissions) and query the database:

    sudo -u postgres psql -c 'SHOW hba_file;'
    sudo -u postgres psql -c 'SHOW config_file;'
    sudo -u postgres psql -c 'SHOW unix_socket_directories;'

    Example Output:

              hba_file
    ----------------------------
     /etc/postgresql/14/main/pg_hba.conf

    config_file ——————————- /etc/postgresql/14/main/postgresql.conf (1 row)

    unixsocketdirectories ————————- /var/run/postgresql (1 row) ` Make a note of these paths. The hbafile output tells you exactly which pghba.conf to edit. The unixsocketdirectories output confirms where the server expects its socket.

    3. Inspect and Edit pg_hba.conf for Local Socket Rules

    Open the pg_hba.conf file using your preferred text editor (e.g., nano, vi). Remember to use sudo for elevated permissions.

    sudo nano /etc/postgresql/14/main/pg_hba.conf # Adjust path based on your 'SHOW hba_file;' output

    Focus on lines that start with local. These lines define authentication rules for UNIX domain socket connections.

    Common scenarios and how to configure pg_hba.conf:

    • Connecting as the OS user postgres to any database:
    • This is typically the default and allows sudo -u postgres psql to work.
    • `conf
    • # TYPE DATABASE USER ADDRESS METHOD
    • local all postgres peer
    • `
    • The peer method authenticates by checking if the operating system user matches the requested database user. If your OS user is postgres and you're connecting as DB user postgres, this works.
    • Connecting as an OS user (e.g., webuser) to a database (e.g., myapp_db) as the same DB user (webuser):
    • This is common for application users where the OS user running the app matches the DB user.
    • `conf
    • # TYPE DATABASE USER ADDRESS METHOD
    • local myapp_db webuser peer
    • `
    • This rule allows the OS user webuser to connect to myapp_db as DB user webuser without a password.
    • Connecting as any OS user to any database using md5 password authentication:
    • This is a flexible option, requiring the client to provide a password.
    • `conf
    • # TYPE DATABASE USER ADDRESS METHOD
    • local all all md5
    • `
    • With this rule, any local connection (via socket) will require a password. Ensure your PostgreSQL users have passwords set (e.g., ALTER USER webuser WITH PASSWORD 'yoursecurepassword';).
    • Connecting as a specific DB user (e.g., dashboard_user) from any local OS user using md5:
    • `conf
    • # TYPE DATABASE USER ADDRESS METHOD
    • local dashboarddb dashboarduser md5
    • `

    Important considerations when editing pg_hba.conf:

    • Order of Rules: Rules are evaluated from top to bottom. The first rule that matches the connection parameters (TYPE, DATABASE, USER, ADDRESS) is used. Place more specific rules before more general rules. For example, a rule allowing webuser access to myapp_db should come before a local all all peer rule if you want different authentication for webuser.
    • ADDRESS column for local connections: The ADDRESS column is ignored for local (UNIX domain socket) connections; leave it blank.
    • TYPE values:
    • * local: Matches connections using UNIX domain sockets.
    • host: Matches connections using TCP/IP. (Not relevant for this* error, but good to know).
    • METHOD values:
    • * peer: Authenticates by obtaining the client's operating system user name and checking if it matches the requested database user name. No password. Very secure for local system users.
    • * md5: Requires the client to supply an MD5-hashed password for authentication.
    • * trust: Allows anyone to connect without a password if the connection parameters match.
    • * ident: Authenticates by contacting an Ident server on the client to get the client's OS user name.

    [!WARNING] Avoid using trust for production environments unless absolutely necessary and with a full understanding of the security implications. It grants unrestricted access to anyone who can connect locally.

    Example pg_hba.conf snippet (modified):

    Local connections (UNIX domain socket) # Allow OS user 'postgres' to connect as DB user 'postgres' local all postgres peer

    Allow OS user 'webuser' to connect to 'myapp_db' as DB user 'webuser' local myapp_db webuser peer

    Allow any local user to connect to any database using MD5 password authentication local all all md5

    … other rules … ` After making your changes, save the pg_hba.conf file.

    4. Reload PostgreSQL Configuration

    For pg_hba.conf changes to take effect, PostgreSQL needs to reload its configuration. A reload is preferred over a full restart as it doesn't drop existing connections.

    sudo systemctl reload postgresql

    If you encounter issues or are unsure, a full restart can also work:

    sudo systemctl restart postgresql

    5. Verify UNIX Socket Path and Permissions (If pg_hba.conf still doesn't fix it)

    If you're still facing issues, or if the pg_hba.conf rule seems correct, double-check the actual socket file.

    1. Check the socket directory permissions:
    2. The unixsocketdirectories setting in postgresql.conf (which you found in Step 2) indicates where the socket should be. Ensure this directory exists and has appropriate permissions.
    3. `bash
    4. ls -ld /var/run/postgresql/ # Adjust path as per 'SHOW unixsocketdirectories;'
    5. `
    6. Expected permissions: drwxrwxr-x or similar, owned by postgres:postgres or root:postgres.
    1. Check the socket file itself:
    2. `bash
    3. ls -l /var/run/postgresql/.s.PGSQL.5432 # Adjust path
    4. `
    5. Expected output should show the socket file existing and owned by postgres:postgres.
    6. `
    7. srwxrwxrwx 1 postgres postgres 0 Jun 27 10:45 /var/run/postgresql/.s.PGSQL.5432
    8. `
    9. The s at the beginning signifies a socket file. If the file is missing after starting PostgreSQL, or has incorrect permissions that prevent the client from accessing it, this can lead to connection failures. In rare cases, if the postgresql.conf file itself (from SHOW configfile;) had an incorrect unixsocket_directories setting, you would need to edit that file and restart PostgreSQL.

    [!NOTE] On Debian/Ubuntu, the /var/run/postgresql directory and socket are usually managed correctly by systemd and the postgresql service. Manual intervention for permissions here is rarely needed unless you've made custom configurations.

    6. Test the Connection

    After making changes and reloading/restarting PostgreSQL, attempt to connect again using your client or application.

    Using psql:

    psql -U youruser -d yourdb -h localhost # -h localhost often forces TCP/IP, omit for socket
    psql -U youruser -d yourdb # This will default to UNIX socket connection

    If you are connecting as an operating system user that has a matching PostgreSQL user and peer authentication is configured:

    sudo -u webuser psql -d myapp_db

    If the connection is successful, you should see the psql prompt or your application should connect without error.

    By systematically working through these steps, you should be able to identify and correct the pg_hba.conf configuration issue preventing your PostgreSQL server from accepting local socket connections.

  • Troubleshooting PostgreSQL Deadlocks: Transaction Cancel Lock Conflicts

    Resolve PostgreSQL deadlocks related to transaction cancel lock conflicts. Diagnose root causes from logs, optimize queries, and refine application logic.

    When your PostgreSQL database logs start reporting "deadlock detected" messages, particularly those mentioning a "transaction cancel lock conflict", it's a clear signal of serious contention within your application's interaction with the database. These deadlocks can manifest as slow query responses, failed transactions, and an overall degradation of application performance and reliability. Understanding and resolving these requires a deep dive into both database behavior and application transaction logic.

    This guide provides a highly technical, step-by-step approach to diagnose, understand, and resolve PostgreSQL deadlocks specifically related to transaction cancel lock conflicts, drawing on common production hosting practices.

    Symptom & Error Signature

    The primary symptom you'll encounter is an application error indicating a failed transaction, a query timeout, or a database connection error. Concurrently, your PostgreSQL logs will be filled with ERROR: deadlock detected entries. The "transaction cancel lock conflict" specifically points to a scenario where one transaction is waiting for a lock held by another, which itself is in the process of being cancelled or terminated, leading to a circular dependency.

    Typical log entries might look like this:

    2026-06-27 10:00:01 UTC [23456]: [APPLICATION] ERROR:  deadlock detected
    2026-06-27 10:00:01 UTC [23456]: [APPLICATION] DETAIL:  Process 23456 waits for AccessExclusiveLock on relation 12345 of database 12346; blocked by process 23457.
        Process 23457 waits for transactionid 7890 (transaction cancel lock conflict); blocked by process 23456.
        Process 23456: UPDATE accounts SET balance = balance - 100 WHERE id = 123;
        Process 23457: SELECT * FROM audit_logs WHERE account_id = 123 FOR UPDATE;
    2026-06-27 10:00:01 UTC [23456]: [APPLICATION] HINT:  See server log for full deadlock information.
    2026-06-27 10:00:01 UTC [23456]: [APPLICATION] STATEMENT:  UPDATE accounts SET balance = balance - 100 WHERE id = 123;

    Another variation could be:

    2026-06-27 10:05:05 UTC [98765]: [APPLICATION] ERROR:  deadlock detected
    2026-06-27 10:05:05 UTC [98765]: [APPLICATION] DETAIL:  Process 98765 waits for ExclusiveLock on tuple (0,27) of relation 67890 of database 67891; blocked by process 98766.
        Process 98766 waits for transactionid 1234 (transaction cancel lock conflict); blocked by process 98765.
        Process 98765: INSERT INTO orders (user_id, product_id, quantity) VALUES (456, 789, 1);
        Process 98766: DELETE FROM carts WHERE user_id = 456;
    2026-06-27 10:05:05 UTC [98765]: [APPLICATION] HINT:  See server log for full deadlock information.

    Root Cause Analysis

    A deadlock occurs when two or more transactions are waiting for each other to release locks, forming a cyclical dependency where none can proceed. PostgreSQL's deadlock detector automatically identifies these situations and arbitrarily aborts one of the transactions (the "deadlock victim") to allow the others to proceed.

    The specific "transaction cancel lock conflict" indicates a more nuanced scenario: 1. Transaction A holds a lock (e.g., on a row or table). 2. Transaction B attempts to acquire a lock that Transaction A holds, so Transaction B waits. 3. Concurrently, some external process (e.g., pgcancelbackend(), pgterminatebackend(), or a client-side timeout) attempts to cancel or terminate Transaction A. 4. The cancellation/termination itself requires a lock on Transaction A's internal state (e.g., a "transaction cancel lock"). 5. However, Transaction B is waiting for Transaction A's initial lock, and now Transaction A is blocked internally trying to acquire the cancel lock because Transaction B might be holding it indirectly, or vice-versa. This creates a circular wait involving the cancellation mechanism itself.

    Root causes often include:

    • Inconsistent Lock Ordering: Transactions acquire locks on resources (rows, tables) in different orders. For example, Transaction 1 locks resource A then resource B, while Transaction 2 locks resource B then resource A.
    • Long-Running Transactions: Transactions that hold locks for extended periods increase the window for conflicts.
    • Inefficient Queries: Queries that scan large portions of tables or lack appropriate indexes can acquire more locks than necessary, or hold them for longer.
    • Aggressive Connection Management: Applications or external monitors might be too eager to cancel or terminate "idle" or long-running transactions, inadvertently triggering or exacerbating deadlocks if those transactions hold critical locks.
    • Application Logic Flaws: Business logic that performs multiple database operations within a single transaction without proper atomicity considerations.
    • High Concurrency: A large number of concurrent transactions operating on the same data.
    • Misunderstood Isolation Levels: While READ COMMITTED (the default) usually prevents predicate locks from leading to deadlocks, higher isolation levels like SERIALIZABLE or REPEATABLE READ are more prone to deadlocks if not handled carefully.

    Step-by-Step Resolution

    Resolving "transaction cancel lock conflict" deadlocks typically involves a combination of database optimization and application code refactoring.

    1. Analyze PostgreSQL Logs and Current Activity

    The first step is to gather as much information as possible from the PostgreSQL logs and the live database state.

    • Access PostgreSQL Logs:
    • On Systemd-based systems (like Ubuntu), logs are often managed by journald.
        # View recent PostgreSQL logs for a specific version (e.g., 15)

    Follow live PostgreSQL logs sudo journalctl -u [email protected] -f ` Alternatively, if logdestination is set to stderr and loggingcollector to on, logs will be in /var/lib/postgresql/<version>/main/log/.

    • Identify Conflicting Processes and Queries:
    • The DETAIL: section of the deadlock error is crucial. Note the Process ID (PID), relation ID, database ID, and the exact STATEMENT for both processes. This information directly points to the queries and tables involved.
    • Inspect pgstatactivity:
    • While the deadlock is happening, or to investigate its aftermath, pgstatactivity can show what queries were running, their states, and the locks they held.
        -- Connect to your database as a superuser (e.g., postgres)

    SELECT pid, application_name, datname, usename, client_addr, state, waiteventtype, wait_event, query_start, query FROM pgstatactivity WHERE state != 'idle' ORDER BY query_start DESC; `

    • Examine pg_locks:
    • pglocks provides real-time information about locks held by transactions. Correlate PIDs from the logs with pglocks to see what types of locks were involved.
        SELECT
            a.pid,
            a.usename,
            a.datname,
            a.client_addr,
            a.state,
            a.query,
            pg_blocking_pids(a.pid) as blocked_by,
            locktype,
            mode,
            granted,
            relname
        FROM pg_stat_activity a
        JOIN pg_locks pl ON a.pid = pl.pid
        LEFT JOIN pg_class pc ON pl.relation = pc.oid
        WHERE a.state = 'active'
        AND pg_blocking_pids(a.pid) IS NOT NULL;
        ```

    2. Optimize Database Schema and Indexes

    Inefficient database design or missing indexes can lead to queries holding locks for too long or locking more rows than necessary.

    • Identify Slow or Inefficient Queries:
    • Use the STATEMENT from the deadlock log and analyze its execution plan.
        -- Example for a problematic UPDATE statement

    — Example for a problematic SELECT … FOR UPDATE EXPLAIN ANALYZE SELECT * FROM auditlogs WHERE accountid = 123 FOR UPDATE; ` > [!NOTE] > EXPLAIN ANALYZE actually executes the query, so use it carefully on production if it's a modifying query. For SELECT statements, it's generally safe.

    • Add or Optimize Indexes:
    • Ensure appropriate indexes are in place for columns used in WHERE, JOIN, ORDER BY, GROUP BY clauses, and especially for columns involved in UPDATE or DELETE conditions. Missing indexes can cause full table scans, leading to AccessExclusiveLock conflicts or long-held RowExclusiveLocks.
        -- Example: Ensure an index on account_id if frequently used in WHERE clauses

    — Example: Ensure an index on the 'id' column of the accounts table CREATE UNIQUE INDEX IF NOT EXISTS idxaccountsid ON accounts (id); `

    • Review Table Design:
    • Consider if table structures or relationships contribute to contention. Sometimes, denormalization or partitioning might alleviate hot spots, but these come with their own trade-offs.

    3. Refactor Application Transaction Logic

    This is often the most critical area for resolving deadlocks. The "transaction cancel lock conflict" specifically hints at issues exacerbated by external cancellation, which means transactions are holding locks long enough to become targets for cancellation while also being part of a deadlock cycle.

    • Consistent Lock Ordering:
    • > [!IMPORTANT]
    • > This is paramount for preventing deadlocks. All transactions accessing the same set of resources (tables, rows) must acquire locks in the same, predefined order. For example, if transactions often update tableA and tableB, always lock rows in tableA first, then tableB, or vice-versa, but never mix the order. This applies to SELECT ... FOR UPDATE as well.
    • Reduce Transaction Duration:
    • Keep transactions as short as possible. Acquire locks just before they are needed and release them as soon as possible by committing or rolling back the transaction. Avoid user interaction or network calls within an active transaction.
    • Minimize Lock Contention:
    • * Use SELECT ... FOR UPDATE carefully: Only lock the rows you truly need to modify.
    • * Consider SKIP LOCKED: If your application can tolerate processing a subset of rows (e.g., a worker queue), SELECT ... FOR UPDATE SKIP LOCKED can be very effective in preventing waiting and thus deadlocks, by simply skipping rows currently locked by other transactions.
            -- Example: Process items from a queue without waiting for locked items
            SELECT * FROM queue_items WHERE processed = false
            ORDER BY created_at
            FOR UPDATE SKIP LOCKED LIMIT 10;
            ```
    • Batch Operations:
    • For bulk updates or inserts, break them into smaller, manageable transactions rather than one giant transaction. This reduces the total time locks are held.
    • Implement Robust Retry Logic:
    • Client-side retry logic with exponential backoff is crucial. When PostgreSQL chooses a deadlock victim, the application should be prepared to catch the error, roll back, and retry the entire transaction. Exponential backoff helps avoid immediately re-entering the deadlock scenario.
    • Review Application Cancellation/Timeout Strategies:
    • If your application or monitoring tools are aggressively cancelling long-running queries (e.g., using pgcancelbackend()), ensure this is done judiciously. Understand that a query holding locks might be critical for other transactions. Shortening query timeouts might push the problem from "slow queries" to "deadlocks if cancellation fails quickly."

    4. Adjust PostgreSQL Configuration Parameters

    While not a direct fix for application logic, some PostgreSQL parameters can help diagnose or manage deadlock behavior.

    • deadlock_timeout:
    • This parameter defines how long a transaction waits for a lock before checking for a deadlock. The default is usually 1 second (1000ms).
    • > [!WARNING]
    • > Do NOT blindly increase deadlock_timeout to solve deadlocks. This will only make your application wait longer before detecting and resolving the deadlock, leading to worse user experience and timeouts. It should be kept relatively low. Increasing it might only mask the underlying contention.
    • >
    • > If you suspect specific long-running non-deadlocking queries are being cancelled, and you want to give them more time, you might slightly increase deadlock_timeout, but only after thorough analysis and understanding the consequences.
    • loglockwaits:
    • Set this to on to log information about any lock waits that last longer than deadlock_timeout. This is extremely useful for identifying which queries are frequently waiting for locks.
        # In postgresql.conf
        log_lock_waits = on
    • logminduration_statement:
    • Setting this to a value (e.g., 500ms) will log all statements that run for longer than that duration. This helps identify slow queries that might be holding locks for too long.
        # In postgresql.conf
        log_min_duration_statement = 500ms
    • maxlocksper_transaction:
    • Increases the maximum number of locks an individual transaction can hold. The default (64) is usually sufficient, but if you have extremely complex transactions, it might need adjustment. Increasing this too much can consume more shared memory.

    How to change configuration parameters: 1. Edit your postgresql.conf file. The path is typically /etc/postgresql/<version>/main/postgresql.conf. `bash sudo vi /etc/postgresql/15/main/postgresql.conf ` 2. After making changes, reload PostgreSQL for them to take effect (no restart needed for most postgresql.conf changes).

        sudo systemctl reload [email protected]

    5. Utilize Monitoring Tools

    Proactive monitoring is key to catching contention patterns before they escalate into frequent deadlocks.

    • pgstatstatements:
    • Enable pgstatstatements to track execution statistics for all queries executed by your server. This helps identify the most expensive, slowest, or most frequently executed queries that might be candidates for optimization.
        -- In postgresql.conf:
        # shared_preload_libraries = 'pg_stat_statements'

    — After restart, connect to your database and enable it: CREATE EXTENSION pgstatstatements;

    — Then query it: SELECT query, calls, totalexectime, meanexectime FROM pgstatstatements ORDER BY totalexectime DESC LIMIT 10; `

    • Prometheus/Grafana with pg_exporter:
    • Deploy pg_exporter to collect PostgreSQL metrics and visualize them in Grafana. Key metrics to monitor include:
    • * pgstatactivity metrics (number of active queries, waiting queries).
    • * Lock wait events.
    • * Transaction duration.
    • * Deadlock count.

    By systematically applying these steps, focusing on both database-level optimizations and critically, application-level transaction design, you can effectively resolve PostgreSQL deadlocks caused by "transaction cancel lock conflicts" and significantly improve your database's stability and performance.

  • PostgreSQL pg_log Disk Space Exhausted: Troubleshooting ‘lock folder block’ and Service Failure

    Resolve critical PostgreSQL pg_log disk space exhaustion issues causing 'lock folder block' errors. Learn to free space, prevent recurrence, and restore database operations.

    When a PostgreSQL database server runs out of disk space, especially due to an overgrown pg_log directory, it can lead to a cascade of critical failures. Your applications might become unresponsive, new database connections could fail, and PostgreSQL itself might refuse to start or operate correctly. The symptom "lock folder block" often arises from the system's inability to write crucial lock files or other temporary data due to insufficient disk space, essentially blocking any further database operations. This guide provides a highly technical, accurate, and step-by-step resolution for this common and severe issue.

    Symptom & Error Signature

    The most prominent symptoms will be application errors indicating an inability to connect to the database, perform queries, or write data. On the server itself, you'll observe PostgreSQL service failures and critical errors in its logs or system journals.

    Typical error messages you might encounter include:

    Application-level errors: ` FATAL: remaining connection slots are reserved for non-replication superuser connections SQLSTATE[53100]: diskfull: ERROR: could not write to file "base/pgsqltmp/…" : No space left on device `

    PostgreSQL server logs (/var/lib/postgresql/1X/main/pg_log/ or journalctl -u postgresql): ` LOG: could not open temporary statistics file "pgstattmp/global.tmp": No space left on device WARNING: could not write to log file: No space left on device FATAL: could not write to file "pgwal/archivestatus/000000010000000000000001.00000000.backup": No space left on device PANIC: could not write to file "pg_xact/0000": No space left on device LOG: could not write to log file: No space left on device FATAL: the database system is shutting down `

    The "lock folder block" might not be a literal error message, but rather an observed behavior where the postmaster process fails to create or update its postmaster.pid lock file, or other processes fail to acquire locks because any write operation, including lock file management, is blocked by the full disk. This prevents the database from starting or running.

    Root Cause Analysis

    The primary root cause for "pglog disk space exhausted" is straightforward: the /var/lib/postgresql/1X/main/pglog directory has consumed all available disk space on its mounted partition, or the entire root filesystem. This happens due to:

    1. Excessive Logging: PostgreSQL's logging level (logminmessages, logstatement) might be set too verbosely (e.g., logstatement = 'all') for a production environment, generating an immense volume of log data.
    2. Lack of Log Rotation: The logrotationage or logrotationsize parameters in postgresql.conf are not configured, or a system-level log rotation utility like logrotate is not properly set up for PostgreSQL logs. This allows log files to accumulate indefinitely.
    3. High Transaction Volume / Errors: A very busy database or an application generating frequent errors can rapidly fill logs, especially if combined with verbose logging.
    4. Unmonitored Disk Usage: Absence of proactive disk space monitoring and alerting systems allows the problem to escalate unnoticed until it causes a production outage.

    When the disk becomes full, PostgreSQL cannot: * Write new log entries. * Create or update temporary files (e.g., for complex queries, sorts). * Perform WAL (Write-Ahead Log) archiving or even write new WAL segments. * Update critical system files, including the postmaster.pid lock file which signals the running status of the server. * Allocate memory or swap space effectively if the /tmp or swap partitions are also affected.

    This inability to perform basic write operations leads to the database system becoming unstable, unresponsive, or failing to start altogether.

    Step-by-Step Resolution

    Addressing this issue requires immediate action to free up space, followed by preventive measures to ensure it doesn't recur.

    1. Immediate Action: Free Up Disk Space

    The very first step is to free up enough disk space to allow PostgreSQL to start and function.

    a. Identify the Full Filesystem: Use df -h to see disk usage across all mounted filesystems. This will confirm which partition is full and likely hosting your PostgreSQL data.

       df -h
       ```

    b. Locate Large Files/Directories: Navigate to the PostgreSQL data directory, usually /var/lib/postgresql/1X/main/, where 1X is your PostgreSQL version (e.g., 14). Then, examine the pg_log directory.

       # Replace '14' with your PostgreSQL version
       sudo su - postgres
       cd /var/lib/postgresql/14/main/
       du -sh pg_log/*
       du -sh pg_log/
       exit
       ```

    c. Safely Remove Old pg_log Files: This is the most critical immediate step. You need to delete old log files. Do NOT delete currently open log files or the entire pg_log directory.

    [!WARNING] > Be extremely careful when deleting files on a production system. Incorrect rm commands can lead to data loss. Always double-check your find and rm commands before execution. Only delete files you are certain are old log files and not actively being written to.

    To delete log files older than, say, 7 days:

       # Replace '14' with your PostgreSQL version
       sudo find /var/lib/postgresql/14/main/pg_log -name "postgresql-*.log" -mtime +7 -exec rm {} ;
       ```
       *   `-name "postgresql-*.log"`: Targets PostgreSQL log files. Adjust if your naming convention differs.
       *   `-mtime +7`: Selects files modified more than 7 days ago. Adjust as needed to free sufficient space.

    If you need more space immediately, consider deleting files older than 3 or 1 day, or even specific large files.

       # Example: Delete files older than 3 days

    If you are absolutely desperate, you could delete all but the last few hours/days, # but use with extreme caution: # List files by modification time, exclude very recent ones, and pipe to rm # sudo find /var/lib/postgresql/14/main/pg_log -name "postgresql-*.log" -mmin +60 -print0 | xargs -0 rm `

    d. Check Disk Space Again: After deleting files, run df -h again to confirm that space has been freed.

       df -h
       ```

    2. Restart PostgreSQL Service

    Once sufficient disk space is available, attempt to restart PostgreSQL.

    sudo systemctl start postgresql
    sudo systemctl status postgresql

    [!NOTE] If PostgreSQL still fails to start, check journalctl -u postgresql -xn 100 for recent errors and verify there's enough free space for temporary files and WAL segments. Sometimes, after a disk full event, WAL consistency checks can take time.

    3. Implement Log Rotation (Preventive)

    To prevent future disk space exhaustion, configure logrotate for your PostgreSQL logs.

    a. Create/Edit logrotate Configuration: Create a new file or edit an existing one in /etc/logrotate.d/ for PostgreSQL.

       sudo nano /etc/logrotate.d/postgresql
       ```
       # Replace '14' with your PostgreSQL version if it differs
       /var/lib/postgresql/14/main/pg_log/*.log {
           daily
           missingok
           rotate 7
           compress
           delaycompress
           notifempty
           create 0640 postgres postgres
           sharedscripts
           postrotate
               # Reload PostgreSQL to make it open new log files.
               # This sends a SIGHUP signal to the postmaster.
               # Ensure this command is correct for your system/init system.
               if [ -f "/var/run/postgresql/.s.PGSQL.5432" ]; then
                   pg_ctlcluster 14 main reload > /dev/null || true
               fi
           endscript
       }
       ```
       *   `daily`: Rotate logs daily. Can be `weekly` or `monthly`.
       *   `rotate 7`: Keep 7 rotated log files.
       *   `compress`, `delaycompress`: Compress old logs.
       *   `create 0640 postgres postgres`: Create new log files with specified permissions and ownership.

    b. Test logrotate Configuration: You can manually run logrotate in debug mode to check the configuration.

       sudo logrotate -d /etc/logrotate.d/postgresql
       ```

    4. Optimize PostgreSQL Logging Configuration (Preventive)

    Adjust postgresql.conf to control the volume and rotation of logs directly from PostgreSQL. This is supplementary to logrotate and can provide finer control.

    a. Edit postgresql.conf: Locate your postgresql.conf file, typically at /etc/postgresql/1X/main/postgresql.conf.

       # Replace '14' with your PostgreSQL version
       sudo nano /etc/postgresql/14/main/postgresql.conf

    b. Adjust Logging Parameters: Find and modify (or add) the following parameters. Uncomment them if they are commented out.

       # --- FILE LOCATIONS ---
       log_directory = 'pg_log'             # directory where log files are written, relative to PGDATA
       log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log' # log file name pattern; use 'csvlog' for CSV format
       log_rotation_age = 1d                # Automatic log file rotation will occur after this much time.
       log_rotation_size = 0                # Automatic log file rotation will occur after this much data is written.
                                            # Set to 0 to use log_rotation_age only, or a value like '100MB'.
       log_truncate_on_rotation = on        # If ON, PostgreSQL will overwrite existing log files with the same name.
                                            # This is useful with log_rotation_age when you don't use a time-based filename.
                                            # With the above log_filename, it's less critical.

    — WHAT TO LOG — logminmessages = warning # Minimum message level to log (DEBUG5, DEBUG4, DEBUG3, DEBUG2, DEBUG1, INFO, NOTICE, WARNING, ERROR, LOG, FATAL, PANIC) # Set to 'warning' or 'error' for production to reduce log volume. logminerror_statement = error # Statements generating this level (or higher) of error will be logged. log_statement = 'none' # 'none', 'ddl', 'mod', 'all' # Set to 'none' in production unless debugging. log_duration = off # Log the duration of each completed statement. Useful for performance tuning. `

    [!IMPORTANT] > For logrotationage and logrotationsize, if logrotate is configured to handle the files, you might set logtruncateonrotation = on and rely solely on logrotate for actual file deletion. If logrotationsize is set to 0 and logrotation_age to 1d, PostgreSQL will create a new log file every day, and logrotate will then manage rotating and compressing these daily files.

    c. Reload PostgreSQL Configuration: After modifying postgresql.conf, you need to reload PostgreSQL for the changes to take effect. A reload is usually sufficient; a full restart is not always required unless logging_collector itself was changed from off to on.

       sudo systemctl reload postgresql

    5. Monitor Disk Usage (Long-term Prevention)

    Implement robust monitoring to prevent recurrence.

    • Monitoring Tools: Use tools like Prometheus with node_exporter for disk metrics, Zabbix, Nagios, or Icinga to monitor disk space usage on your PostgreSQL server.
    • Alerting: Configure alerts to notify you (via email, Slack, PagerDuty, etc.) when disk usage exceeds a predefined threshold (e.g., 80% or 90%).

    6. Consider WAL Archiving Configuration (Advanced)

    If your setup uses WAL archiving for point-in-time recovery or replication, ensure it's not contributing to disk space issues.

    • archivemode and archivecommand: Verify that archivecommand is successfully moving WAL segments to their destination and that the destination itself has sufficient space and is being properly managed (e.g., old archives being deleted). Failed archivecommand will cause WAL segments to accumulate in pgwal/pgarchive or pg_wal (depending on PostgreSQL version), leading to disk exhaustion.

    By following these steps, you can recover from a PostgreSQL disk space exhaustion incident and implement robust measures to prevent it from happening again, ensuring the stability and reliability of your database environment.