When working with systemd services, traditional ulimit
approaches often prove insufficient because:
- Systemd launches processes in its own execution context
- Child processes may inherit or override limits
- OOM killer behavior is unpredictable
The modern solution lies in systemd's integration with cgroups. Add these directives to your service unit file (/etc/systemd/system/your-service.service
):
[Service]
MemoryMax=500M
MemoryHigh=450M
MemorySwapMax=100M
Key parameters explanation:
Directive | Effect |
---|---|
MemoryMax | Hard limit - triggers OOM killer when exceeded |
MemoryHigh | Soft limit - throttles allocations when approached |
MemorySwapMax | Controls swap usage separately |
For a memory-intensive Python service, here's a complete unit file:
[Unit]
Description=Memory-limited data processor
After=network.target
[Service]
Type=simple
User=appuser
ExecStart=/usr/bin/python3 /opt/app/main.py
MemoryMax=1G
MemoryHigh=900M
MemorySwapMax=200M
Restart=on-failure
[Install]
WantedBy=multi-user.target
For more granular control, consider these additional settings:
MemoryLimit=800M
AllowedCPUs=0-3
IOWeight=50
After modifying your service file:
sudo systemctl daemon-reload
sudo systemctl restart your-service
systemd-analyze verify your-service.service # Check for syntax errors
systemctl show your-service | grep Memory # View applied limits
Configure custom behavior when limits are hit:
[Service]
...
OOMPolicy=kill
OOMScoreAdjust=-100
For systems without recent systemd versions, manually configure via:
sudo cgcreate -g memory:/your-service
echo "500000000" > /sys/fs/cgroup/memory/your-service/memory.limit_in_bytes
echo "appuser" > /sys/fs/cgroup/memory/your-service/cgroup.procs
Remember that modern systemd versions (v230+) provide better integration than manual cgroup management.
When dealing with systemd services, traditional ulimit
approaches often fall short because:
- Systemd launches processes in its own context
- Child processes may inherit different limits
- OOM killer behavior is unpredictable
The modern solution uses cgroups via systemd's native directives. Add these to your service unit file (/etc/systemd/system/your-service.service
):
[Service]
MemoryMax=500M
MemoryHigh=450M
MemorySwapMax=100M
Key parameters:
MemoryMax
: Hard limit (process gets killed when exceeded)MemoryHigh
: Soft limit (throttling occurs)MemorySwapMax
: Swap space limitation
For a Node.js application running as a systemd service:
[Unit]
Description=Node.js Memory Limited Service
[Service]
ExecStart=/usr/bin/node /opt/app/server.js
User=appuser
Group=appgroup
MemoryMax=1.5G
MemoryHigh=1.2G
MemorySwapMax=0
Restart=on-failure
[Install]
WantedBy=multi-user.target
After applying changes (systemctl daemon-reload
and systemctl restart your-service
), verify with:
systemd-cgtop -m
# Or for specific service:
systemctl show your-service -p MemoryCurrent,MemoryMax
For more granular control, create a slice with memory limits:
[Unit]
Description=Memory Limited Slice
[Slice]
MemoryMax=2G
MemoryAccounting=yes
Then assign your service to this slice:
[Service]
Slice=memory-limited.slice
To make your application properly handle memory limits:
// C/C++ example
void* safe_malloc(size_t size) {
void* ptr = malloc(size);
if (!ptr) {
syslog(LOG_ERR, "Memory allocation failed");
// Handle error or exit gracefully
}
return ptr;
}