How to Prevent PostgreSQL Log Files from Growing Forever on Debian

Postgres has a good mechanism for preventing the unlimited growing size of log files. In postgres, we can get two types of logs. One is from the postgresql logs that were directly written to the /var/log/* folder, and you can see this normal log file path by checking the result of the pg_lsclusters command. Other is based on the enabling of the logging_collector parameter. If we enable the logging collector, then the log files are redirected to a folder named log and it should exist inside the data directory.

They have a separate mechanism to prevent the uncontrolled size of postgres logfiles using the log_rotation-related parameters in the postgres.

But when we install the packages of the postgresql, it will automatically create a file named postgresql-common and that file act the job of the log rotation in the postgres based on the log files that are exist inside the /var/log/* folder.

Go to the /etc folder, and you can see that a folder named logrotate.d

cybrosys@cybrosys:/etc$ ls -lh logrotate.d/

Result:

total 72K
-rw-r--r-- 1 root root  120 Sep 12  2021 alternatives
-rw-r--r-- 1 root root  433 Mar 18  2024 apache2
-rw-r--r-- 1 root root  126 Apr 22  2022 apport
-rw-r--r-- 1 root root  173 Apr  8  2022 apt
-rw-r--r-- 1 root root   91 Mar 18  2022 bootlog
-rw-r--r-- 1 root root  130 Oct 14  2019 btmp
-rw-r--r-- 1 root root  181 Feb 22  2022 cups-daemon
-rw-r--r-- 1 root root  112 Sep 12  2021 dpkg
-rw-r--r-- 1 root root 1.1K Nov 19  2025 mysql-server
-rw-r--r-- 1 root root  173 May 14 16:00 postgresql-common
-rw-r--r-- 1 root root   94 Feb 23  2022 ppp
-rw-r--r-- 1 root root  374 Dec 24  2021 rsyslog
-rw-r--r-- 1 root root  132 Sep 10  2020 sane-utils
-rw-r--r-- 1 root root  677 Sep 15  2021 speech-dispatcher
-rw-r--r-- 1 root root  270 Jun 18  2024 ubuntu-pro-client
-rw-r--r-- 1 root root  209 Sep 19  2021 ufw
-rw-r--r-- 1 root root  235 Feb 19  2021 unattended-upgrades
-rw-r--r-- 1 root root  145 Oct 14  2019 wtmp

Check the contents inside the logrotate.d file like this.

cybrosys@cybrosys:/etc/logrotate.d$ ls

Result:

alternatives  apport  apt  bootlog  btmp  cups-daemon  dpkg  mysql-server  postgresql-common  ppp  rsyslog  sane-utils  speech-dispatcher  ubuntu-pro-client  ufw  unattended-upgrades  wtmp

Now, there is a file named postgresql-common. Open this file by using the cat command.

cybrosys@cybrosys:/etc/logrotate.d$ cat postgresql-common 

Result:

/var/log/postgresql/*.log {
       weekly
       rotate 10
       copytruncate
       delaycompress
       compress
       notifempty
       missingok
       su root root
}

Check the current clusters.

cybrosys@cybrosys:/etc/logrotate.d$ pg_lsclusters 

Result:

Ver Cluster Port Status Owner    Data directory              Log file
17  main    5434 online postgres /var/lib/postgresql/17/main /var/log/postgresql/postgresql-17-main.log
18  main    5432 online postgres /var/lib/postgresql/18/main /var/log/postgresql/postgresql-18-main.log
19  main    5433 online postgres /var/lib/postgresql/19/main /var/log/postgresql/postgresql-19-main.log

Now, edit the file named /etc/logrotate.d/postgresql-common

sudo nano /etc/logrotate.d/postgresql-common

Modify the contents of this file like this.

/var/log/postgresql/*.log {
       size 1M
       rotate 3
       copytruncate
       delaycompress
       compress
       notifempty
       missingok
       su root root
}

Here, we remove the weekly configuration from this file to speed up log rotation.

We newly added the size 1M to set that the log rotation will happen when the size of the logfile becomes more than 1 MB.

The rotate keyword indicates the number of old log files to keep before deleting the oldest ones.

The log rotation can happen in two ways:

  1. By manually
  2. By automatically using the linux timers

Now, let’s try to execute this manually by directly using the logrotate command.

Before this command execution, these are the metadata of files, and it’s size is based on postgres logs

cybrosys@cybrosys:/var/log/postgresql$ ls -lh /var/log/postgresql/

Result:

total 144M
-rw-r--r-- 1 postgres postgres 7.0M Nov  7 11:16 pgbouncer.log
-rw-r----- 1 postgres adm       61K Nov  7 11:13 postgresql-17-main.log
-rw-r----- 1 postgres adm      137M Nov  7 11:14 postgresql-18-main.log
-rw-r----- 1 postgres adm       21K Nov  7 11:13 postgresql-19-main.log
-rw------- 1 postgres postgres  46K Nov  2 23:29 postgresql-19-main-standby.log

Now execute the log rotation manually.

cybrosys@cybrosys:/var/log/postgresql$ sudo logrotate -v /etc/logrotate.d/postgresql-common

Result:

reading config file /etc/logrotate.d/postgresql-common
Reading state from file: /var/lib/logrotate/status
Allocating hash table for state file, size 64 entries
Creating new state
Creating new state
Creating new state
Creating new state
Creating new state
Creating new state
Creating new state
Creating new state
Creating new state
Creating new state
Creating new state
Creating new state
Creating new state
Creating new state
Creating new state
Creating new state
Creating new state
Creating new state
Creating new state
Creating new state
Creating new state
Creating new state
Creating new state
Creating new state
Creating new state
Creating new state
Creating new state
Creating new state
Creating new state
Creating new state
Creating new state
Creating new state
Creating new state
Creating new state
Creating new state
Creating new state
Creating new state
Creating new state
Creating new state
Creating new state
Creating new state
Creating new state
Handling 1 logs
rotating pattern: /var/log/postgresql/*.log  136314880 bytes (3 rotations)
empty log files are not rotated, old logs are removed
considering log /var/log/postgresql/pgbouncer.log
  Now: 2026-11-07 11:17
  Last rotated at 2026-11-03 00:00
  log does not need rotating (log size is below the 'size' threshold)
considering log /var/log/postgresql/postgresql-17-main.log
  Now: 2026-11-07 11:17
  Last rotated at 2026-11-03 00:00
  log does not need rotating (log size is below the 'size' threshold)
considering log /var/log/postgresql/postgresql-18-main.log
  Now: 2026-11-07 11:17
  Last rotated at 2026-11-03 00:00
  log needs rotating
considering log /var/log/postgresql/postgresql-19-main.log
  Now: 2026-11-07 11:17
  Last rotated at 2026-11-03 00:00
  log does not need rotating (log size is below the 'size' threshold)
considering log /var/log/postgresql/postgresql-19-main-standby.log
  Now: 2026-11-07 11:17
  Last rotated at 2026-11-03 00:00
  log does not need rotating (log size is below the 'size' threshold)
rotating log /var/log/postgresql/postgresql-18-main.log, log->rotateCount is 3
dateext suffix '-20261107'
glob pattern '-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]'
previous log /var/log/postgresql/postgresql-18-main.log.1 does not exist
renaming /var/log/postgresql/postgresql-18-main.log.3.gz to /var/log/postgresql/postgresql-18-main.log.4.gz (rotatecount 3, logstart 1, i 3), 
old log /var/log/postgresql/postgresql-18-main.log.3.gz does not exist
renaming /var/log/postgresql/postgresql-18-main.log.2.gz to /var/log/postgresql/postgresql-18-main.log.3.gz (rotatecount 3, logstart 1, i 2), 
old log /var/log/postgresql/postgresql-18-main.log.2.gz does not exist
renaming /var/log/postgresql/postgresql-18-main.log.1.gz to /var/log/postgresql/postgresql-18-main.log.2.gz (rotatecount 3, logstart 1, i 1), 
old log /var/log/postgresql/postgresql-18-main.log.1.gz does not exist
renaming /var/log/postgresql/postgresql-18-main.log.0.gz to /var/log/postgresql/postgresql-18-main.log.1.gz (rotatecount 3, logstart 1, i 0), 
old log /var/log/postgresql/postgresql-18-main.log.0.gz does not exist
log /var/log/postgresql/postgresql-18-main.log.4.gz doesn't exist -- won't try to dispose of it
copying /var/log/postgresql/postgresql-18-main.log to /var/log/postgresql/postgresql-18-main.log.1
truncating /var/log/postgresql/postgresql-18-main.log

Now, check the contents inside the log folder.

cybrosys@cybrosys:/var/log/postgresql$ ls -lh /var/log/postgresql/

Result:

total 144M
-rw-r--r-- 1 postgres postgres 7.0M Nov  7 11:16 pgbouncer.log
-rw-r----- 1 postgres adm       61K Nov  7 11:13 postgresql-17-main.log
-rw-r----- 1 postgres adm         0 Nov  7 11:17 postgresql-18-main.log
-rw-r----- 1 postgres adm      137M Nov  7 11:17 postgresql-18-main.log.1
-rw-r----- 1 postgres adm       21K Nov  7 11:13 postgresql-19-main.log
-rw------- 1 postgres postgres  46K Nov  2 23:29 postgresql-19-main-standby.log

Now, we can see that the new logfile is created and the name of the new logfile is postgresql-18-main.log.1

But, we can see that the postgres file is not changed and the old logs are copied into the newly created postgres log file, and it truncated the contents of the first log file.

Now, let’s automate the log rotation in ubuntu/

In debian/ubuntu we can list the timers of log rotation by using the below command.

systemctl list-timers logrotate.timer

Result :

NEXT                        LEFT     LAST                        PASSED       UNIT            ACTIVATES        
Sun 2026-11-08 00:00:00 IST 12h left Sat 2026-11-07 08:37:56 IST 2h 41min ago logrotate.timer logrotate.service

1 timer listed.

Now, create a folder and file named override. conf to override the current setting of log rotation.

cybrosys@cybrosys:/var/log/postgresql$ sudo mkdir -p /etc/systemd/system/logrotate.timer.d

Add this to the newly created conf file.

sudo tee /etc/systemd/system/logrotate.timer.d/override.conf <<'EOF'
[Timer]
OnCalendar=
OnCalendar=*:0/2
AccuracySec=1s
EOF

After setting this, reload and check the timers of log rotation again.

cybrosys@cybrosys:/var/log/postgresql$ sudo systemctl daemon-reload
cybrosys@cybrosys:/var/log/postgresql$ sudo systemctl restart logrotate.timer

Now, check the timers of log rotation.

cybrosys@cybrosys:/var/log/postgresql$ systemctl list-timers logrotate.timer

Result:

NEXT                        LEFT         LAST                        PASSED  UNIT            ACTIVATES        
Sat 2026-11-07 11:40:00 IST 1min 8s left Sat 2026-11-07 11:38:00 IST 50s ago logrotate.timer logrotate.service
1 timers listed.

Now, we need to turn off one option in the file named logrotate.service

So, we cannot directly edit this file.

We will create an override.conf file to override the default values of this file.

Create a folder.

cybrosys@cybrosys:/var/log/postgresql$ sudo mkdir -p /etc/systemd/system/logrotate.service.d

Now, add the following content into the override.conf file like this.

sudo tee /etc/systemd/system/logrotate.service.d/override.conf > /dev/null << 'EOF'
[Unit]
# Run even when not on AC power
ConditionACPower=
[Service]
# Clear the original ExecStart before redefining it
ExecStart=
ExecStart=/usr/sbin/logrotate -v /etc/logrotate.conf
EOF

Now, reload the log rotation-related service.

cybrosys@cybrosys:/var/log/postgresql$ sudo systemctl daemon-reload

Now, check whether the override is correctly working or not.

cybrosys@cybrosys:/var/log/postgresql$ systemctl cat logrotate.service 

Result:

# /lib/systemd/system/logrotate.service
[Unit]
Description=Rotate log files
Documentation=man:logrotate(8) man:logrotate.conf(5)
RequiresMountsFor=/var/log
ConditionACPower=true
[Service]
Type=oneshot
ExecStart=/usr/sbin/logrotate /etc/logrotate.conf
# performance options
Nice=19
IOSchedulingClass=best-effort
IOSchedulingPriority=7
# hardening options
#  details: https://www.freedesktop.org/software/systemd/man/systemd.exec.html
#  no ProtectHome for userdir logs
#  no PrivateNetwork for mail deliviery
#  no NoNewPrivileges for third party rotate scripts
#  no RestrictSUIDSGID for creating setgid directories
LockPersonality=true
MemoryDenyWriteExecute=true
PrivateDevices=true
PrivateTmp=true
ProtectClock=true
ProtectControlGroups=true
ProtectHostname=true
ProtectKernelLogs=true
ProtectKernelModules=true
ProtectKernelTunables=true
ProtectSystem=full
RestrictNamespaces=true
RestrictRealtime=true
# /etc/systemd/system/logrotate.service.d/override.conf
[Unit]
# Run even when not on AC power
ConditionACPower=
[Service]
# Clear the original ExecStart before redefining it
ExecStart=
ExecStart=/usr/sbin/logrotate -v /etc/logrotate.conf
cybrosys@cybrosys:/var/log/postgresql$

Here, we can see that the values we set for overriding are placed at the end of this file like the above one.

After 2 minutes, check the contents inside the log folder of postgres.

cybrosys@cybrosys:/var/log/postgresql$ ls -lh

Result:

total 32M
-rw-r--r-- 1 postgres postgres    0 Nov  7 11:40 pgbouncer.log
-rw-r--r-- 1 postgres postgres 7.0M Nov  7 11:40 pgbouncer.log.1
-rw-r----- 1 postgres adm       62K Nov  7 11:18 postgresql-17-main.log
-rw-r----- 1 postgres adm         0 Nov  7 11:40 postgresql-18-main.log
-rw-r----- 1 postgres adm       16M Nov  7 11:40 postgresql-18-main.log.1
-rw-r----- 1 postgres adm      8.9M Nov  7 11:17 postgresql-18-main.log.2.gz
-rw-r----- 1 postgres adm       22K Nov  7 11:18 postgresql-19-main.log
-rw------- 1 postgres postgres  46K Nov  2 23:29 postgresql-19-main-standby.log

Now, we can see that the log rotation works automatically.

PostgreSQL already provides built-in log rotation through the logging_collector and its log_rotation_* parameters. However, on Debian and Ubuntu systems, PostgreSQL packages also integrate with the operating system's logrotate service to manage log files stored under /var/log/postgresql/. Understanding which logging mechanism is active is important because each one is managed differently.

In this article, we explored how Debian's postgresql-common logrotate configuration prevents log files from growing indefinitely, how to customize rotation based on file size instead of time, how the rotate option controls log retention, and how to trigger log rotation manually for testing. We also configured systemd timers and service overrides to automate log rotation more frequently, making it easier to verify the configuration in a test environment.

With an appropriate log rotation policy in place, PostgreSQL log files remain manageable, disk usage stays under control, and older logs are retained only for as long as they are needed. Choosing the right rotation schedule and retention policy depends on the workload, logging volume, and operational requirements of your PostgreSQL environment.

WhatsApp