Enforcing CPU quota on postgresDB
For this we are going to use a nice feature of systemd which allows us to specifiy the cgroup CPU quota in an override file. The override file looks like this
cat /etc/systemd/system/postgresql@18-main.service.d/override.conf
[Service]
CPUAccounting=yes
CPUQuota=200%
notice the name of the service/directory is postgresql@18-main.service not postgresql.service
It turns out that postgresql.service is just a wrapper.
Setting up the override file
Step 1 find the name of the actual postgres service
systemctl status "postgresql*"
On my system I see
● postgresql.service - PostgreSQL RDBMS
Loaded: loaded (/usr/lib/systemd/system/postgresql.service; enabled; preset: enabled)
Drop-In: /etc/systemd/system/postgresql.service.d
└─override.conf
Active: active (exited) since Thu 2026-10-08 20:07:31 UTC; 20min ago
...
● postgresql@18-main.service - PostgreSQL Cluster 18-main
Loaded: loaded (/usr/lib/systemd/system/postgresql@.service; enabled-runtime; preset: enabled)
Drop-In: /etc/systemd/system/postgresql@18-main.service.d
└─override.conf
Active: active (running) since Thu 2026-10-08 20:14:01 UTC; 14min ago
Notice how postgresql.service has the status active (exited) whereas postgresql@18-main.service has the status active (runnning)
Step 2 create/edit the override
Do sudo systemctl edit postgresql@16-main.service
add the CPU accounting and quota lines
### Editing /etc/systemd/system/postgresql@18-main.service.d/override.conf
### Anything between here and the comment below will become the contents of the drop-in file
[Service]
CPUAccounting=yes
CPUQuota=200%
Step 3 reload and restart postgres
sudo systemctl daemon-reload
sudo systemctl restart postgresql@16-main.service
Step 4 Verify the cgroup is applied
systemctl status postgresql@16-main.service
You should see a tree under CGroup e.g.
postgresql@18-main.service - PostgreSQL Cluster 18-main
Loaded: loaded (/usr/lib/systemd/system/postgresql@.service; enabled-runtime; preset: enabled)
Drop-In: /etc/systemd/system/postgresql@18-main.service.d
└─override.conf
Active: active (running) since Thu 2026-10-08 20:14:01 UTC; 14min ago
Process: 1544827 ExecStart=/usr/bin/pg_ctlcluster --skip-systemctl-redirect 18-main start (code=exited, status=0/SUCC>
Main PID: 1544833 (postgres)
Tasks: 9 (limit: 618334)
Memory: 4.5G (peak: 4.7G)
CPU: 1min 35.443s
CGroup: /system.slice/system-postgresql.slice/postgresql@18-main.service
├─1544833 /usr/lib/postgresql/18/bin/postgres -D /var/lib/postgresql/18/main -c config_file=/etc/postgresql/>
├─1544834 "postgres: 18/main: io worker 0"
├─1544835 "postgres: 18/main: io worker 1"
├─1544836 "postgres: 18/main: io worker 2"
├─1544837 "postgres: 18/main: checkpointer "
├─1544838 "postgres: 18/main: background writer "
├─1544840 "postgres: 18/main: walwriter "
├─1544841 "postgres: 18/main: autovacuum launcher "
└─1544842 "postgres: 18/main: logical replication launcher "
Step 5
Test with something like pgbench and observe that the expected amount of CPU is consumed e.g. in this case I ask for 20 postgresql back-end servers…
pgbench -S -T 120 pgbench-sf300 -c 20 -j 10 -n -P 1
and when I view in top I should see 10% per CPU per postgres process which x20 (servers) == 200% CPU.
1580983 postgres 20 0 682684 8372 6608 S 38.4 0.0 0:02.16 pgbench
1581000 postgres 20 0 8725076 1.4g 1.4g R 10.6 0.3 0:00.60 postgres
1580994 postgres 20 0 8725076 1.4g 1.4g R 10.3 0.3 0:00.58 postgres
1580995 postgres 20 0 8725076 1.4g 1.4g R 10.3 0.3 0:00.60 postgres
1580997 postgres 20 0 8725076 1.4g 1.4g R 10.3 0.3 0:00.59 postgres
1581001 postgres 20 0 8725076 1.4g 1.4g R 10.3 0.3 0:00.60 postgres
1581002 postgres 20 0 8725076 1.3g 1.3g R 10.3 0.3 0:00.58 postgres
1581003 postgres 20 0 8725076 1.4g 1.4g R 10.3 0.3 0:00.60 postgres
1581005 postgres 20 0 8725076 1.4g 1.4g R 10.3 0.3 0:00.59 postgres
1581007 postgres 20 0 8725076 1.4g 1.4g R 10.3 0.3 0:00.59 postgres