Category: Database

  • Redis OOM Error: ‘command not allowed when used memory limit reached’ on Alpine Linux

    Troubleshoot Redis OOM errors on Alpine Linux, common in containerized environments. Learn to analyze memory, adjust limits, and optimize Redis for production stability.

    Introduction

    As an experienced Systems Administrator, encountering Redis Out-Of-Memory (OOM) errors is a critical incident that can bring applications to a halt. The specific error message "OOM command not allowed when used memory limit reached" indicates that your Redis instance has hit its configured maxmemory threshold and, crucially, is configured with a noeviction policy, preventing it from automatically removing keys to free up space. This is particularly common in resource-constrained environments like Docker containers running Alpine Linux, where memory limits are often tightly controlled. When this occurs, your application will experience command failures, timeouts, and data unavailability, leading to a degraded user experience or complete service outage.

    This guide will provide a highly technical, accurate, and step-by-step approach to diagnose, understand, and resolve this critical Redis OOM issue on Alpine Linux environments.

    Symptom & Error Signature

    When Redis reaches its maxmemory limit with a noeviction policy, all write commands (and some read commands depending on specific configuration) will be blocked. You will typically observe the following:

    1. Application Logs: Your application (e.g., PHP, Python, Node.js) will receive errors from its Redis client library:

    # Example PHP client error
    [2026-07-18 10:30:05] app.ERROR: RedisException: OOM command not allowed when used memory > 'maxmemory' in /app/vendor/phpredis/phpredis/src/Redis.php:123
    Stack trace:
    #0 /app/src/Service/CacheService.php(45): Redis->setEx()

    Example Python client error Traceback (most recent call last): File "myapp.py", line 25, in <module> r.set("user:123", "data") File "/usr/local/lib/python3.9/site-packages/redis/client.py", line 1914, in set return self.execute_command('SET', args, *kwargs) File "/usr/local/lib/python3.9/site-packages/redis/client.py", line 1021, in execute_command return self.executecommand(conn, command_name, args, *kwargs) File "/usr/local/lib/python3.9/site-packages/redis/client.py", line 1047, in executecommand response = conn.read_response() File "/usr/local/lib/python3.9/site-packages/redis/connection.py", line 828, in read_response raise self.read_error() redis.exceptions.ResponseError: OOM command not allowed when used memory > 'maxmemory' (bytes) `

    2. Redis Server Logs: The Redis server itself will log warnings indicating the OOM condition:

    1:M 18 Jul 2026 10:30:00.123 # WARNING: OOM command not allowed when used memory > 'maxmemory' (1073741824 bytes)

    3. redis-cli Output: Direct attempts to write data via redis-cli will also fail:

    redis-cli SET mykey myvalue
    (error) OOM command not allowed when used memory > 'maxmemory' (1073741824 bytes)

    Root Cause Analysis

    The "OOM command not allowed" error on Alpine Linux, or any other OS, points to a fundamental conflict between available memory, configured limits, and the Redis eviction policy.

    1. maxmemory Limit Reached: This is the primary trigger. Redis is configured with a maxmemory directive in its redis.conf (or via CONFIG SET) which sets an explicit upper bound on the amount of memory it will use for data. When the used_memory metric (reported by INFO memory) exceeds this threshold, Redis enters an OOM state.
    1. noeviction Policy: The choice of maxmemory-policy dictates Redis's behavior when maxmemory is reached.
    2. * If maxmemory-policy is set to noeviction (which is often the default or chosen for critical data stores), Redis will return OOM errors for all write commands (e.g., SET, LPUSH, HSET) and some read commands that might potentially create new keys or increase memory usage (e.g., GETSET, SADD if a set key doesn't exist).
    3. * Other policies (e.g., allkeys-lru, volatile-ttl) would attempt to evict keys to free up space, preventing the OOM error but potentially losing data.
    1. Actual Memory Consumption: The memory usage within Redis can grow due to several factors:
    2. * Data Growth: Natural increase in the number of keys or the size of values stored.
    3. * Temporary Keys: Keys without TTLs that accumulate over time.
    4. * Memory Fragmentation: The memfragmentationratio (from INFO memory) indicates how efficiently Redis is using physical memory. A high ratio (e.g., > 1.5) means Redis is consuming significantly more physical RAM than its reported used_memory due to the underlying memory allocator.
    5. * Client Output Buffers: Large or numerous client connections can consume significant memory for output buffers.
    6. * Replication Buffers: Master-replica replication links maintain buffers for changes, which can grow large, especially if replicas fall behind.
    7. * RDB/AOF Background Saves: During a background save operation (BGSAVE or BGREWRITEAOF), Redis uses a copy-on-write mechanism. If data changes significantly during a save, the memory footprint can temporarily double.
    8. * Lua Scripting: Complex or long-running Lua scripts can hold onto memory.
    1. Alpine Linux & Docker Specifics:
    2. Container Resource Limits: In Docker or Kubernetes, the container itself has memory limits (e.g., --memory flag for docker run, resources.limits.memory in Kubernetes). It's crucial that the Redis maxmemory setting is less than the container's memory limit. If Redis's usedmemoryrss (Resident Set Size) approaches or exceeds the container's limit, the OS's OOM killer will terminate the entire container*, which is much more disruptive than Redis simply refusing commands.
    3. * Memory Allocator: Alpine Linux by default uses musl libc for its C standard library, which includes a simple malloc implementation. Official Redis builds typically link against jemalloc on Linux, which is highly optimized for Redis's allocation patterns and generally results in lower fragmentation. If your Redis build on Alpine is using musl's malloc, you might experience higher memory fragmentation and less efficient memory utilization compared to a jemalloc build. You can check mem_allocator in INFO memory.

    Step-by-Step Resolution

    Resolving this OOM issue requires a systematic approach, combining immediate mitigation with long-term optimization and scaling strategies.

    1. Analyze Current Redis Configuration and Memory Usage

    First, gather critical information about your Redis instance. If running in Docker, you'll need to execute commands inside the container.

    # Example: Accessing a Dockerized Redis instance

    — OR —

    Example: Accessing a directly installed Redis instance redis-cli `

    Once connected to redis-cli:

    # Check current maxmemory limit and eviction policy
    CONFIG GET maxmemory

    Get detailed memory statistics INFO memory

    Expected important output from INFO memory: # used_memory: The total number of bytes allocated by Redis using its allocator (usually jemalloc) # usedmemoryrss: The number of bytes that Redis allocated as reported by the operating system. # usedmemorypeak: The maximum memory consumed by Redis (in bytes) since server startup. # memfragmentationratio: usedmemoryrss / used_memory. A ratio > 1 means fragmentation. # maxmemory_human: The configured maxmemory limit in human-readable format. # maxmemory_policy: The configured eviction policy. # mem_allocator: The memory allocator used (e.g., "jemalloc-5.1.0", "libc")

    Check connected clients (can consume memory for output buffers) INFO clients

    Check persistence information (RDB/AOF background saves consume temporary memory) INFO persistence `

    [!IMPORTANT] A memfragmentationratio significantly above 1.0 (e.g., 1.5+) indicates memory fragmentation. If memallocator is libc, especially on Alpine, this could be a contributing factor. If usedmemory_rss is close to your Docker container's memory limit, you risk container OOM kills.

    2. Identify Memory Consumers

    Pinpoint which keys or operations are consuming the most memory.

    # Identify large keys (requires Redis 4.0+)
    # This is a good starting point, but can be slow on very large databases
    # Replace <prefix>* with actual key patterns if known

    For more detailed memory breakdown (Redis 4.0+) redis-cli MEMORY STATS

    Monitor commands in real-time (use cautiously in production due to overhead) # This can help identify commands that might be creating large objects or many keys rapidly. redis-cli MONITOR `

    If running in Docker, monitor the container's resource usage:

    docker stats <redis-container-id-or-name>

    Look for trends in MEM USAGE / LIMIT.

    3. Adjust Redis maxmemory and maxmemory-policy (Short-term / Mitigation)

    This step provides immediate relief but might not be a long-term solution without further optimization.

    A. Increase maxmemory (Temporary Relief or Resource Upgrade)

    If you have available RAM on your host or can increase your container's memory limit, raising maxmemory can alleviate the immediate pressure.

    [!WARNING] Increasing maxmemory without addressing underlying data growth is merely a band-aid. Also, ensure your new maxmemory value is comfortably below your Docker container's memory limit to prevent container OOM kills. A good rule of thumb is maxmemory < containermemorylimit * 0.8 to account for overhead.

    Method 1: Via redis-cli (Runtime – temporary until restart)

    # Example: Set maxmemory to 2GB (2147483648 bytes)
    CONFIG SET maxmemory 2gb

    Method 2: Via redis.conf (Persistent)

    Edit your redis.conf file (typically located at /etc/redis/redis.conf or in your Docker volume mount) and modify the maxmemory directive.

    # /etc/redis/redis.conf or mounted config file
    maxmemory 2gb

    After modifying redis.conf, you must restart the Redis service or container:

    # For a non-containerized Redis instance (e.g., Systemd on Ubuntu)

    For a Docker container docker restart <redis-container-id-or-name> `

    B. Adjust maxmemory-policy (Data Loss Risk vs. Availability)

    Changing the eviction policy can prevent the OOM error by allowing Redis to automatically delete keys. This is suitable for Redis instances primarily used as a cache.

    [!IMPORTANT] Carefully consider the implications of changing maxmemory-policy. Policies other than noeviction will result in data loss when the maxmemory limit is hit. Choose a policy that aligns with your application's data criticality.

    Common eviction policies: * noeviction: (Current problematic state) Returns errors on write operations when maxmemory is reached. Use for critical data. allkeys-lru: Evicts keys least recently used (LRU) from all* keys until maxmemory is respected. Ideal for general-purpose caching. volatile-lru: Evicts LRU keys only among those with an expire set*. Useful if you mix persistent and cache data. allkeys-random: Evicts random keys from all* keys. volatile-random: Evicts random keys only among those with an expire set*. allkeys-ttl: Evicts keys with the shortest time to live (TTL) from all* keys. volatile-ttl: Evicts keys with the shortest time to live (TTL) only among those with an expire set*.

    To change the policy:

    Method 1: Via redis-cli (Runtime – temporary until restart)

    # Example: Set policy to allkeys-lru for a cache
    CONFIG SET maxmemory-policy allkeys-lru

    Method 2: Via redis.conf (Persistent)

    Edit your redis.conf file:

    # /etc/redis/redis.conf or mounted config file
    maxmemory-policy allkeys-lru

    Then restart Redis as described above.

    4. Optimize Data Structures and Application Usage

    This is a crucial long-term strategy to reduce Redis's memory footprint.

    • Set TTLs for Transient Data: Ensure that session data, temporary caches, and other non-persistent data automatically expire.
    • `bash
    • SET mytempkey "some data" EX 3600 # Expires in 1 hour
    • `
    • Use Efficient Data Structures:
    • * Hashes for Related Fields: Instead of SET user:1:name "Alice", SET user:1:email "[email protected]", use a single hash: HSET user:1 name "Alice" email "[email protected]". Hashes (especially "ziplist" encoded ones for small numbers of fields/values) are more memory-efficient than many individual string keys.
    • * Bitmaps for Booleans/Flags: For many on/off flags, bitmaps are extremely memory-efficient.
    • * HyperLogLogs for Unique Counts: For approximate unique counts (e.g., unique visitors), HyperLogLogs (PFADD, PFCOUNT) use fixed, small amounts of memory regardless of the number of unique items.
    • Avoid Large Keys/Values: Very large lists, sets, hashes, or string values consume significant memory. Consider breaking them down or offloading large binary data to object storage.
    • Connection Pooling: Ensure your application uses connection pooling to manage client connections efficiently, reducing the number of active clients and their associated output buffers.

    5. Scale Redis Resources (Long-term / Scaling)

    When optimization isn't enough, scaling is necessary.

    • Vertical Scaling: Increase the RAM allocated to the VM or Docker container running Redis. This is often the simplest first step if you have available host resources.
    • `bash
    • # Example for Docker compose
    • services:
    • redis:
    • image: redis:7-alpine
    • deploy:
    • resources:
    • limits:
    • memory: 2G # Increase to 2GB
    • `
    • Remember to also adjust Redis's maxmemory accordingly to utilize the new resources.
    • Horizontal Scaling (Redis Cluster): For very large datasets or high throughput, consider a Redis Cluster. This shards your data across multiple Redis nodes, distributing memory and CPU load. This requires significant application-level changes and operational complexity.
    • Read Replicas: If your workload is read-heavy, offload read queries to Redis replicas, reducing the load (and potentially memory usage if client buffers are an issue) on the primary instance.

    6. Tune Alpine/Docker Memory Allocator (Advanced)

    If you suspect memory fragmentation is a major issue (high memfragmentationratio) and your INFO memory shows mem_allocator: libc on Alpine, you might benefit from using jemalloc.

    # Check current allocator
    redis-cli INFO memory | grep mem_allocator
    • Official Redis Docker Images: The official redis:<version>-alpine Docker images typically ship with Redis compiled to use jemalloc. If you're using a custom Dockerfile or a non-official image, you might not have jemalloc.
    • Building with jemalloc: If you're building Redis from source on Alpine, ensure jemalloc is included. This usually involves installing jemalloc-dev and configuring Redis with MALLOC=jemalloc.
    • `dockerfile
    • # Example Dockerfile snippet for building Redis with jemalloc on Alpine
    • FROM alpine:3.18
    • RUN apk add –no-cache gcc make musl-dev jemalloc-dev tcl
    • WORKDIR /tmp/redis
    • RUN wget http://download.redis.io/releases/redis-7.0.11.tar.gz &&
    • tar xvzf redis-7.0.11.tar.gz &&
    • cd redis-7.0.11 &&
    • make MALLOC=jemalloc &&
    • make install

    … rest of your Redis setup … ` > [!IMPORTANT] > Switching memory allocators requires rebuilding Redis and is a non-trivial change. It should be thoroughly tested. For most users, simply using the official redis:alpine Docker images is sufficient as they are pre-optimized.

    7. Implement Robust Monitoring and Alerting

    Proactive monitoring is key to preventing future OOM issues.

    • Key Metrics to Monitor:
    • * used_memory: Absolute memory usage.
    • * usedmemoryrss: Physical memory consumed.
    • * memfragmentationratio: Detects memory fragmentation.
    • * keys: Number of keys.
    • * expires: Number of keys with TTLs.
    • * evicted_keys: (If using an eviction policy) indicates when Redis is actively deleting keys.
    • * blocked_clients: Number of clients blocked by commands (can indicate issues).
    • Alerting: Set up alerts for when usedmemory (or usedmemory_rss) exceeds a predefined threshold (e.g., 70-80% of maxmemory or container limit) to provide early warning. Also, alert on OOM command not allowed errors in logs.
    • Tools: Utilize monitoring solutions like Prometheus + Grafana, Datadog, New Relic, or custom scripts to collect and visualize Redis metrics.

    By systematically applying these steps, you can effectively troubleshoot and resolve the "Redis OOM command not allowed when used memory limit reached" error, ensuring the stability and performance of your applications.

  • 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.

  • 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 MySQL Error 1040: Too Many Connections on Ubuntu 20.04 LTS

    Troubleshoot and fix MySQL Error 1040 'Too many connections' on Ubuntu 20.04 LTS. Learn to adjust system limits and MySQL configurations for optimal performance.

    When your web application or service experiences intermittent downtime or slow responses, and you encounter error messages like "Error establishing a database connection," it often points to an underlying database issue. MySQL Error 1040, specifically "Too many connections," is a common indicator that your database server has reached its capacity for handling simultaneous client connections, leading to service disruption. This guide will walk you through diagnosing and resolving this critical issue on Ubuntu 20.04 LTS, addressing both MySQL's internal limits and the underlying operating system configurations.

    Symptom & Error Signature

    Users typically experience one of the following:

    • Website/Application unavailability: A generic database connection error message displayed in the browser or application UI (e.g., "Error establishing a database connection" for WordPress).
    • Application Log Entries: Specific error messages within your application's logs (e.g., PHP, Python, Java stack traces) indicating a connection failure.
    • MySQL Client Error: When attempting to connect to MySQL from the command line or an application, you receive:
        ERROR 1040 (HY000): Too many connections
    • MySQL Error Log Entries: The MySQL server error log (/var/log/mysql/error.log or viewable via journalctl -u mysql.service) will contain entries like:
        [ERROR] [MY-00000] [Server] Too many connections

    Root Cause Analysis

    MySQL Error 1040 indicates that the server has reached the maximum number of simultaneous client connections it can handle. This can be due to several factors, often a combination of them:

    1. max_connections Limit: This is the primary MySQL configuration parameter that defines the maximum number of concurrent client connections allowed. The default value is often set conservatively (e.g., 151), which can be easily exceeded by busy applications.
    2. systemd LimitNOFILE: Each connection to MySQL, along with open tables and other internal server operations, consumes file descriptors. The mysqld process, when started by systemd, is subject to the LimitNOFILE (Maximum number of open file descriptors) setting in its systemd unit file (mysql.service). If this limit is too low, MySQL won't be able to open new files or accept new connections, even if max_connections is high.
    3. sysctl fs.file-max: This is a global system-wide kernel parameter that defines the maximum number of file handles the Linux kernel can allocate. If this limit is hit, no process on the system, including MySQL, can open new files or sockets.
    4. Application Behavior:
    5. * Connection Leaks: Applications failing to properly close database connections after use, leading to a build-up of open connections.
    6. * Inefficient Queries/Long-running Transactions: Slow queries or transactions that hold connections open for extended periods, consuming slots.
    7. * Spikes in Traffic: Sudden, unmanaged surges in user traffic or bot activity overwhelming the database.
    8. * Lack of Connection Pooling: Applications constantly opening and closing new connections instead of reusing a pool of existing ones.
    9. Resource Exhaustion: While not a direct cause of "too many connections," high CPU, RAM, or I/O utilization can slow down query processing, causing connections to remain active longer, exacerbating the connection limit issue.

    Step-by-Step Resolution

    This section outlines a structured approach to diagnose and resolve MySQL Error 1040. We'll start with diagnostics and then move to increasing limits at different levels.

    1. Diagnose Current Status

    Before making changes, understand the current state of your system and MySQL configuration.

    • Check MySQL max_connections and active connections:
        mysql -u root -p -e "SHOW VARIABLES LIKE 'max_connections';"
        mysql -u root -p -e "SHOW STATUS LIKE 'Threads_connected';"
        mysql -u root -p -e "SHOW STATUS LIKE 'Max_used_connections';"

    Maxusedconnections shows the highest number of connections that have been in use simultaneously since the server started. This is crucial for determining how high you need to set max_connections.

    • Check mysqld process open file limits:
        # Find the process ID (PID) of the MySQL server
        PID=$(pgrep mysqld)
        # View its current limits
        sudo cat /proc/$PID/limits | grep "Max open files"

    This will show you the Limit: Max open files for the running mysqld process, which is often constrained by systemd.

    • Check system-wide file descriptor limits:
        cat /proc/sys/fs/file-max

    This displays the global maximum number of file handles the kernel can allocate.

    • Review MySQL error logs:
        sudo journalctl -u mysql.service --since "1 hour ago" -p err
        # Or, if using a separate log file:
        # tail -f /var/log/mysql/error.log

    Look for repeated "Too many connections" errors or other issues indicating instability.

    2. Adjust MySQL max_connections

    This is the most common fix, directly increasing the number of connections MySQL will accept.

    1. Edit MySQL configuration:
    2. The main MySQL configuration file is typically /etc/mysql/mysql.conf.d/mysqld.cnf or /etc/my.cnf. On Ubuntu, it's often a file within the /etc/mysql/mysql.conf.d/ directory (e.g., mysqld.cnf).
        sudo vim /etc/mysql/mysql.conf.d/mysqld.cnf
    1. Locate or add max_connections:
    2. Under the [mysqld] section, add or modify the maxconnections parameter. A good starting point is often 200-500, but it should be tailored based on Maxused_connections from your diagnostics and available RAM. Each connection consumes RAM, so setting it excessively high without sufficient memory can lead to swapping and performance degradation.
        [mysqld]
        # ... other configurations ...
        max_connections = 500

    [!IMPORTANT] > Carefully consider your server's RAM. Each connection requires memory (e.g., buffer sizes, thread stack). Setting max_connections too high without sufficient RAM can cause the server to run out of memory, crash, or swap excessively, severely impacting performance.

    1. Save and exit the editor.
    1. Restart MySQL service:
        sudo systemctl restart mysql
    1. Verify the change:
        mysql -u root -p -e "SHOW VARIABLES LIKE 'max_connections';"

    3. Increase Open File Limits for MySQL (systemd LimitNOFILE)

    If increasing max_connections doesn't resolve the issue, or if Max open files for mysqld is too low, you need to adjust the systemd service unit for MySQL.

    1. Create a systemd override file:
    2. Use systemctl edit to safely create an override file for the mysql.service unit. This prevents direct modification of the main unit file, making upgrades cleaner.
        sudo systemctl edit mysql.service
    1. Add/modify LimitNOFILE:
    2. Add the following lines to the override file. The value for LimitNOFILE should be significantly higher than maxconnections. A common practice is maxconnections * 2 or more, to account for other file descriptors used by MySQL. For example, if max_connections is 500, set LimitNOFILE to 2048 or even 4096.
        [Service]
        LimitNOFILE=4096

    [!WARNING] > Setting LimitNOFILE to an extremely high or incorrect value can prevent the MySQL service from starting. Start with a moderately high value and increase if necessary. This value must also not exceed the system-wide fs.file-max.

    1. Save and exit the editor. systemd will automatically reload the daemon when you save the file.
    1. Restart MySQL service:
    2. This is crucial for the new systemd limits to take effect.
        sudo systemctl restart mysql
    1. Verify the new LimitNOFILE:
        PID=$(pgrep mysqld)
        sudo cat /proc/$PID/limits | grep "Max open files"

    Ensure the "Max open files" value reflects your change.

    4. Adjust System-wide File Descriptor Limit (sysctl)

    If fs.file-max (system-wide limit) is too low, it can prevent any process from opening new files. This is less common but important to check, especially on systems with many services or high file I/O.

    1. Edit sysctl.conf:
        sudo vim /etc/sysctl.conf
    1. Add or modify fs.file-max:
    2. Add or modify the following line. The value should be higher than the sum of all LimitNOFILE values for all processes on your system. A common value for busy servers is 500,000 to 1,000,000.
        fs.file-max = 1048576 # Set to 1 million as an example

    [!IMPORTANT] > This is a global kernel parameter. While a high value is generally safe on modern systems, ensure it's reasonable for your server's total workload.

    1. Save and exit the editor.
    1. Apply the changes immediately:
        sudo sysctl -p
    1. Verify the new limit:
        cat /proc/sys/fs/file-max

    5. Analyze and Optimize Application Behavior (Advanced)

    While increasing limits provides immediate relief, addressing application-level issues is crucial for long-term stability and performance.

    1. Review Application Code for Connection Leaks:
    2. Ensure that database connections are properly closed after use, especially in loops or error handling blocks. Use try-finally constructs or similar mechanisms in your programming language.
    1. Implement Connection Pooling:
    2. For high-traffic applications, use a database connection pool (e.g., built into frameworks, or a dedicated proxy like ProxySQL for MySQL). Connection pooling reuses existing connections, significantly reducing the overhead of opening and closing connections and managing the total number of active connections more efficiently.
    1. Optimize Slow Queries:
    2. Long-running or inefficient queries hold connections open, contributing to the "too many connections" error.
    3. * Enable the MySQL slow query log (slowquerylog = 1, longquerytime = 1) to identify problematic queries.
    4. * Use EXPLAIN to analyze query execution plans and add appropriate indexes.
    5. * Consider query caching or redesigning schema/queries.
    1. Adjust waittimeout and interactivetimeout (MySQL):
    2. These parameters in my.cnf define how long the server waits for activity on a non-interactive/interactive connection before closing it. Reducing these values (e.g., to 60 or 120 seconds) can help release idle connections faster, but exercise caution as too low a value can prematurely disconnect legitimate idle connections.
        [mysqld]
        # ...
        wait_timeout = 120
        interactive_timeout = 120
    1. Traffic Management:
    2. Implement load balancing (e.g., Nginx, HAProxy) to distribute traffic across multiple application servers, reducing the load on a single database. Consider rate limiting for potentially abusive traffic.

    6. Monitoring and Further Tuning

    Ongoing monitoring is essential to ensure the solution is effective and to identify future bottlenecks.

    • Monitor Threadsconnected and Maxused_connections: Regularly check these status variables to understand your connection usage patterns.
    • System Resource Monitoring: Keep an eye on CPU, RAM, and I/O utilization to ensure your server has enough resources. Tools like htop, atop, nmon, or dedicated monitoring solutions (e.g., Prometheus with Grafana, Percona Monitoring and Management) are invaluable.
    • Database-specific Metrics: Monitor cache hit rates, query execution times, and other relevant database performance metrics.

    By systematically working through these steps, you can effectively resolve MySQL Error 1040 and ensure your database server can handle its workload efficiently on Ubuntu 20.04 LTS.

  • 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.

  • Resolving ‘MySQL Syntax Error Near Line’ During Database Restore & Migration

    Troubleshoot and fix MySQL syntax errors encountered during database restore or migration, often caused by version mismatches or corrupted dump files. A guide for SysAdmins.

    Introduction

    As a seasoned Systems Administrator or DevOps engineer, encountering a MySQL syntax error near line message during a critical database restore or migration operation can be particularly frustrating. This error typically halts the entire process, leaving a database in an inconsistent state or preventing a vital service migration from completing. While the message clearly indicates a problem with the SQL syntax, the underlying causes are often nuanced, ranging from database version incompatibilities and deprecated features to character set mismatches or even a corrupted dump file. This guide will delve into these common root causes and provide a structured, actionable approach to diagnose and resolve this issue, ensuring your database migrations are robust and successful.

    Symptom & Error Signature

    The most common manifestation of this issue is a database import or migration script failing with an error message that points to a specific line and provides a snippet of the problematic SQL. You might see this in your terminal when running mysql from the command line, within application logs (e.g., PHP-FPM, Node.js, Python), or in Docker container logs.

    Typical error output will resemble:

    ERROR 1064 (42000) at line 1234: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'some_problematic_sql_snippet' at line 1234

    Or, when performing an import:

    mysql -u root -p my_database < database_dump.sql
    Enter password:
    ERROR 1064 (42000) at line 5678: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'DEFINER=`olduser`@`%` EVENT `daily_cleanup_event` ON SCHEDULE EVERY 1 DAY STARTS '2023-01-' at line 5678

    In a Docker environment, you might observe similar errors in your database container logs:

    # docker logs my_mysql_container
    ...
    [ERROR] [MY-000000] [Server] Error in parsing SQL statement: 'ALTER TABLE `users` ADD COLUMN `email_verified_at` TIMESTAMP(0) NULL DEFAULT NULL AFTER `remember_token` COMMENT 'Email verification timestamp';' at line 987.
    ...

    The key diagnostic elements are ERROR 1064 (42000), syntax error, near '...', and at line X.

    Root Cause Analysis

    Understanding the underlying reasons for a MySQL syntax error during a restore or migration is crucial for an effective resolution. Here are the most common culprits:

    1. Database Version Mismatch: This is the most frequent cause.
    2. * Upgrading to Newer Versions (e.g., MySQL 5.7 to 8.0, MariaDB 10.3 to 10.6+): Newer versions often introduce new reserved keywords, deprecate old syntax, remove features (like ZEROFILL for DATETIME or certain CHARACTER SET / COLLATION defaults), or change default behaviors. A dump generated from an older server might contain syntax or features no longer supported or interpreted differently by the newer server. Examples include changes around DEFINER clauses for views/routines/events, DEFAULTCOLLATIONFORUTF8MB4 removal in MySQL 8.0, or specific COLLATE values (e.g., utf8mb40900aici which is MySQL 8.0 specific).
    3. * Downgrading to Older Versions: While less common for syntax errors, a dump from a newer version might use features or syntax not understood by an older server.
    1. Reserved Keywords: As MySQL/MariaDB evolve, new keywords are added. If your schema uses a table, column, or index name that becomes a reserved keyword in a newer version, it can cause a syntax error during import if not properly quoted (e.g., with backticks `).
    1. Character Set and Collation Inconsistencies: Differences in character set or collation definitions between the source and target databases can lead to errors. For example, a dump might specify a collation (utf8mb40900ai_ci) that doesn't exist on the target server (e.g., MySQL 5.7 or older MariaDB).
    1. Corrupted or Incomplete SQL Dump File: Network transfer issues, disk corruption, or an interrupted mysqldump process can result in a truncated or malformed SQL dump file, leading to syntax errors at an unexpected point.
    1. Strict SQL Mode Differences: The target MySQL/MariaDB server might be running with a stricter sqlmode than the source server. For instance, STRICTTRANSTABLES, NOZERODATE, or ONLYFULLGROUPBY can cause certain SQL statements (especially INSERT statements with invalid defaults or GROUP BY clauses) to fail if they were tolerated by the source server.
    1. DEFINER Clause Issues: When moving a database between different MySQL/MariaDB instances, especially when root or other users have different hostnames or are not present on the target system, the DEFINER clause for views, stored procedures, functions, or events in the SQL dump can cause syntax errors if the definer user does not exist or has insufficient privileges on the target.
    1. Engine Specific Syntax/Features: While less common for generic "syntax errors," certain features unique to a specific storage engine (e.g., SPATIAL INDEX for MyISAM) might be used in a way that is problematic for another engine or if the engine itself isn't available.

    Step-by-Step Resolution

    Follow these steps to systematically identify and resolve the "MySQL syntax error near line" issue.

    1. Pinpoint the Exact Error Location and Context

    The error message at line X is your most valuable clue.

    1. Locate the problematic line:
    2. `bash
    3. # For a direct line number
    4. head -n <ERRORLINENUMBER> database_dump.sql | tail -n 1

    For context around the line (e.g., 5 lines before and after) sed -n "$((ERRORLINENUMBER-5)),$((ERRORLINENUMBER+5))p" database_dump.sql | nl ` Replace <ERRORLINENUMBER> with the line number from your error message.

    1. Analyze the SQL snippet: Examine the SQL statement at and around the identified line. What kind of statement is it? (e.g., CREATE TABLE, ALTER TABLE, CREATE VIEW, INSERT, CREATE EVENT, DELIMITER). This will guide your investigation into specific syntax changes or features.

    2. Verify MySQL/MariaDB Server Versions (Source vs. Target)

    Knowing the exact versions of both the database server where the dump was created (source) and where you're trying to restore it (target) is critical.

    1. Check target server version:
    2. `bash
    3. mysql –version
    4. # or inside the MySQL client:
    5. mysql -u root -p -e "SELECT VERSION();"
    6. `
    7. Example output: mysql Ver 8.0.35-0ubuntu0.22.04.1 for Linux on x86_64 ((Ubuntu)).
    1. Determine source server version: If you don't have access to the source server, check the top of your mysqldump file. Often, mysqldump includes comments about the server version it was run on.
    2. `bash
    3. head -n 20 database_dump.sql | grep "MySQL dump"
    4. `
    5. Example output: -- MySQL dump 10.13 Distrib 5.7.38, for Linux (x86_64).

    [!IMPORTANT] > A major version difference (e.g., MySQL 5.7 to 8.0, or MariaDB 10.3 to 10.6+) is a strong indicator of version incompatibility issues. Consult the official upgrade guides for specific syntax changes between these versions.

    3. Inspect and Adapt the SQL Dump File

    Based on the error context and version comparison, you may need to modify the SQL dump file. Always work on a copy of the original dump file.

    1. Address DEFINER Clauses (Common for Views, Stored Procedures, Events):
    2. If the error is related to DEFINER=olduser@% or similar, the user olduser might not exist on the target server, or the host part (%`) might be invalid.
    3. `bash
    4. # Option A: Remove DEFINER clauses (if you don't need to preserve original definers)
    5. # This is often the safest for simple migrations.
    6. sed -i 's/DEFINER=["][^"]["]@["][^"]["]//g' databasedumpmodified.sql

    Option B: Change DEFINER to a known user (e.g., 'root'@'localhost') # Use with caution and only if you understand the security implications. # Replace 'olduser', '%', 'newuser', 'localhost' as appropriate. sed -i 's/DEFINER=olduser@%/DEFINER=newuser@localhost/g' databasedumpmodified.sql `

    [!WARNING] > Modifying DEFINER clauses can impact the security and functionality of views, stored procedures, and events if not handled correctly. Ensure the new definer user has the necessary privileges.

    1. Handle Collation Mismatches (e.g., utf8mb40900ai_ci on MySQL 5.7/MariaDB):
    2. MySQL 8.0 introduced utf8mb40900ai_ci as its default collation. If your target is an older MySQL or MariaDB server, this collation will not exist.
    3. `bash
    4. # Replace the problematic collation with a compatible one (e.g., utf8mb4unicodeci)
    5. sed -i 's/COLLATE utf8mb40900aici/COLLATE utf8mb4unicodeci/g' databasedump_modified.sql

    Similarly, for character set issues sed -i 's/DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb40900aici/DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4unicodeci/g' databasedump_modified.sql `

    1. Adjust sqlmode Syntax (e.g., NOZERO_DATE in MySQL 8.0):
    2. Sometimes, mysqldump might include SET sql_mode statements that are specific to the source version. Check if the problematic line is part of such a statement. You might need to comment out or modify it.
    1. Quote Reserved Keywords:
    2. If the error points to an unquoted identifier that has become a reserved keyword (e.g., RANK or SYSTEM), you'll need to manually add backticks.
    3. `sql
    4. — Before (error if 'SYSTEM' is a reserved keyword)
    5. CREATE TABLE my_table ( id INT, SYSTEM VARCHAR(255) );
    6. — After
    7. CREATE TABLE my_table ( id INT, SYSTEM VARCHAR(255) );
    8. `
    1. Remove ROW_FORMAT=DYNAMIC for Older Targets (less common but possible):
    2. If migrating to a very old MySQL 5.6 or earlier, ROW_FORMAT=DYNAMIC might cause issues.
    3. `bash
    4. sed -i 's/ROWFORMAT=DYNAMIC//g' databasedump_modified.sql
    5. `

    4. Temporarily Adjust MySQL Server Configuration (Strict Mode)

    If the error relates to data insertion failures (e.g., invalid dates, default values, GROUP BY issues) and you suspect sql_mode differences, you can temporarily relax the target server's SQL mode.

    1. Edit MySQL configuration:
    2. `bash
    3. sudo vim /etc/mysql/mysql.conf.d/mysqld.cnf
    4. `
    5. Add or modify the sql_mode directive under the [mysqld] section:
    6. `ini
    7. [mysqld]
    8. sql_mode = "" # An empty string disables all strict modes.
    9. # Alternatively, specify modes you want to keep, excluding problematic ones:
    10. # sqlmode = "NOENGINESUBSTITUTION,REALAS_FLOAT"
    11. `

    [!WARNING] > Disabling sql_mode entirely can lead to data integrity issues and hidden problems. This should be a temporary measure for import only. Re-enable appropriate strict modes immediately after a successful restore.

    1. Restart MySQL service:
    2. `bash
    3. sudo systemctl restart mysql
    4. # Or for MariaDB
    5. sudo systemctl restart mariadb
    6. `
    1. Attempt the import again.
    1. Re-enable strict SQL modes: After successful import, revert the sqlmode change in mysqld.cnf to its original value or a recommended strict value (e.g., ONLYFULLGROUPBY,STRICTTRANSTABLES,NOZERODATE,NOENGINESUBSTITUTION) and restart MySQL.

    5. Check for Corrupted or Incomplete Dump File

    If the error occurs abruptly mid-file, especially towards the end, and doesn't seem to be a specific syntax issue, consider dump file corruption.

    1. Check file integrity:
    2. * Examine the beginning and end of the file. Do they look complete?
    3. * Use tail database_dump.sql to ensure the file doesn't end abruptly mid-statement.
    4. * If the dump was compressed (.sql.gz, .tar.gz), try decompressing it again to rule out decompression errors.
    5. `bash
    6. gzip -t database_dump.sql.gz # Checks integrity of a gzip file
    7. `
    1. Re-create the dump: If possible, try generating the mysqldump file again from the source server. Ensure enough disk space and proper execution.
        # Example mysqldump command for a robust dump:
        mysqldump -u <user> -p --single-transaction --routines --triggers --events --add-drop-database --databases <database_name> > database_dump.sql
        ```
        For specific migration scenarios, consider adding:
        *   `--set-gtid-purged=OFF` (important when GTIDs are involved)
        *   `--no-data` (to dump only schema)
        *   `--no-create-info` (to dump only data)

    6. Incremental Import (Advanced Troubleshooting)

    For very large or complex databases, or if the exact error is hard to pinpoint, an incremental approach can help isolate the problem.

    1. Import Schema First:
    2. `bash
    3. # Create a schema-only dump
    4. mysqldump -u <user> -p –single-transaction –no-data –add-drop-database <databasename> > schemaonly_dump.sql
    5. # Import schema
    6. mysql -u root -p < schemaonlydump.sql
    7. `
    8. If this fails, the error is within your table/view/routine definitions.
    1. Import Data Second:
    2. `bash
    3. # Create a data-only dump (after schema is imported)
    4. mysqldump -u <user> -p –single-transaction –no-create-info <databasename> > dataonly_dump.sql
    5. # Import data
    6. mysql -u root -p < dataonlydump.sql
    7. `
    8. If this fails, the error is likely in the INSERT statements, possibly due to sql_mode issues or character set problems with the data itself.

    This structured approach will help you systematically narrow down the cause of the MySQL syntax error near line and successfully complete your database restore or migration.

  • 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 MongoDB ‘Connection Refused’ on Port 27017

    Resolve MongoDB socket connection refused errors on port 27017. This guide covers firewall, service status, bind IP, and configuration issues for seamless database connectivity.

    When your application attempts to connect to a MongoDB instance and encounters a "Connection Refused" error on port 27017, it signifies that the client's connection request was actively rejected by the target server. This typically means that while the network path to the server might be open, there's no process listening on the specified port, or a firewall is explicitly blocking the connection. This guide provides a systematic approach to diagnose and resolve this critical database connectivity issue, ensuring your MongoDB instance is accessible to your applications.

    Symptom & Error Signature

    Users will typically observe their applications failing to connect to the MongoDB database. This can manifest as blank pages, server-side error messages, or direct CLI connection failures.

    Typical error messages you might encounter include:

    From a client application (e.g., Node.js with Mongoose):

    MongooseError: connect ECONNREFUSED 127.0.0.1:27017
        at Connection.<anonymous> (/path/to/node_modules/mongoose/lib/connection.js:779:20)
        at Object.onceWrapper (node:events:628:28)
        at Connection.emit (node:events:513:28)
        at Connection.emit (/path/to/node_modules/mongoose/lib/connection.js:130:48)
        at Socket.<anonymous> (/path/to/node_modules/mongoose/lib/connection.js:730:16)
        at Socket.emit (node:events:513:28)
        at emitErrorNT (node:internal/process/next_tick:107:12)
        at process.processTicksAndRejections (node:internal/process/task_queues:82:21)

    From the MongoDB shell (mongo or mongosh):

    MongoDB shell version vX.Y.Z
    connecting to: mongodb://127.0.0.1:27017/test
    Error: couldn't connect to server 127.0.0.1:27017, connection attempt failed: SocketException: Error connecting to 127.0.0.1:27017 :: caused by :: Connection refused calling connect()...

    From network utilities (telnet or nc):

    $ telnet 127.0.0.1 27017
    Trying 127.0.0.1...

    $ nc -zv 127.0.0.1 27017 nc: connect to 127.0.0.1 port 27017 (tcp) failed: Connection refused `

    Root Cause Analysis

    The "Connection Refused" error almost always points to one of the following underlying issues:

    1. MongoDB Service Not Running: The mongod process, which is the core MongoDB database server, is not active on the machine. This is the most frequent cause.
    2. Firewall Blocking Connection: An active firewall (either on the host OS like UFW/iptables, or a cloud provider's security group/network ACL) is configured to block incoming connections to port 27017. The client's connection attempt is explicitly denied by the firewall.
    3. Incorrect bindIp Configuration: MongoDB is configured to listen only on specific IP addresses (e.g., 127.0.0.1 for localhost only), but the client application is attempting to connect from a different IP address (e.g., a remote server or a different network interface on the same host).
    4. Incorrect Port Configuration: While less common for the default 27017, MongoDB might be configured to listen on a non-standard port, but the client is still trying to connect to 27017.
    5. Resource Exhaustion or System Issues: In rare cases, the mongod process might fail to start due to issues like disk full, corrupted data files, too many open file descriptors, or other system-level constraints.
    6. SELinux/AppArmor Restrictions: Security enforcement modules (like SELinux on RHEL-based systems or AppArmor on Ubuntu) might be preventing mongod from binding to its port or accessing necessary resources, though this typically manifests as service startup failures rather than a direct "connection refused" if the service isn't attempting to start at all.

    Step-by-Step Resolution

    Follow these steps to diagnose and resolve the MongoDB connection refused error.

    1. Verify MongoDB Service Status

    The first and most crucial step is to ensure that the MongoDB service is actually running.

    sudo systemctl status mongod

    Expected Output (Running):

    ● mongod.service - MongoDB Database Server
         Loaded: loaded (/lib/systemd/system/mongod.service; enabled; vendor preset: enabled)
         Active: active (running) since Tue 2026-06-25 10:00:00 UTC; 1min 2s ago
           Docs: https://docs.mongodb.org/manual
       Main PID: 1234 (mongod)
          Tasks: 23 (limit: 4627)
         Memory: 100.0M
            CPU: 1.500s
         CGroup: /system.slice/mongod.service
                 └─1234 /usr/bin/mongod --config /etc/mongod.conf

    If Active: inactive (dead) or failed: The service is not running. Try starting it:

    sudo systemctl start mongod

    Then check the status again. If it fails to start, investigate the service logs:

    sudo journalctl -xeu mongod
    ```

    [!IMPORTANT] Always check journalctl -xeu mongod if the service fails to start or immediately stops after being started. This log provides critical insights into the startup process and any encountered errors.

    2. Check Network Listener and Port Availability

    If the service is running, verify that it's listening on the expected port (27017) and IP address.

    sudo ss -tuln | grep 27017
    # Or for older systems:
    # sudo netstat -tuln | grep 27017

    Expected Output (Listening):

    tcp   LISTEN 0      4096   127.0.0.1:27017      0.0.0.0:*
    # Or if listening on all interfaces:
    # tcp   LISTEN 0      4096   0.0.0.0:27017        0.0.0.0:*
    • If you see 127.0.0.1:27017, MongoDB is only listening on the local loopback interface. Remote connections will fail.
    • If you see 0.0.0.0:27017, MongoDB is listening on all available network interfaces, including external ones.
    • If no output is returned, MongoDB is either not running, or it's configured to listen on a different port or interface that grep 27017 doesn't match. In this case, re-verify Step 1.

    3. Inspect MongoDB Configuration (mongod.conf)

    MongoDB's network binding is configured in its main configuration file, typically /etc/mongod.conf. The bindIp setting is critical here.

    Open the configuration file for editing:

    sudo nano /etc/mongod.conf

    Locate the net: section and specifically the bindIp parameter:

    # network interfaces
    net:
      port: 27017
      bindIp: 127.0.0.1 # COMMENT THIS LINE OUT or CHANGE IT

    Common bindIp scenarios:

    • bindIp: 127.0.0.1: MongoDB will only accept connections from the local machine. This is the default and recommended for security if the application resides on the same server.
    • bindIp: 0.0.0.0: MongoDB will listen on all available network interfaces. Use this ONLY if you have proper firewall rules in place to restrict access, otherwise, your database will be publicly exposed.
    • bindIp: 127.0.0.1,<yourserverprivate_ip>: To allow connections from the local machine and a specific internal IP address (e.g., from another server in your private network).
    • bindIp: <yourserverpublic_ip>: If your application is external and directly connects to MongoDB, this allows connections on a specific public IP. Not generally recommended.

    Resolution: If your application is trying to connect from a remote server or a different network interface and bindIp is set to 127.0.0.1, you need to modify it.

    • For applications on the same server: Keep bindIp: 127.0.0.1. Ensure your application also connects to 127.0.0.1:27017.
    • For applications on a different server (private network): Change bindIp to include the local server's private IP, or 0.0.0.0 (with strict firewall rules).
    • * Example for private IP: bindIp: 127.0.0.1,192.168.1.10 (replace with your server's actual private IP).
    • * Example for all interfaces (with caution): bindIp: 0.0.0.0

    [!WARNING] Setting bindIp: 0.0.0.0 without properly configured external firewall rules (Step 4) will expose your MongoDB instance to the entire internet. This is a severe security vulnerability. Only use this if you fully understand the implications and have robust network security in place.

    After making changes, save the file and restart the MongoDB service:

    sudo systemctl restart mongod
    ```

    4. Examine Firewall Rules

    Firewalls can block connections even if MongoDB is running and correctly configured. Check both OS-level firewalls and any cloud provider security groups.

    4.1. UFW (Uncomplicated Firewall) on Ubuntu

    Check UFW status:

    sudo ufw status verbose

    If UFW is active and MongoDB is meant to be accessed remotely, ensure port 27017 is allowed.

    To allow access from a specific IP (most secure):

    sudo ufw allow from <CLIENT_IP_ADDRESS>/32 to any port 27017
    sudo ufw reload
    ```

    To allow access from any IP (less secure, use with caution):

    sudo ufw allow 27017/tcp
    sudo ufw reload

    [!WARNING] Allowing 27017/tcp from anywhere (implied by the command above without from clause) opens MongoDB to the public internet. Only do this if bindIp is restricted to an internal IP or if you have other network security controls in place.

    4.2. Cloud Provider Security Groups / Network ACLs

    If your MongoDB server is hosted on a cloud platform (AWS EC2, Azure VM, Google Cloud Compute Engine, etc.), you must check the cloud provider's firewall settings:

    • AWS: Check the Security Group attached to your EC2 instance. Ensure an inbound rule exists for TCP port 27017, allowing traffic from your client's IP address or the appropriate security group.
    • Azure: Review Network Security Groups (NSGs) associated with your VM's network interface or subnet. Add an inbound security rule for TCP port 27017.
    • Google Cloud: Examine your VPC Firewall rules. Create or modify a rule to allow TCP traffic on port 27017 from the necessary source IP ranges.

    These cloud firewalls often supersede OS-level firewalls and are a common source of "Connection Refused" for remote connections.

    5. Verify Disk Space and Permissions

    While less common for a direct "Connection Refused," an inability to write to its data or log directories can prevent MongoDB from starting or operating correctly.

    Check Disk Space:

    df -h
    ```

    Check Permissions:

    ls -ld /var/lib/mongodb /var/log/mongodb
    sudo ls -l /var/lib/mongodb
    ```

    If permissions are incorrect, fix them:

    sudo chown -R mongodb:mongodb /var/lib/mongodb
    sudo chown mongodb:mongodb /var/log/mongodb
    sudo systemctl restart mongod

    6. Review MongoDB Log Files

    Always consult the MongoDB log files for detailed error messages, especially if the service is failing to start or acting unexpectedly.

    sudo tail -f /var/log/mongodb/mongod.log
    ```

    7. Ensure Consistent Configuration for Docker (if applicable)

    If MongoDB is running inside a Docker container, the troubleshooting steps vary slightly:

    • Check container status:
    • `bash
    • docker ps -a | grep mongo
    • `
    • Ensure your MongoDB container is Up. If Exited, check logs: docker logs <containeridor_name>.
    • Port Mapping: Verify that the Docker container's port 27017 is correctly mapped to the host's port 27017 (or another desired host port).
    • In your docker run command or docker-compose.yml, look for the -p or ports entry:
    • `bash
    • # Example docker run
    • docker run -d –name my-mongo -p 27017:27017 mongo:latest

    Example docker-compose.yml # services: # mongo: # image: mongo:latest # ports: # – "27017:27017" ` The format is HOSTPORT:CONTAINERPORT.

    • bindIp inside Docker: For MongoDB running inside a container, it's common and often necessary to set bindIp: 0.0.0.0 in its configuration, as the container's network interface is usually isolated. Docker's port mapping then handles exposing it to the host.
    • This would typically be done via an environment variable or by mounting a custom mongod.conf into the container.
    • Host Firewall: Remember that even with Docker, the host machine's firewall (UFW, iptables) still applies to traffic coming into the host before it reaches Docker's port mappings. Ensure the host firewall allows traffic to the HOST_PORT (e.g., 27017).

    By systematically working through these steps, you should be able to identify and resolve the root cause of your MongoDB "Connection Refused" error on port 27017.

  • 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.