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