memory.swap.max=0 on a database is often correct, but on a stateless web worker it turns manageable pressure into a hard kill.systemd-cgls.memory.limit_in_bytes, tasks or cgroup.clone_children is cgroups v1 and will not work on a modern VPS.Where this fits
cgroup memory limits do not make a workload fit in less RAM. They make the failure mode predictable: instead of a global OOM that takes out MariaDB because it happened to be the largest RSS, you get a scoped kill of the process that actually misbehaved, and a counter in memory.events telling you it happened. On an oversubscribed VPS that is the difference between a degraded service and a dead one. Pair it with correct application-level sizing — see our PHP-FPM pool sizing guide — and you have a system that fails locally instead of globally.
memory.max. Without memory.high you lose the graceful throttle and go straight to a kill.memory.swap.max=0 on a database is often correct, but on a stateless web worker it turns manageable pressure into a hard kill.systemd-cgls.memory.limit_in_bytes, tasks or cgroup.clone_children is cgroups v1 and will not work on a modern VPS.Where this fits
cgroup memory limits do not make a workload fit in less RAM. They make the failure mode predictable: instead of a global OOM that takes out MariaDB because it happened to be the largest RSS, you get a scoped kill of the process that actually misbehaved, and a counter in memory.events telling you it happened. On an oversubscribed VPS that is the difference between a degraded service and a dead one. Pair it with correct application-level sizing — see our PHP-FPM pool sizing guide — and you have a system that fails locally instead of globally.
memory.max. Without memory.high you lose the graceful throttle and go straight to a kill.memory.swap.max=0 on a database is often correct, but on a stateless web worker it turns manageable pressure into a hard kill.systemd-cgls.memory.limit_in_bytes, tasks or cgroup.clone_children is cgroups v1 and will not work on a modern VPS.Where this fits
cgroup memory limits do not make a workload fit in less RAM. They make the failure mode predictable: instead of a global OOM that takes out MariaDB because it happened to be the largest RSS, you get a scoped kill of the process that actually misbehaved, and a counter in memory.events telling you it happened. On an oversubscribed VPS that is the difference between a degraded service and a dead one. Pair it with correct application-level sizing — see our PHP-FPM pool sizing guide — and you have a system that fails locally instead of globally.
- Assuming
memory.maxcaps RSS. It caps the cgroup’s total memory including page cache charged to it. A process reading a 300 MB file may hit its limit with a small RSS. - Setting only
memory.max. Withoutmemory.highyou lose the graceful throttle and go straight to a kill. - Forgetting swap.
memory.swap.max=0on a database is often correct, but on a stateless web worker it turns manageable pressure into a hard kill. - Ignoring nested cgroups. A limit on a parent slice and a child unit both apply; the more restrictive one wins. Check the full path with
systemd-cgls. - Trusting old guides. Anything referencing
memory.limit_in_bytes,tasksorcgroup.clone_childrenis cgroups v1 and will not work on a modern VPS.
Where this fits
cgroup memory limits do not make a workload fit in less RAM. They make the failure mode predictable: instead of a global OOM that takes out MariaDB because it happened to be the largest RSS, you get a scoped kill of the process that actually misbehaved, and a counter in memory.events telling you it happened. On an oversubscribed VPS that is the difference between a degraded service and a dead one. Pair it with correct application-level sizing — see our PHP-FPM pool sizing guide — and you have a system that fails locally instead of globally.
memory.swap.max=0 on a database is often correct, but on a stateless web worker it turns manageable pressure into a hard kill.systemd-cgls.memory.limit_in_bytes, tasks or cgroup.clone_children is cgroups v1 and will not work on a modern VPS.Where this fits
cgroup memory limits do not make a workload fit in less RAM. They make the failure mode predictable: instead of a global OOM that takes out MariaDB because it happened to be the largest RSS, you get a scoped kill of the process that actually misbehaved, and a counter in memory.events telling you it happened. On an oversubscribed VPS that is the difference between a degraded service and a dead one. Pair it with correct application-level sizing — see our PHP-FPM pool sizing guide — and you have a system that fails locally instead of globally.
- Assuming
memory.maxcaps RSS. It caps the cgroup’s total memory including page cache charged to it. A process reading a 300 MB file may hit its limit with a small RSS. - Setting only
memory.max. Withoutmemory.highyou lose the graceful throttle and go straight to a kill. - Forgetting swap.
memory.swap.max=0on a database is often correct, but on a stateless web worker it turns manageable pressure into a hard kill. - Ignoring nested cgroups. A limit on a parent slice and a child unit both apply; the more restrictive one wins. Check the full path with
systemd-cgls. - Trusting old guides. Anything referencing
memory.limit_in_bytes,tasksorcgroup.clone_childrenis cgroups v1 and will not work on a modern VPS.
Where this fits
cgroup memory limits do not make a workload fit in less RAM. They make the failure mode predictable: instead of a global OOM that takes out MariaDB because it happened to be the largest RSS, you get a scoped kill of the process that actually misbehaved, and a counter in memory.events telling you it happened. On an oversubscribed VPS that is the difference between a degraded service and a dead one. Pair it with correct application-level sizing — see our PHP-FPM pool sizing guide — and you have a system that fails locally instead of globally.
memory.swap.max=0 on a database is often correct, but on a stateless web worker it turns manageable pressure into a hard kill.systemd-cgls.memory.limit_in_bytes, tasks or cgroup.clone_children is cgroups v1 and will not work on a modern VPS.Where this fits
cgroup memory limits do not make a workload fit in less RAM. They make the failure mode predictable: instead of a global OOM that takes out MariaDB because it happened to be the largest RSS, you get a scoped kill of the process that actually misbehaved, and a counter in memory.events telling you it happened. On an oversubscribed VPS that is the difference between a degraded service and a dead one. Pair it with correct application-level sizing — see our PHP-FPM pool sizing guide — and you have a system that fails locally instead of globally.
- Assuming
memory.maxcaps RSS. It caps the cgroup’s total memory including page cache charged to it. A process reading a 300 MB file may hit its limit with a small RSS. - Setting only
memory.max. Withoutmemory.highyou lose the graceful throttle and go straight to a kill. - Forgetting swap.
memory.swap.max=0on a database is often correct, but on a stateless web worker it turns manageable pressure into a hard kill. - Ignoring nested cgroups. A limit on a parent slice and a child unit both apply; the more restrictive one wins. Check the full path with
systemd-cgls. - Trusting old guides. Anything referencing
memory.limit_in_bytes,tasksorcgroup.clone_childrenis cgroups v1 and will not work on a modern VPS.
Where this fits
cgroup memory limits do not make a workload fit in less RAM. They make the failure mode predictable: instead of a global OOM that takes out MariaDB because it happened to be the largest RSS, you get a scoped kill of the process that actually misbehaved, and a counter in memory.events telling you it happened. On an oversubscribed VPS that is the difference between a degraded service and a dead one. Pair it with correct application-level sizing — see our PHP-FPM pool sizing guide — and you have a system that fails locally instead of globally.
memory.max. Without memory.high you lose the graceful throttle and go straight to a kill.memory.swap.max=0 on a database is often correct, but on a stateless web worker it turns manageable pressure into a hard kill.systemd-cgls.memory.limit_in_bytes, tasks or cgroup.clone_children is cgroups v1 and will not work on a modern VPS.Where this fits
cgroup memory limits do not make a workload fit in less RAM. They make the failure mode predictable: instead of a global OOM that takes out MariaDB because it happened to be the largest RSS, you get a scoped kill of the process that actually misbehaved, and a counter in memory.events telling you it happened. On an oversubscribed VPS that is the difference between a degraded service and a dead one. Pair it with correct application-level sizing — see our PHP-FPM pool sizing guide — and you have a system that fails locally instead of globally.
- Assuming
memory.maxcaps RSS. It caps the cgroup’s total memory including page cache charged to it. A process reading a 300 MB file may hit its limit with a small RSS. - Setting only
memory.max. Withoutmemory.highyou lose the graceful throttle and go straight to a kill. - Forgetting swap.
memory.swap.max=0on a database is often correct, but on a stateless web worker it turns manageable pressure into a hard kill. - Ignoring nested cgroups. A limit on a parent slice and a child unit both apply; the more restrictive one wins. Check the full path with
systemd-cgls. - Trusting old guides. Anything referencing
memory.limit_in_bytes,tasksorcgroup.clone_childrenis cgroups v1 and will not work on a modern VPS.
Where this fits
cgroup memory limits do not make a workload fit in less RAM. They make the failure mode predictable: instead of a global OOM that takes out MariaDB because it happened to be the largest RSS, you get a scoped kill of the process that actually misbehaved, and a counter in memory.events telling you it happened. On an oversubscribed VPS that is the difference between a degraded service and a dead one. Pair it with correct application-level sizing — see our PHP-FPM pool sizing guide — and you have a system that fails locally instead of globally.
memory.max. Without memory.high you lose the graceful throttle and go straight to a kill.memory.swap.max=0 on a database is often correct, but on a stateless web worker it turns manageable pressure into a hard kill.systemd-cgls.memory.limit_in_bytes, tasks or cgroup.clone_children is cgroups v1 and will not work on a modern VPS.Where this fits
cgroup memory limits do not make a workload fit in less RAM. They make the failure mode predictable: instead of a global OOM that takes out MariaDB because it happened to be the largest RSS, you get a scoped kill of the process that actually misbehaved, and a counter in memory.events telling you it happened. On an oversubscribed VPS that is the difference between a degraded service and a dead one. Pair it with correct application-level sizing — see our PHP-FPM pool sizing guide — and you have a system that fails locally instead of globally.
- Assuming
memory.maxcaps RSS. It caps the cgroup’s total memory including page cache charged to it. A process reading a 300 MB file may hit its limit with a small RSS. - Setting only
memory.max. Withoutmemory.highyou lose the graceful throttle and go straight to a kill. - Forgetting swap.
memory.swap.max=0on a database is often correct, but on a stateless web worker it turns manageable pressure into a hard kill. - Ignoring nested cgroups. A limit on a parent slice and a child unit both apply; the more restrictive one wins. Check the full path with
systemd-cgls. - Trusting old guides. Anything referencing
memory.limit_in_bytes,tasksorcgroup.clone_childrenis cgroups v1 and will not work on a modern VPS.
Where this fits
cgroup memory limits do not make a workload fit in less RAM. They make the failure mode predictable: instead of a global OOM that takes out MariaDB because it happened to be the largest RSS, you get a scoped kill of the process that actually misbehaved, and a counter in memory.events telling you it happened. On an oversubscribed VPS that is the difference between a degraded service and a dead one. Pair it with correct application-level sizing — see our PHP-FPM pool sizing guide — and you have a system that fails locally instead of globally.
memory.swap.max=0 on a database is often correct, but on a stateless web worker it turns manageable pressure into a hard kill.systemd-cgls.memory.limit_in_bytes, tasks or cgroup.clone_children is cgroups v1 and will not work on a modern VPS.Where this fits
cgroup memory limits do not make a workload fit in less RAM. They make the failure mode predictable: instead of a global OOM that takes out MariaDB because it happened to be the largest RSS, you get a scoped kill of the process that actually misbehaved, and a counter in memory.events telling you it happened. On an oversubscribed VPS that is the difference between a degraded service and a dead one. Pair it with correct application-level sizing — see our PHP-FPM pool sizing guide — and you have a system that fails locally instead of globally.
memory.max. Without memory.high you lose the graceful throttle and go straight to a kill.memory.swap.max=0 on a database is often correct, but on a stateless web worker it turns manageable pressure into a hard kill.systemd-cgls.memory.limit_in_bytes, tasks or cgroup.clone_children is cgroups v1 and will not work on a modern VPS.Where this fits
cgroup memory limits do not make a workload fit in less RAM. They make the failure mode predictable: instead of a global OOM that takes out MariaDB because it happened to be the largest RSS, you get a scoped kill of the process that actually misbehaved, and a counter in memory.events telling you it happened. On an oversubscribed VPS that is the difference between a degraded service and a dead one. Pair it with correct application-level sizing — see our PHP-FPM pool sizing guide — and you have a system that fails locally instead of globally.
- Assuming
memory.maxcaps RSS. It caps the cgroup’s total memory including page cache charged to it. A process reading a 300 MB file may hit its limit with a small RSS. - Setting only
memory.max. Withoutmemory.highyou lose the graceful throttle and go straight to a kill. - Forgetting swap.
memory.swap.max=0on a database is often correct, but on a stateless web worker it turns manageable pressure into a hard kill. - Ignoring nested cgroups. A limit on a parent slice and a child unit both apply; the more restrictive one wins. Check the full path with
systemd-cgls. - Trusting old guides. Anything referencing
memory.limit_in_bytes,tasksorcgroup.clone_childrenis cgroups v1 and will not work on a modern VPS.
Where this fits
cgroup memory limits do not make a workload fit in less RAM. They make the failure mode predictable: instead of a global OOM that takes out MariaDB because it happened to be the largest RSS, you get a scoped kill of the process that actually misbehaved, and a counter in memory.events telling you it happened. On an oversubscribed VPS that is the difference between a degraded service and a dead one. Pair it with correct application-level sizing — see our PHP-FPM pool sizing guide — and you have a system that fails locally instead of globally.
memory.swap.max=0 on a database is often correct, but on a stateless web worker it turns manageable pressure into a hard kill.systemd-cgls.memory.limit_in_bytes, tasks or cgroup.clone_children is cgroups v1 and will not work on a modern VPS.Where this fits
cgroup memory limits do not make a workload fit in less RAM. They make the failure mode predictable: instead of a global OOM that takes out MariaDB because it happened to be the largest RSS, you get a scoped kill of the process that actually misbehaved, and a counter in memory.events telling you it happened. On an oversubscribed VPS that is the difference between a degraded service and a dead one. Pair it with correct application-level sizing — see our PHP-FPM pool sizing guide — and you have a system that fails locally instead of globally.
memory.max. Without memory.high you lose the graceful throttle and go straight to a kill.memory.swap.max=0 on a database is often correct, but on a stateless web worker it turns manageable pressure into a hard kill.systemd-cgls.memory.limit_in_bytes, tasks or cgroup.clone_children is cgroups v1 and will not work on a modern VPS.Where this fits
cgroup memory limits do not make a workload fit in less RAM. They make the failure mode predictable: instead of a global OOM that takes out MariaDB because it happened to be the largest RSS, you get a scoped kill of the process that actually misbehaved, and a counter in memory.events telling you it happened. On an oversubscribed VPS that is the difference between a degraded service and a dead one. Pair it with correct application-level sizing — see our PHP-FPM pool sizing guide — and you have a system that fails locally instead of globally.
- Assuming
memory.maxcaps RSS. It caps the cgroup’s total memory including page cache charged to it. A process reading a 300 MB file may hit its limit with a small RSS. - Setting only
memory.max. Withoutmemory.highyou lose the graceful throttle and go straight to a kill. - Forgetting swap.
memory.swap.max=0on a database is often correct, but on a stateless web worker it turns manageable pressure into a hard kill. - Ignoring nested cgroups. A limit on a parent slice and a child unit both apply; the more restrictive one wins. Check the full path with
systemd-cgls. - Trusting old guides. Anything referencing
memory.limit_in_bytes,tasksorcgroup.clone_childrenis cgroups v1 and will not work on a modern VPS.
Where this fits
cgroup memory limits do not make a workload fit in less RAM. They make the failure mode predictable: instead of a global OOM that takes out MariaDB because it happened to be the largest RSS, you get a scoped kill of the process that actually misbehaved, and a counter in memory.events telling you it happened. On an oversubscribed VPS that is the difference between a degraded service and a dead one. Pair it with correct application-level sizing — see our PHP-FPM pool sizing guide — and you have a system that fails locally instead of globally.
memory.max. Without memory.high you lose the graceful throttle and go straight to a kill.memory.swap.max=0 on a database is often correct, but on a stateless web worker it turns manageable pressure into a hard kill.systemd-cgls.memory.limit_in_bytes, tasks or cgroup.clone_children is cgroups v1 and will not work on a modern VPS.Where this fits
cgroup memory limits do not make a workload fit in less RAM. They make the failure mode predictable: instead of a global OOM that takes out MariaDB because it happened to be the largest RSS, you get a scoped kill of the process that actually misbehaved, and a counter in memory.events telling you it happened. On an oversubscribed VPS that is the difference between a degraded service and a dead one. Pair it with correct application-level sizing — see our PHP-FPM pool sizing guide — and you have a system that fails locally instead of globally.
memory.max. Without memory.high you lose the graceful throttle and go straight to a kill.memory.swap.max=0 on a database is often correct, but on a stateless web worker it turns manageable pressure into a hard kill.systemd-cgls.memory.limit_in_bytes, tasks or cgroup.clone_children is cgroups v1 and will not work on a modern VPS.Where this fits
cgroup memory limits do not make a workload fit in less RAM. They make the failure mode predictable: instead of a global OOM that takes out MariaDB because it happened to be the largest RSS, you get a scoped kill of the process that actually misbehaved, and a counter in memory.events telling you it happened. On an oversubscribed VPS that is the difference between a degraded service and a dead one. Pair it with correct application-level sizing — see our PHP-FPM pool sizing guide — and you have a system that fails locally instead of globally.
- Assuming
memory.maxcaps RSS. It caps the cgroup’s total memory including page cache charged to it. A process reading a 300 MB file may hit its limit with a small RSS. - Setting only
memory.max. Withoutmemory.highyou lose the graceful throttle and go straight to a kill. - Forgetting swap.
memory.swap.max=0on a database is often correct, but on a stateless web worker it turns manageable pressure into a hard kill. - Ignoring nested cgroups. A limit on a parent slice and a child unit both apply; the more restrictive one wins. Check the full path with
systemd-cgls. - Trusting old guides. Anything referencing
memory.limit_in_bytes,tasksorcgroup.clone_childrenis cgroups v1 and will not work on a modern VPS.
Where this fits
cgroup memory limits do not make a workload fit in less RAM. They make the failure mode predictable: instead of a global OOM that takes out MariaDB because it happened to be the largest RSS, you get a scoped kill of the process that actually misbehaved, and a counter in memory.events telling you it happened. On an oversubscribed VPS that is the difference between a degraded service and a dead one. Pair it with correct application-level sizing — see our PHP-FPM pool sizing guide — and you have a system that fails locally instead of globally.
memory.swap.max=0 on a database is often correct, but on a stateless web worker it turns manageable pressure into a hard kill.systemd-cgls.memory.limit_in_bytes, tasks or cgroup.clone_children is cgroups v1 and will not work on a modern VPS.Where this fits
cgroup memory limits do not make a workload fit in less RAM. They make the failure mode predictable: instead of a global OOM that takes out MariaDB because it happened to be the largest RSS, you get a scoped kill of the process that actually misbehaved, and a counter in memory.events telling you it happened. On an oversubscribed VPS that is the difference between a degraded service and a dead one. Pair it with correct application-level sizing — see our PHP-FPM pool sizing guide — and you have a system that fails locally instead of globally.
memory.max. Without memory.high you lose the graceful throttle and go straight to a kill.memory.swap.max=0 on a database is often correct, but on a stateless web worker it turns manageable pressure into a hard kill.systemd-cgls.memory.limit_in_bytes, tasks or cgroup.clone_children is cgroups v1 and will not work on a modern VPS.Where this fits
cgroup memory limits do not make a workload fit in less RAM. They make the failure mode predictable: instead of a global OOM that takes out MariaDB because it happened to be the largest RSS, you get a scoped kill of the process that actually misbehaved, and a counter in memory.events telling you it happened. On an oversubscribed VPS that is the difference between a degraded service and a dead one. Pair it with correct application-level sizing — see our PHP-FPM pool sizing guide — and you have a system that fails locally instead of globally.
memory.max. Without memory.high you lose the graceful throttle and go straight to a kill.memory.swap.max=0 on a database is often correct, but on a stateless web worker it turns manageable pressure into a hard kill.systemd-cgls.memory.limit_in_bytes, tasks or cgroup.clone_children is cgroups v1 and will not work on a modern VPS.Where this fits
cgroup memory limits do not make a workload fit in less RAM. They make the failure mode predictable: instead of a global OOM that takes out MariaDB because it happened to be the largest RSS, you get a scoped kill of the process that actually misbehaved, and a counter in memory.events telling you it happened. On an oversubscribed VPS that is the difference between a degraded service and a dead one. Pair it with correct application-level sizing — see our PHP-FPM pool sizing guide — and you have a system that fails locally instead of globally.
- Assuming
memory.maxcaps RSS. It caps the cgroup’s total memory including page cache charged to it. A process reading a 300 MB file may hit its limit with a small RSS. - Setting only
memory.max. Withoutmemory.highyou lose the graceful throttle and go straight to a kill. - Forgetting swap.
memory.swap.max=0on a database is often correct, but on a stateless web worker it turns manageable pressure into a hard kill. - Ignoring nested cgroups. A limit on a parent slice and a child unit both apply; the more restrictive one wins. Check the full path with
systemd-cgls. - Trusting old guides. Anything referencing
memory.limit_in_bytes,tasksorcgroup.clone_childrenis cgroups v1 and will not work on a modern VPS.
Where this fits
cgroup memory limits do not make a workload fit in less RAM. They make the failure mode predictable: instead of a global OOM that takes out MariaDB because it happened to be the largest RSS, you get a scoped kill of the process that actually misbehaved, and a counter in memory.events telling you it happened. On an oversubscribed VPS that is the difference between a degraded service and a dead one. Pair it with correct application-level sizing — see our PHP-FPM pool sizing guide — and you have a system that fails locally instead of globally.
memory.swap.max=0 on a database is often correct, but on a stateless web worker it turns manageable pressure into a hard kill.systemd-cgls.memory.limit_in_bytes, tasks or cgroup.clone_children is cgroups v1 and will not work on a modern VPS.Where this fits
cgroup memory limits do not make a workload fit in less RAM. They make the failure mode predictable: instead of a global OOM that takes out MariaDB because it happened to be the largest RSS, you get a scoped kill of the process that actually misbehaved, and a counter in memory.events telling you it happened. On an oversubscribed VPS that is the difference between a degraded service and a dead one. Pair it with correct application-level sizing — see our PHP-FPM pool sizing guide — and you have a system that fails locally instead of globally.
memory.max. Without memory.high you lose the graceful throttle and go straight to a kill.memory.swap.max=0 on a database is often correct, but on a stateless web worker it turns manageable pressure into a hard kill.systemd-cgls.memory.limit_in_bytes, tasks or cgroup.clone_children is cgroups v1 and will not work on a modern VPS.Where this fits
cgroup memory limits do not make a workload fit in less RAM. They make the failure mode predictable: instead of a global OOM that takes out MariaDB because it happened to be the largest RSS, you get a scoped kill of the process that actually misbehaved, and a counter in memory.events telling you it happened. On an oversubscribed VPS that is the difference between a degraded service and a dead one. Pair it with correct application-level sizing — see our PHP-FPM pool sizing guide — and you have a system that fails locally instead of globally.
memory.max. Without memory.high you lose the graceful throttle and go straight to a kill.memory.swap.max=0 on a database is often correct, but on a stateless web worker it turns manageable pressure into a hard kill.systemd-cgls.memory.limit_in_bytes, tasks or cgroup.clone_children is cgroups v1 and will not work on a modern VPS.Where this fits
cgroup memory limits do not make a workload fit in less RAM. They make the failure mode predictable: instead of a global OOM that takes out MariaDB because it happened to be the largest RSS, you get a scoped kill of the process that actually misbehaved, and a counter in memory.events telling you it happened. On an oversubscribed VPS that is the difference between a degraded service and a dead one. Pair it with correct application-level sizing — see our PHP-FPM pool sizing guide — and you have a system that fails locally instead of globally.
- Assuming
memory.maxcaps RSS. It caps the cgroup’s total memory including page cache charged to it. A process reading a 300 MB file may hit its limit with a small RSS. - Setting only
memory.max. Withoutmemory.highyou lose the graceful throttle and go straight to a kill. - Forgetting swap.
memory.swap.max=0on a database is often correct, but on a stateless web worker it turns manageable pressure into a hard kill. - Ignoring nested cgroups. A limit on a parent slice and a child unit both apply; the more restrictive one wins. Check the full path with
systemd-cgls. - Trusting old guides. Anything referencing
memory.limit_in_bytes,tasksorcgroup.clone_childrenis cgroups v1 and will not work on a modern VPS.
Where this fits
cgroup memory limits do not make a workload fit in less RAM. They make the failure mode predictable: instead of a global OOM that takes out MariaDB because it happened to be the largest RSS, you get a scoped kill of the process that actually misbehaved, and a counter in memory.events telling you it happened. On an oversubscribed VPS that is the difference between a degraded service and a dead one. Pair it with correct application-level sizing — see our PHP-FPM pool sizing guide — and you have a system that fails locally instead of globally.
memory.swap.max=0 on a database is often correct, but on a stateless web worker it turns manageable pressure into a hard kill.systemd-cgls.memory.limit_in_bytes, tasks or cgroup.clone_children is cgroups v1 and will not work on a modern VPS.Where this fits
cgroup memory limits do not make a workload fit in less RAM. They make the failure mode predictable: instead of a global OOM that takes out MariaDB because it happened to be the largest RSS, you get a scoped kill of the process that actually misbehaved, and a counter in memory.events telling you it happened. On an oversubscribed VPS that is the difference between a degraded service and a dead one. Pair it with correct application-level sizing — see our PHP-FPM pool sizing guide — and you have a system that fails locally instead of globally.
memory.swap.max=0 on a database is often correct, but on a stateless web worker it turns manageable pressure into a hard kill.systemd-cgls.memory.limit_in_bytes, tasks or cgroup.clone_children is cgroups v1 and will not work on a modern VPS.Where this fits
cgroup memory limits do not make a workload fit in less RAM. They make the failure mode predictable: instead of a global OOM that takes out MariaDB because it happened to be the largest RSS, you get a scoped kill of the process that actually misbehaved, and a counter in memory.events telling you it happened. On an oversubscribed VPS that is the difference between a degraded service and a dead one. Pair it with correct application-level sizing — see our PHP-FPM pool sizing guide — and you have a system that fails locally instead of globally.
memory.max. Without memory.high you lose the graceful throttle and go straight to a kill.memory.swap.max=0 on a database is often correct, but on a stateless web worker it turns manageable pressure into a hard kill.systemd-cgls.memory.limit_in_bytes, tasks or cgroup.clone_children is cgroups v1 and will not work on a modern VPS.Where this fits
cgroup memory limits do not make a workload fit in less RAM. They make the failure mode predictable: instead of a global OOM that takes out MariaDB because it happened to be the largest RSS, you get a scoped kill of the process that actually misbehaved, and a counter in memory.events telling you it happened. On an oversubscribed VPS that is the difference between a degraded service and a dead one. Pair it with correct application-level sizing — see our PHP-FPM pool sizing guide — and you have a system that fails locally instead of globally.
memory.max. Without memory.high you lose the graceful throttle and go straight to a kill.memory.swap.max=0 on a database is often correct, but on a stateless web worker it turns manageable pressure into a hard kill.systemd-cgls.memory.limit_in_bytes, tasks or cgroup.clone_children is cgroups v1 and will not work on a modern VPS.Where this fits
cgroup memory limits do not make a workload fit in less RAM. They make the failure mode predictable: instead of a global OOM that takes out MariaDB because it happened to be the largest RSS, you get a scoped kill of the process that actually misbehaved, and a counter in memory.events telling you it happened. On an oversubscribed VPS that is the difference between a degraded service and a dead one. Pair it with correct application-level sizing — see our PHP-FPM pool sizing guide — and you have a system that fails locally instead of globally.
- Assuming
memory.maxcaps RSS. It caps the cgroup’s total memory including page cache charged to it. A process reading a 300 MB file may hit its limit with a small RSS. - Setting only
memory.max. Withoutmemory.highyou lose the graceful throttle and go straight to a kill. - Forgetting swap.
memory.swap.max=0on a database is often correct, but on a stateless web worker it turns manageable pressure into a hard kill. - Ignoring nested cgroups. A limit on a parent slice and a child unit both apply; the more restrictive one wins. Check the full path with
systemd-cgls. - Trusting old guides. Anything referencing
memory.limit_in_bytes,tasksorcgroup.clone_childrenis cgroups v1 and will not work on a modern VPS.
Where this fits
cgroup memory limits do not make a workload fit in less RAM. They make the failure mode predictable: instead of a global OOM that takes out MariaDB because it happened to be the largest RSS, you get a scoped kill of the process that actually misbehaved, and a counter in memory.events telling you it happened. On an oversubscribed VPS that is the difference between a degraded service and a dead one. Pair it with correct application-level sizing — see our PHP-FPM pool sizing guide — and you have a system that fails locally instead of globally.
On a VPS with 1 GB of RAM you have two blunt instruments for memory pressure: point your applications at sane limits and hope, or let the kernel OOM killer decide. Neither is satisfying, and the OOM killer’s choice is frequently not the process you would have picked. cgroups v2 gives you a third option — a hard, enforceable ceiling on any process tree, with the OOM event delivered to that cgroup instead of the whole system. This article covers setting that up from scratch on a Debian 12 / Ubuntu 24.04 VPS, and the differences from cgroups v1 that catch people out.
Before capping anything, it helps to know what your workload’s real working set is. Our guide to reading load average versus actual CPU saturation explains why memory pressure and load average tell different stories on a shared VPS.
Confirm you are on cgroups v2
stat -fc %T /sys/fs/cgroup
cgroup2fs means unified v2. tmpfs means v1 and this article’s paths do not apply. Check the controller list:
cat /sys/fs/cgroup/cgroup.controllers
You should see at least cpu io memory pids. On Ubuntu the memory controller is available at the root but must be enabled in child cgroups by writing to cgroup.subtree_control. Debian 12 with a recent kernel behaves the same.
The three memory knobs that matter
| File | Type | Behaviour | Typical use |
|---|---|---|---|
memory.max | Hard limit | Allocation triggers reclaim; if reclaim fails, cgroup OOM killer fires | Hard ceiling for an app that must not starve the host |
memory.high | Soft limit | Throttles allocation and reclaims aggressively, no OOM | Preferred: gives the app a chance to shed load |
memory.min | Protection | Pages here are never reclaimed under pressure | Protect a critical daemon in a shared cgroup tree |
memory.swap.max | Hard limit | Caps swap the cgroup may use | Stop one service from swapping the whole box |
memory.low | Protection (soft) | Reclaim avoids this range where possible | Softer alternative to memory.min |
memory.oom.group | Toggle | Kill the entire cgroup instead of one process | Multi-process apps that corrupt state if half-killed |
The practical advice: set memory.high at roughly 90% of your intended ceiling and memory.max at 100%. The high watermark throttles and reclaims gradually, which most applications survive; the max watermark is the backstop that prevents a runaway from consuming the host.
Cap a service manually with systemd
systemd already creates a cgroup per unit and exposes the controls as directives. This is the least error-prone route and needs no manual filesystem work. Create a drop-in:
systemctl edit php8.3-fpm.service
[Service]
MemoryHigh=460M
MemoryMax=512M
MemorySwapMax=128M
MemoryAccounting=yes
TasksMax=256
systemctl daemon-reload && systemctl restart php8.3-fpm.service
Verify:
systemctl show php8.3-fpm.service -p MemoryCurrent -p MemoryMax -p MemoryHigh
cat /sys/fs/cgroup/system.slice/php8.3-fpm.service/memory.current
Note that MemoryCurrent is in bytes and reports [not set] if MemoryAccounting= is off. Always set accounting explicitly.
Cap an arbitrary process tree with systemd-run
For something not managed by a unit — a batch job, a scraper, a build — wrap it:
systemd-run --scope --unit=batchjob --property=MemoryMax=384M --property=MemorySwapMax=0 \
/usr/local/bin/nightly-indexer.sh
--scope runs the process in the current session but still inside a cgroup, so the cap applies and it appears in systemd-cgtop. If the job exceeds 384 MB it is killed by its own cgroup OOM, and the rest of the VPS is unaffected. This is by far the simplest way to stop a one-off job from taking down your database.
Watching pressure and OOM events
Every cgroup exposes its own pressure stall information. For a cgroup at /sys/fs/cgroup/system.slice/php8.3-fpm.service/:
cat memory.pressure
cat memory.events
memory.events counts high, max, oom and oom_kill occurrences since boot. A rising high count with zero oom_kill is exactly what you want: the throttle is working. Non-zero oom_kill means the ceiling is genuinely too low for the workload, not that tuning is needed.
systemd-cgtop -m --order=memory gives a live ranked view, which is usually faster than reading the files by hand when you suspect a leak.
A worked oversubscription plan for a 1 GB VPS
Assume 1 GB total, ~120 MB consumed by the kernel and base system. That leaves roughly 880 MB to allocate. Deliberately oversubscribe the soft limits (they can borrow when idle) but keep the hard limits within the physical budget.
| Service | memory.high | memory.max | swap.max | Rationale |
|---|---|---|---|---|
| MariaDB | 320M | 380M | 64M | Buffer pool is fixed; small headroom for connections |
| PHP-FPM | 200M | 240M | 64M | Scales with children; throttle before OOM |
| Nginx | 24M | 48M | 0 | Tiny, should never swap |
| Redis | 64M | 96M | 0 | Set maxmemory in Redis too |
| System / sshd / cron | 96M | 128M | 32M | Protected with memory.min |
| Total hard max | — | 892M | 160M | Fits in RAM, swap is a cushion only |
Protect the system slice so SSH stays reachable even when MariaDB is reclaiming hard:
echo 67108864 > /sys/fs/cgroup/system.slice/memory.min
Persist that with a drop-in on system.slice or, more practically, accept the systemd defaults and instead make sure your application limits are the ones being hit first. Setting a memory.min on the system slice is a blunt safety net and worth doing once you have been OOM-killed out of an SSH session during an incident.
Common mistakes
systemd-cgls.memory.limit_in_bytes, tasks or cgroup.clone_children is cgroups v1 and will not work on a modern VPS.Where this fits
cgroup memory limits do not make a workload fit in less RAM. They make the failure mode predictable: instead of a global OOM that takes out MariaDB because it happened to be the largest RSS, you get a scoped kill of the process that actually misbehaved, and a counter in memory.events telling you it happened. On an oversubscribed VPS that is the difference between a degraded service and a dead one. Pair it with correct application-level sizing — see our PHP-FPM pool sizing guide — and you have a system that fails locally instead of globally.
memory.swap.max=0 on a database is often correct, but on a stateless web worker it turns manageable pressure into a hard kill.systemd-cgls.memory.limit_in_bytes, tasks or cgroup.clone_children is cgroups v1 and will not work on a modern VPS.Where this fits
cgroup memory limits do not make a workload fit in less RAM. They make the failure mode predictable: instead of a global OOM that takes out MariaDB because it happened to be the largest RSS, you get a scoped kill of the process that actually misbehaved, and a counter in memory.events telling you it happened. On an oversubscribed VPS that is the difference between a degraded service and a dead one. Pair it with correct application-level sizing — see our PHP-FPM pool sizing guide — and you have a system that fails locally instead of globally.
memory.swap.max=0 on a database is often correct, but on a stateless web worker it turns manageable pressure into a hard kill.systemd-cgls.memory.limit_in_bytes, tasks or cgroup.clone_children is cgroups v1 and will not work on a modern VPS.Where this fits
cgroup memory limits do not make a workload fit in less RAM. They make the failure mode predictable: instead of a global OOM that takes out MariaDB because it happened to be the largest RSS, you get a scoped kill of the process that actually misbehaved, and a counter in memory.events telling you it happened. On an oversubscribed VPS that is the difference between a degraded service and a dead one. Pair it with correct application-level sizing — see our PHP-FPM pool sizing guide — and you have a system that fails locally instead of globally.
memory.max. Without memory.high you lose the graceful throttle and go straight to a kill.memory.swap.max=0 on a database is often correct, but on a stateless web worker it turns manageable pressure into a hard kill.systemd-cgls.memory.limit_in_bytes, tasks or cgroup.clone_children is cgroups v1 and will not work on a modern VPS.Where this fits
cgroup memory limits do not make a workload fit in less RAM. They make the failure mode predictable: instead of a global OOM that takes out MariaDB because it happened to be the largest RSS, you get a scoped kill of the process that actually misbehaved, and a counter in memory.events telling you it happened. On an oversubscribed VPS that is the difference between a degraded service and a dead one. Pair it with correct application-level sizing — see our PHP-FPM pool sizing guide — and you have a system that fails locally instead of globally.
memory.max. Without memory.high you lose the graceful throttle and go straight to a kill.memory.swap.max=0 on a database is often correct, but on a stateless web worker it turns manageable pressure into a hard kill.systemd-cgls.memory.limit_in_bytes, tasks or cgroup.clone_children is cgroups v1 and will not work on a modern VPS.Where this fits
cgroup memory limits do not make a workload fit in less RAM. They make the failure mode predictable: instead of a global OOM that takes out MariaDB because it happened to be the largest RSS, you get a scoped kill of the process that actually misbehaved, and a counter in memory.events telling you it happened. On an oversubscribed VPS that is the difference between a degraded service and a dead one. Pair it with correct application-level sizing — see our PHP-FPM pool sizing guide — and you have a system that fails locally instead of globally.
- Assuming
memory.maxcaps RSS. It caps the cgroup’s total memory including page cache charged to it. A process reading a 300 MB file may hit its limit with a small RSS. - Setting only
memory.max. Withoutmemory.highyou lose the graceful throttle and go straight to a kill. - Forgetting swap.
memory.swap.max=0on a database is often correct, but on a stateless web worker it turns manageable pressure into a hard kill. - Ignoring nested cgroups. A limit on a parent slice and a child unit both apply; the more restrictive one wins. Check the full path with
systemd-cgls. - Trusting old guides. Anything referencing
memory.limit_in_bytes,tasksorcgroup.clone_childrenis cgroups v1 and will not work on a modern VPS.
Where this fits
cgroup memory limits do not make a workload fit in less RAM. They make the failure mode predictable: instead of a global OOM that takes out MariaDB because it happened to be the largest RSS, you get a scoped kill of the process that actually misbehaved, and a counter in memory.events telling you it happened. On an oversubscribed VPS that is the difference between a degraded service and a dead one. Pair it with correct application-level sizing — see our PHP-FPM pool sizing guide — and you have a system that fails locally instead of globally.



Leave a Reply
You must be logged in to post a comment.