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:
- By manually
- 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.