# How the Linux Kernel's CFS Scheduler and OOM Killer Impact Containerized Applications

> Understand how the Linux CFS scheduler and OOM Killer affect containerized apps. Learn about cgroup tunables like cpu.shares and memory.limit_in_bytes for performance tuning and isolation.

- Repository: [Arie Bregman/devops-exercises](https://github.com/bregman-arie/devops-exercises)
- Tags: internals
- Published: 2026-02-28

---

**The Linux kernel's Completely Fair Scheduler (CFS) allocates CPU time to containers through cgroup tunables like `cpu.shares` and `cpu.cfs_quota_us`, while the Out-of-Memory (OOM) Killer terminates processes within memory-limited cgroups when `memory.limit_in_bytes` is breached, isolating failures to individual containers.**

When running production containerized workloads, understanding how the Linux kernel manages CPU scheduling and memory exhaustion is critical for stability. As documented in `bregman-arie/devops-exercises`—specifically in [`topics/linux/README.md`](https://github.com/bregman-arie/devops-exercises/blob/main/topics/linux/README.md), [`topics/kubernetes/README.md`](https://github.com/bregman-arie/devops-exercises/blob/main/topics/kubernetes/README.md), and [`topics/containers/README.md`](https://github.com/bregman-arie/devops-exercises/blob/main/topics/containers/README.md)—the CFS scheduler and OOM Killer provide the foundational resource isolation mechanisms that prevent container failures from cascading across host systems.

## Understanding CFS Scheduling for Containers

The Completely Fair Scheduler (CFS) maintains a **virtual runtime** for every runnable task, always selecting the task with the smallest virtual runtime to ensure equitable CPU distribution. While containers appear as isolated environments, their processes are standard Linux tasks grouped by cgroups, allowing CFS to apply resource controls at the group level rather than the individual process level.

### Cgroup CPU Controls: Shares vs. Quotas

The cgroup CPU controller exposes two primary mechanisms for controlling container CPU access:

- **`cpu.shares`**: Sets a relative weight for CPU time allocation. With a default value of 1024, a container configured with `cpu.shares=2048` receives approximately twice the CPU time of a container with default settings during contention. This creates **burstable** workloads that can utilize idle CPU but yield proportionally when the system is busy.

- **`cpu.cfs_quota_us` and `cpu.cfs_period_us`**: Establish hard limits on CPU consumption. Setting `cpu.cfs_quota_us` to 200000 with the default `cpu.cfs_period_us` of 100000 microseconds limits the container to **2 CPU cores** worth of time per period, regardless of system idle capacity.

### Impact on Workload Types

**Burstable applications** benefit from `cpu.shares`, scaling CPU usage dynamically with node availability. Conversely, **CPU-bound workloads** require strict quotas to prevent them from monopolizing processor time and starving neighboring containers. CFS also respects real-time scheduling parameters (`cpu.rt_runtime_us` and `cpu.rt_period_us`) for latency-sensitive containers requiring guaranteed execution windows.

## OOM Killer Behavior in Containerized Environments

The kernel's **Out-of-Memory (OOM) Killer** activates when the system cannot satisfy memory allocation requests, selecting victim processes based on an OOM score calculated from memory usage, process privileges, and `oom_score_adj` values. In containerized deployments, the memory cgroup controller modifies this behavior to provide isolation between container boundaries.

### Memory Cgroup Isolation Mechanisms

When a container exceeds its configured memory allocation, the kernel employs cgroup-specific controls before invoking the global OOM Killer:

- **`memory.limit_in_bytes`**: Defines a hard memory ceiling for the cgroup. When a container's total memory usage exceeds this limit, the kernel first attempts page eviction; if memory pressure persists, the OOM Killer selects and terminates a process **within that specific cgroup** rather than targeting system-wide processes.

- **`memory.oom_control`**: Provides visibility into OOM state through the `under_oom` flag, allowing monitoring systems to detect when specific containers experience memory pressure before process termination occurs.

### Configuring OOM Priority

The **`oom_score_adj`** parameter (range -1000 to +1000) influences which container processes are terminated first during memory exhaustion. As noted in [`topics/linux/README.md`](https://github.com/bregman-arie/devops-exercises/blob/main/topics/linux/README.md) within the `bregman-arie/devops-exercises` repository, setting negative values protects critical containers, while positive values designate sacrificial workloads that should be terminated before system-critical processes.

## Critical Tuning Parameters for Container Optimization

Effective container tuning requires adjusting both cgroup-specific controls and kernel-wide sysctls to align resource management with application requirements.

### CPU Scheduling Tuning

| Parameter | Default | Purpose | Configuration Method |
|-----------|---------|---------|---------------------|
| `cpu.shares` | 1024 | Relative CPU weight during contention | Docker: `--cpu-shares`, Kubernetes: `resources.requests.cpu` |
| `cpu.cfs_quota_us` | -1 (unlimited) | Hard CPU time limit per period | Docker: `--cpus`, Kubernetes: `resources.limits.cpu` |
| `cpu.cfs_period_us` | 100000 | Scheduling period duration (microseconds) | Container runtime or systemd slice configurations |

### Memory and OOM Control Parameters

| Parameter | Default | Purpose | Configuration Method |
|-----------|---------|---------|---------------------|
| `memory.limit_in_bytes` | Unlimited | Hard memory ceiling triggering cgroup OOM | Docker: `--memory`, Kubernetes: `resources.limits.memory` |
| `memory.soft_limit_in_bytes` | Unlimited | Soft limit triggering reclamation before hard OOM | Docker: `--memory-reservation`, Kubernetes: `resources.requests.memory` |
| `oom_score_adj` | 0 | Process priority adjustment for OOM selection | Docker: `--oom-score-adj`, Kubernetes: `securityContext.oomScoreAdj` |

### Kernel-Wide Sysctl Settings

As referenced in [`topics/linux/README.md`](https://github.com/bregman-arie/devops-exercises/blob/main/topics/linux/README.md), several kernel parameters affect container resource behavior:

- **`vm.overcommit_memory`**: Controls memory allocation policy. Setting to `2` disables overcommitment, forcing containers to respect strict memory accounting, while `0` (heuristic) allows flexible allocation suitable for development environments.

- **`vm.panic_on_oom`**: When set to `0` (default), the system invokes the OOM Killer; setting to `1` triggers a kernel panic on OOM events, which is generally unsuitable for production container hosts.

- **`kernel.sched_child_runs_first`**: Defaulting to `0`, this parameter influences whether child processes run before parents upon fork, potentially affecting container startup latency as noted in the repository's [`README.md`](https://github.com/bregman-arie/devops-exercises/blob/main/README.md).

## Translating Kubernetes Resources to Kernel Controls

Kubernetes abstracts cgroup configuration through resource specifications, mapping directly to the kernel parameters detailed above. The translation layer converts pod specifications to cgroup files:

1. **`resources.requests.cpu`** → `cpu.shares` (relative weight calculation)
2. **`resources.limits.cpu`** → `cpu.cfs_quota_us` / `cpu.cfs_period_us` ratio
3. **`resources.requests.memory`** → `memory.soft_limit_in_bytes`
4. **`resources.limits.memory`** → `memory.limit_in_bytes`

According to [`topics/kubernetes/README.md`](https://github.com/bregman-arie/devops-exercises/blob/main/topics/kubernetes/README.md), failing to define these requests and limits exposes clusters to host-wide OOM conditions, as unconstrained pods can exhaust node memory, triggering the global OOM Killer and destabilizing the entire Kubernetes node. The [`topics/kubernetes/CKA.md`](https://github.com/bregman-arie/devops-exercises/blob/main/topics/kubernetes/CKA.md) file further emphasizes that resource quotas rely on these underlying cgroup mechanisms to enforce limits.

## Practical Configuration Examples

### Docker Resource Constraints

Configure a web container with guaranteed CPU shares, hard limits, and OOM protection:

```bash
docker run -d \
  --name web \
  --cpus 0.5 \
  --cpu-shares 2048 \
  --memory 512m \
  --memory-reservation 256m \
  --oom-score-adj -500 \
  nginx

```

### Kubernetes Pod Specification

Define resource boundaries and OOM scoring in a pod manifest:

```yaml
apiVersion: v1
kind: Pod
metadata:
  name: data-processor
spec:
  containers:
  - name: worker
    image: myorg/processor:latest
    resources:
      requests:
        cpu: "250m"
        memory: "256Mi"
      limits:
        cpu: "500m"
        memory: "512Mi"
    securityContext:
      oomScoreAdj: -200

```

### Inspecting Active Container Cgroups

Verify that Docker and Kubernetes settings correctly propagated to the cgroup filesystem:

```bash

# Locate the cgroup path for a running container

CONTAINER_ID=$(docker ps -qf "name=web")
CGROUP=$(systemd-cgls | grep "$CONTAINER_ID" | head -1)

# Examine CFS configuration

cat /sys/fs/cgroup/cpu${CGROUP}/cpu.shares
cat /sys/fs/cgroup/cpu${CGROUP}/cpu.cfs_quota_us
cat /sys/fs/cgroup/cpu${CGROUP}/cpu.cfs_period_us

# Verify memory limits

cat /sys/fs/cgroup/memory${CGROUP}/memory.limit_in_bytes

```

### Kernel Parameter Tuning

Apply system-wide memory management policies suitable for containerized workloads:

```bash

# Disable memory overcommitment for predictable container limits

sudo sysctl -w vm.overcommit_memory=2

# Prevent kernel panic on OOM events

sudo sysctl -w vm.panic_on_oom=0

# Persist settings across reboots

echo "vm.overcommit_memory=2" >> /etc/sysctl.conf
echo "vm.panic_on_oom=0" >> /etc/sysctl.conf

```

## Summary

The Linux kernel's CFS scheduler and OOM Killer provide the foundational resource isolation mechanisms for containerized applications through the cgroup subsystem. Key takeaways include:

- **CFS Allocation**: The `cpu.shares` parameter provides proportional CPU distribution, while `cpu.cfs_quota_us` enforces absolute core limits.
- **Memory Isolation**: Setting `memory.limit_in_bytes` triggers cgroup-local OOM Killer behavior, preventing single container failures from destabilizing host systems.
- **OOM Prioritization**: The `oom_score_adj` parameter offers fine-grained control over termination order during memory pressure events.
- **Kubernetes Mapping**: Container orchestration platforms translate high-level resource requests and limits directly to these kernel cgroup controls, as documented in `bregman-arie/devops-exercises`.

## Frequently Asked Questions

### How does CFS differ from the OOM Killer in resource management?

**CFS (Completely Fair Scheduler)** manages CPU time distribution by maintaining virtual runtime metrics and selecting tasks with the smallest runtime values, operating continuously during normal system operation. **The OOM Killer** is an emergency mechanism triggered only when memory allocation requests cannot be satisfied, terminating processes to reclaim memory. While CFS throttles CPU usage through cgroup parameters like `cpu.cfs_quota_us`, the OOM Killer acts as a circuit breaker when `memory.limit_in_bytes` is exceeded.

### What happens when a container exceeds its memory limit in Kubernetes?

When a Kubernetes pod exceeds its `resources.limits.memory` value, the kernel writes the limit to `memory.limit_in_bytes` for the pod's cgroup. If the container continues allocating memory, the kernel first attempts page reclamation; if unsuccessful, the **OOM Killer terminates the largest process within that specific cgroup**. Kubernetes marks the pod as **Evicted** and may reschedule it, while other pods on the node continue operating unaffected.

### Why should vm.overcommit_memory be set to 2 for strict container environments?

Setting `vm.overcommit_memory=2` disables the kernel's heuristic memory overcommitment, meaning every `malloc()` call must have physical backing store available. This ensures that containers with defined `memory.limit_in_bytes` values cannot implicitly allocate beyond system capacity, preventing situations where the OOM Killer must activate. According to [`topics/linux/README.md`](https://github.com/bregman-arie/devops-exercises/blob/main/topics/linux/README.md) in the `bregman-arie/devops-exercises` repository, this setting provides predictable failure modes for memory-constrained containerized applications.

### How do cpu.shares and cpu.cfs_quota_us interact when both are set?

**`cpu.shares`** and **`cpu.cfs_quota_us`** operate independently: shares provide proportional weighting during CPU contention (e.g., 2048 shares receives twice the CPU time of 1024 shares when both containers want CPU), while quota establishes absolute consumption ceilings (e.g., 200000 microseconds per 100000 period equals 2 cores maximum). A container with high shares but low quota receives priority among competing processes but cannot exceed its quota-defined core limit, even on idle systems.