Skip to main content
unbound mongodb at scale

Container Constraints: MongoDB in Kubernetes

3 min read Chapter 64 of 72

Container Constraints

Running MongoDB in Kubernetes introduces constraints that do not exist on bare metal. The container runtime enforces CPU and memory limits. Kubernetes manages storage volumes. The scheduler decides where pods run. Each of these interactions can cause performance problems that look like MongoDB bugs but are actually infrastructure misconfigurations.

Container constraints diagram. Shows Kubernetes pod with MongoDB container. CPU limit triggers CFS throttling (latency spikes). Memory limit must align with WiredTiger cacheSizeGB. PersistentVolumeClaim backed by storage class determines IOPS. Pod anti-affinity prevents co-location on same node.

Memory Limits and WiredTiger

WiredTiger’s cache size defaults to 50% of available RAM minus 1 GB. In a container, “available RAM” is the container’s memory limit, not the host’s physical RAM. If the container has a 16 GB memory limit, WiredTiger sets its cache to 7 GB.

But the container needs memory beyond the WiredTiger cache: the connection pool, query execution buffers, aggregation pipeline memory, the operating system page cache, and the MongoDB server process itself. A 16 GB container with a 7 GB WiredTiger cache leaves 9 GB for everything else. This is usually sufficient.

The danger: if the MongoDB process exceeds the container’s memory limit, the OOM killer terminates the process. This is a hard crash with no graceful shutdown.

# Kubernetes pod spec
containers:
  - name: mongodb
    image: mongo:7.0
    resources:
      requests:
        memory: "16Gi"
        cpu: "4"
      limits:
        memory: "16Gi"    # Hard limit - OOM kill above this
        cpu: "4"
    command: ["mongod"]
    args:
      - "--wiredTigerCacheSizeGB=7"  # Explicitly set, do not rely on auto-detection
      - "--bind_ip_all"

Never rely on WiredTiger’s auto-detection in containers. Set --wiredTigerCacheSizeGB explicitly to leave headroom:

Container memory limitRecommended cacheSizeGBHeadroom for OS + server
8 GB3 GB5 GB
16 GB7 GB9 GB
32 GB14 GB18 GB
64 GB28 GB36 GB

The formula: cacheSizeGB = (containerMemoryGB * 0.5) - 1. If running aggregations with allowDiskUse: false (in-memory), reduce cache further to leave room for the 100 MB per-pipeline memory limit.

CPU Limits and CFS Throttling

Kubernetes uses the Linux Completely Fair Scheduler (CFS) to enforce CPU limits. When a container exceeds its CPU limit within a 100ms period, the kernel throttles the container by pausing it until the next period. This manifests as latency spikes.

MongoDB is multi-threaded. A 4-CPU limit means MongoDB can use 4 cores. But during a checkpoint (every 60 seconds), WiredTiger spawns multiple threads for flushing data to disk. If the checkpoint temporarily needs 6 cores and the limit is 4, the CFS throttles MongoDB. All operations pause for the throttling duration (potentially tens of milliseconds).

// Detect CFS throttling from the application side
// Symptom: periodic p99 latency spikes every ~100ms
// Correlates with container CPU throttling metrics:
// container_cpu_cfs_throttled_periods_total
// container_cpu_cfs_throttled_seconds_total

The fix: set CPU requests equal to limits (guaranteed QoS class in Kubernetes) and size CPUs for peak usage, not average:

resources:
  requests:
    cpu: "8"      # Same as limit for Guaranteed QoS
    memory: "16Gi"
  limits:
    cpu: "8"      # Sized for checkpoint peaks
    memory: "16Gi"