Innovate | Connect | Transform
ContactService statusUSD $

GICT knowledge base

How to Limit a Linux Service’s Memory with systemd

Published

Linuxvps
limit-linux-service-memory

A service that grows without a memory budget can crowd out other applications on the same server. Systemd can apply a memory limit to the service’s control group, covering its processes together rather than limiting each worker separately. A small drop-in file keeps the policy separate from the packaged service definition.

Before you start

Use an Ubuntu 24.04 server with sudo access and an existing systemd service. The examples use myapp.service; replace that name with your application’s unit. If you need hosting, an InterServer VPS gives you a Linux environment where you can manage service limits.

Plan a brief maintenance window: applying the example includes restarting the service. The values below are a starting example, not a recommended budget for every application. Measure normal operation, startup and busy periods before choosing your own values.

Confirm that the server uses cgroup v2 and exposes the memory controller:

$ stat -fc %T /sys/fs/cgroup
$ cat /sys/fs/cgroup/cgroup.controllers

The first command should print cgroup2fs; the second should include memory. If they do not, resolve the host’s cgroup configuration before following this procedure. Changing a live server’s cgroup mode is outside this guide.

1. Inspect the existing unit and memory use

Read the service and its current settings:

$ sudo systemctl cat myapp.service
$ sudo systemctl show myapp.service -p MemoryCurrent -p MemoryHigh -p MemoryMax -p MemorySwapMax -p ControlGroup -p DropInPaths

MemoryCurrent is the current cgroup memory charge, not a lifetime peak. It includes more than the application’s managed heap, such as charged cache and kernel memory. Watch the service through a representative workload, including its worker processes, and leave room for startup spikes.

Inspect existing drop-ins before adding another. Limits in a parent slice can be tighter than the service’s own settings. A larger value here cannot grant memory that an ancestor cgroup restricts.

Choose a budget for the whole service

Budget for all processes in the service cgroup, startup peaks and representative busy periods. Leave headroom for the kernel and other services. The values below are examples; a service limit neither reserves that RAM nor guarantees that the whole host cannot run out of memory.

2. Create a memory-limit drop-in

Create the override directory:

$ sudo mkdir -p /etc/systemd/system/myapp.service.d

Create /etc/systemd/system/myapp.service.d/90-memory.conf with the following contents. Use an unused filename if that file already exists; do not overwrite another administrator’s configuration.

[Service]
MemoryAccounting=yes
MemoryHigh=256M
MemoryMax=384M
MemorySwapMax=0

These settings have different jobs:

  • MemoryHigh: a pressure threshold. Above 256 MiB, the kernel applies reclaim pressure and throttles the service. Usage can exceed this value; it is not a hard ceiling.
  • MemoryMax: the hard limit. If reclaim cannot keep the service within 384 MiB, the kernel can invoke the out-of-memory killer inside its cgroup.
  • MemorySwapMax: the service’s swap limit. Zero prevents it from using swap. This is separate from the RAM limit, not a reservation of physical memory.
  • MemoryAccounting: enables memory accounting for the unit.

Systemd interprets M in these settings as powers of 1024. Keep MemoryHigh below MemoryMax so the service encounters pressure before reaching the hard limit. For a workload that needs swap, choose an appropriate swap budget instead of copying zero without review.

3. Apply and check the limits

Validate the unit before restarting it:

$ sudo systemd-analyze verify myapp.service

Resolve any configuration errors, then reload systemd and restart the application:

$ sudo systemctl daemon-reload
$ sudo systemctl restart myapp.service
$ sudo systemctl status myapp.service --no-pager
$ sudo systemctl show myapp.service -p MemoryHigh -p MemoryMax -p MemorySwapMax

With the example values, the final command should show:

MemoryHigh=268435456
MemoryMax=402653184
MemorySwapMax=0

Reloading systemd alone is not a restart. Perform a normal application operation after the restart: load a page, process a job or save and retrieve a record. A service can be running while memory pressure makes it too slow to be useful.

If the displayed settings differ, inspect the full unit again. Drop-ins are processed in filename order, and a later assignment can override the same setting. Do not delete unrelated overrides to make this file win.

4. Read pressure and failure evidence

For a running service, obtain its actual control-group path and inspect the kernel counters:

$ cg=$(systemctl show myapp.service -p ControlGroup --value)
$ test -n "$cg" && sudo cat "/sys/fs/cgroup${cg}/memory.current"
$ test -n "$cg" && sudo cat "/sys/fs/cgroup${cg}/memory.events"
$ sudo journalctl -u myapp.service -n 50 --no-pager

Check that cg is nonempty before reading the files. A stopped service’s cgroup may already have disappeared; use its journal in that case. memory.current reports bytes currently charged to the group.

In memory.events, rising high counts indicate encounters with the pressure threshold. The max counter records encounters with the hard boundary. oom and oom_kill help distinguish an allocation failure condition from an actual OOM kill. These are counters, not memory sizes; compare them over an interval under normal load.

A hard limit can terminate application processes and fail requests. Whether the unit stops or restarts also depends on its main process, OOMPolicy and Restart settings. Do not deliberately exhaust memory in a production service just to see a counter change.

For repeated OOM failures, review the application’s own memory settings, worker count and recent changes. Increase the budget only when the server has headroom. Raising the cap indefinitely can hide a leak; an overly low high threshold can also cause severe slowdown without killing the service.

Interpret a slow service before raising its cap

If requests become slow while the service remains active and the high counter keeps increasing, the pressure threshold is a useful lead. If the journal instead reports an OOM kill, examine the hard limit and the application’s memory demand. A single old counter value does not tell you whether pressure is happening now; compare readings during the operation that is actually slow.

After adjusting the policy, repeat the ordinary application operation used for the initial check. The goal is useful work within a deliberate budget, not merely a green service status or counters that happen to be zero. Keep the recorded previous settings so rollback restores the intended configuration rather than an assumed unlimited default.

5. Remove only this policy if needed

To undo the example, move its drop-in outside the service’s override directory. Choose an unused destination so an earlier backup is not overwritten:

$ sudo mv /etc/systemd/system/myapp.service.d/90-memory.conf /etc/systemd/system/myapp.memory.conf.disabled
$ sudo systemctl daemon-reload
$ sudo systemctl restart myapp.service
$ sudo systemctl show myapp.service -p MemoryHigh -p MemoryMax -p MemorySwapMax

Use your chosen filenames if you changed them earlier. Other drop-ins remain intact. The unit settings now come from the remaining configuration and manager defaults. Parent limits still apply, so rollback does not necessarily mean unlimited memory. Check normal application operation again.

Reference: systemd resource controls. Check the documentation for your installed version before applying changes.

Related guides

Account services are unavailable until the production backend is configured.