> ## Documentation Index
> Fetch the complete documentation index at: https://cloudanix.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Containers Should Set CPU And Memory Limits

### More Info:

Verifies every container sets resources.limits.cpu and resources.limits.memory so a single workload cannot exhaust a node.

### Risk Level

Medium

### Address

Security

### Compliance Standards

* Cloudanix Best Practice

### Triage and Remediation

<Tabs>
  <Tab title="Remediation">
    ### Remediation

    <AccordionGroup>
      <Accordion title="Manual Steps" defaultOpen="true">
        1. Identify noncompliant pods and their controllers (run on any machine with kubectl access):
           ```bash theme={null}
           kubectl get pods --all-namespaces -o json | jq -r '
             [ .items[]
             | select(.metadata.namespace as $n | ["kube-system","kube-public","kube-node-lease"] | index($n) | not)
             | .metadata as $m
             | ([ ($m.ownerReferences // [])[] | select(.controller) ] | first) as $own
             | (.spec.containers // [])[]
             | ((.resources.limits.cpu != null) and (.resources.limits.memory != null)) as $ok
             | select($ok | not)
             | "ns=\($m.namespace) pod=\($m.name) container=\(.name)"
               + (if $own == null then "" else " ownerKind=\($own.kind) ownerName=\($own.name)" end)
           ][]'
           ```

        2. For a pod managed by a controller (e.g., Deployment), edit the controller manifest to add limits (run on any machine with kubectl access):
           ```bash theme={null}
           kubectl -n <namespace> edit deployment <deployment-name>
           ```
           In each container under `spec.template.spec.containers`, ensure:
           ```yaml theme={null}
           resources:
             limits:
               cpu: "500m"
               memory: "256Mi"
           ```
           Adjust values to match your capacity and SLOs, then save and exit to let Kubernetes roll out updated pods.

        3. For other controllers (StatefulSet, DaemonSet, Job, CronJob), edit similarly (run on any machine with kubectl access):
           ```bash theme={null}
           kubectl -n <namespace> edit statefulset <name>
           kubectl -n <namespace> edit daemonset <name>
           kubectl -n <namespace> edit job <name>
           kubectl -n <namespace> edit cronjob <name>
           ```
           Add or update `resources.limits.cpu` and `resources.limits.memory` for every container in `spec.template.spec.containers` (or `spec.jobTemplate.spec.template.spec.containers` for CronJob).

        4. For standalone pods without controllers that must be kept, export, modify, and recreate (run on any machine with kubectl access):
           ```bash theme={null}
           kubectl -n <namespace> get pod <pod-name> -o yaml > /tmp/pod-with-limits.yaml
           ```
           Edit `/tmp/pod-with-limits.yaml`:
           * Remove fields `status`, `metadata.resourceVersion`, `metadata.uid`, `metadata.creationTimestamp`, and `metadata.ownerReferences`.
           * Under every container in `spec.containers`, add:
             ```yaml theme={null}
             resources:
               limits:
                 cpu: "500m"
                 memory: "256Mi"
             ```
           Then recreate:
           ```bash theme={null}
           kubectl -n <namespace> delete pod <pod-name>
           kubectl -n <namespace> apply -f /tmp/pod-with-limits.yaml
           ```

        5. Optionally enforce future compliance with a LimitRange in each namespace (run on any machine with kubectl access):
           ```bash theme={null}
           cat << 'EOF' | kubectl apply -f -
           apiVersion: v1
           kind: LimitRange
           metadata:
             name: default-container-limits
             namespace: <namespace>
           spec:
             limits:
             - type: Container
               default:
                 cpu: "500m"
                 memory: "256Mi"
               defaultRequest:
                 cpu: "250m"
                 memory: "128Mi"
           EOF
           ```

        6. Verify all non-system pods now have CPU and memory limits (run on any machine with kubectl access):
           ```bash theme={null}
           kubectl get pods --all-namespaces -o json | jq -r '
             [ .items[]
             | select(.metadata.namespace as $n | ["kube-system","kube-public","kube-node-lease"] | index($n) | not)
             | .metadata as $m
             | (.spec.containers // [])[]
             | ((.resources.limits.cpu != null) and (.resources.limits.memory != null)) as $ok
             | select($ok | not)
           ] as $rows
           | if ($rows | length) == 0 then "is_compliant=true" else $rows[] end'
           ```
      </Accordion>

      <Accordion title="Using kubectl">
        On any machine with `kubectl` access:

        1. Identify non‑compliant pods and their owning controllers

        ```bash theme={null}
        kubectl get pods --all-namespaces -o json | jq -r '
          [ .items[]
          | select(.metadata.namespace as $n | ["kube-system","kube-public","kube-node-lease"] | index($n) | not)
          | .metadata as $m
          | ([ ($m.ownerReferences // [])[] | select(.controller) ] | first) as $own
          | (.spec.containers // [])[]
          | ((.resources.limits.cpu != null) and (.resources.limits.memory != null)) as $ok
          | select($ok | not)
          | "\($own.kind)//\($m.namespace)//\($own.name)"
          ] | unique[]' | sed 's#//# #g'
        ```

        This outputs lines like:
        `Deployment default my-app`

        2. Patch a controller to add limits (example for a Deployment)

        Export the manifest, edit, and re-apply:

        ```bash theme={null}
        kubectl get deployment my-app -n default -o yaml > /tmp/my-app-deploy.yaml
        ```

        Edit `/tmp/my-app-deploy.yaml` and, for each container under `spec.template.spec.containers`, ensure a `resources` block like:

        ```yaml theme={null}
                resources:
                  limits:
                    cpu: "500m"
                    memory: "256Mi"
                  requests:
                    cpu: "250m"
                    memory: "128Mi"
        ```

        Apply the updated manifest:

        ```bash theme={null}
        kubectl apply -f /tmp/my-app-deploy.yaml
        ```

        Repeat this export/edit/apply pattern for each non‑compliant controller kind (e.g., `StatefulSet`, `DaemonSet`, `Job`, `CronJob`, `ReplicaSet` if managed directly), always updating the containers in `spec.template.spec.containers`.

        3. For bare Pods (no ownerReferences)

        Export, edit, and re‑create:

        ```bash theme={null}
        kubectl get pod my-pod -n default -o yaml > /tmp/my-pod.yaml
        ```

        In `/tmp/my-pod.yaml`:

        * Remove the entire `status:` section.
        * Under `spec.containers[]`, add `resources.limits.cpu` and `resources.limits.memory` as above.

        Delete and recreate:

        ```bash theme={null}
        kubectl delete pod my-pod -n default
        kubectl apply -f /tmp/my-pod.yaml
        ```

        4. Verification

        On any machine with `kubectl` access:

        ```bash theme={null}
        kubectl get pods --all-namespaces -o json | jq -r '
          [ .items[]
          | select(.metadata.namespace as $n | ["kube-system","kube-public","kube-node-lease"] | index($n) | not)
          | .metadata as $m
          | (.spec.containers // [])[]
          | ((.resources.limits.cpu != null) and (.resources.limits.memory != null)) as $ok
          | select($ok | not)
          ] | if (length == 0) then "is_compliant=true" else . end'
        ```
      </Accordion>

      <Accordion title="Automation">
        ```bash theme={null}
        #!/usr/bin/env bash
        #
        # Enforce CPU and memory limits on all non-excluded Pods by patching their controllers.
        # - Skips kube-system, kube-public, kube-node-lease
        # - For each container with missing limits, sets:
        #     limits.cpu: "500m"
        #     limits.memory: "512Mi"
        # - Patches Deployments, StatefulSets, DaemonSets, ReplicaSets, Jobs, CronJobs.
        # - Standalone Pods are reported but NOT modified (edit their manifests/IaC).
        #
        # Run on: any machine with kubectl and jq access to the cluster.
        # Idempotent: safe to re-run; existing limits are preserved.

        set -euo pipefail

        DEFAULT_CPU_LIMIT="500m"
        DEFAULT_MEM_LIMIT="512Mi"

        # Ensure kubectl and jq are available
        command -v kubectl >/dev/null 2>&1 || { echo "kubectl not found in PATH" >&2; exit 1; }
        command -v jq       >/dev/null 2>&1 || { echo "jq not found in PATH" >&2; exit 1; }

        echo "Scanning pods for containers missing CPU or memory limits..."

        # Get all relevant pods as JSON once
        PODS_JSON="$(kubectl get pods --all-namespaces -o json)"

        # Build a list of affected containers with their owner (if any)
        # Output format (tab-separated):
        # ns  podName  ownerKind  ownerName  ownerUID  containerName  hasCpu  hasMem
        AFFECTED_CONTAINERS="$(
          echo "${PODS_JSON}" | jq -r '
            .items[]
            | select(.metadata.namespace as $n | ["kube-system","kube-public","kube-node-lease"] | index($n) | not)
            | .metadata as $m
            | ([ ($m.ownerReferences // [])[] | select(.controller) ] | first) as $own
            | (.spec.containers // [])[]
            | ((.resources.limits.cpu != null)   ) as $hasCpu
            | ((.resources.limits.memory != null)) as $hasMem
            | select( ( $hasCpu and $hasMem ) | not )
            | [
                $m.namespace,
                $m.name,
                (if $own == null then "" else $own.kind end),
                (if $own == null then "" else $own.name end),
                (if $own == null then "" else $own.uid end),
                .name,
                (if $hasCpu then "true" else "false" end),
                (if $hasMem then "true" else "false" end)
              ]
              | @tsv
          '
        )"

        if [[ -z "${AFFECTED_CONTAINERS}" ]]; then
          echo "All scanned containers already have CPU and memory limits set."
          echo "Re-running verification command for confirmation..."
          kubectl get pods --all-namespaces -o json | jq -r '
            [ .items[]
            | select(.metadata.namespace as $n | ["kube-system","kube-public","kube-node-lease"] | index($n) | not)
            | .metadata as $m
            | (.spec.nodeName // "") as $node
            | (($m.labels // {}) | to_entries | map("\(.key):\(.value)") | join(",")) as $labels
            | ([ ($m.ownerReferences // [])[] | select(.controller) ] | first) as $own
            | (.spec.containers // [])[]
            | ((.resources.limits.cpu != null) and (.resources.limits.memory != null)) as $ok
            | "kind=Pod ns=\($m.namespace) name=\($m.name) uid=\($m.uid) apiVersion=v1"
              + (if ($m.creationTimestamp // "") == "" then "" else " created=\($m.creationTimestamp)" end)
              + (if $node   == ""   then "" else " node=\($node)" end)
              + (if $labels == ""   then "" else " labels=\($labels)" end)
              + (if $own    == null then "" else " owner=\($own.kind)/\($m.namespace)/\($own.name)/\($own.uid)" end)
              + " container=\(.name) image=\(.image)"
              + " limitsCpu=\(.resources.limits.cpu // "unset") limitsMemory=\(.resources.limits.memory // "unset")"
              + " is_compliant=\(if $ok then "true" else "false" end)"
            ] as $rows
            | if ($rows | length) == 0 then "is_compliant=true" else $rows[] end'
          exit 0
        fi

        echo "Found containers missing limits. Grouping by owning controller..."

        # Build a unique list of owner controllers to patch
        # Output: ns<TAB>ownerKind<TAB>ownerName
        OWNERS_TO_PATCH="$(
          printf "%s\n" "${AFFECTED_CONTAINERS}" | awk -F'\t' '
            $3 != "" && $4 != "" {
              key = $1 "\t" $3 "\t" $4
              if (!(key in seen)) {
                seen[key]=1
                print key
              }
            }
          '
        )"

        # Track standalone pods (no owner) so users can fix their manifests manually
        STANDALONE_PODS_REPORTED=()

        if [[ -z "${OWNERS_TO_PATCH}" ]]; then
          echo "No controller-backed workloads to patch; only standalone Pods are non-compliant."
        else
          echo "Patching the following controllers with default CPU=${DEFAULT_CPU_LIMIT}, Memory=${DEFAULT_MEM_LIMIT}:"
          printf "%s\n" "${OWNERS_TO_PATCH}" | sed $'s/\t/ /g'

          # For each controller, construct and apply a strategic merge patch
          while IFS=$'\t' read -r NS OWNER_KIND OWNER_NAME; do
            [[ -z "${NS}" || -z "${OWNER_KIND}" || -z "${OWNER_NAME}" ]] && continue

            # Map kind -> kubectl resource type
            case "${OWNER_KIND}" in
              Deployment)   RES_TYPE="deployment" ;;
              StatefulSet)  RES_TYPE="statefulset" ;;
              DaemonSet)    RES_TYPE="daemonset" ;;
              ReplicaSet)   RES_TYPE="replicaset" ;;
              Job)          RES_TYPE="job" ;;
              CronJob)      RES_TYPE="cronjob" ;;
              *)
                echo "Skipping unsupported controller kind ${OWNER_KIND} (${NS}/${OWNER_NAME})" >&2
                continue
                ;;
            esac

            echo "Processing ${OWNER_KIND} ${NS}/${OWNER_NAME}..."

            # Get the current pod template spec for this controller
            CTRL_JSON="$(kubectl -n "${NS}" get "${RES_TYPE}" "${OWNER_NAME}" -o json)"

            # Build a patched pod template with limits added where missing and existing limits preserved
            PATCH_TEMPLATE="$(
              echo "${CTRL_JSON}" | jq --arg cpu "${DEFAULT_CPU_LIMIT}" --arg mem "${DEFAULT_MEM_LIMIT}" '
                .spec.template.spec
                |= (
                  .containers |= map(
                    .resources.limits.cpu    |= (if . == null then $cpu else . end)
                    | .resources.limits.memory |= (if . == null then $mem else . end)
                  )
                )
              '
            )"

            # Extract just the spec.template part for a strategic merge patch
            PATCH="$(
              echo "${PATCH_TEMPLATE}" | jq '{ spec: { template: . } }'
            )"

            echo "${PATCH}" | kubectl -n "${NS}" patch "${RES_TYPE}" "${OWNER_NAME}" --type merge -p "$(cat)"
            echo "Patched ${OWNER_KIND} ${NS}/${OWNER_NAME}"
          done <<< "${OWNERS_TO_PATCH}"
        fi

        # Report standalone pods (no owning controller) requiring manual manifest/IaC updates
        echo
        echo "Checking for standalone Pods (no owning controller) with missing limits..."
        while IFS=$'\t' read -r NS POD OWNER_KIND OWNER_NAME OWNER_UID CONTAINER HASCPU HASMEM; do
          if [[ -z "${OWNER_KIND}" || -z "${OWNER_NAME}" ]]; then
            ID="${NS}/${POD}"
            if [[ " ${STANDALONE_PODS_REPORTED[*]-} " != *" ${ID} "* ]]; then
              STANDALONE_PODS_REPORTED+=("${ID}")
              echo "Standalone Pod requires manual fix: ${NS}/${POD} (container ${CONTAINER})"
            fi
          fi
        done <<< "${AFFECTED_CONTAINERS}"

        if ((${#STANDALONE_PODS_REPORTED[@]} > 0)); then
          echo
          echo "For each standalone Pod above, edit its manifest/IaC to set resources.limits.cpu and resources.limits.memory,"
          echo "then recreate the Pod from the updated definition."
        fi

        echo
        echo "Re-running verification command to confirm compliance..."
        kubectl get pods --all-namespaces -o json | jq -r '
          [ .items[]
          | select(.metadata.namespace as $n | ["kube-system","kube-public","kube-node-lease"] | index($n) | not)
          | .metadata as $m
          | (.spec.nodeName // "") as $node
          | (($m.labels // {}) | to_entries | map("\(.key):\(.value)") | join(",")) as $labels
          | ([ ($m.ownerReferences // [])[] | select(.controller) ] | first) as $own
          | (.spec.containers // [])[]
          | ((.resources.limits.cpu != null) and (.resources.limits.memory != null)) as $ok
          | "kind=Pod ns=\($m.namespace) name=\($m.name) uid=\($m.uid) apiVersion=v1"
            + (if ($m.creationTimestamp // "") == "" then "" else " created=\($m.creationTimestamp)" end)
            + (if $node   == ""   then "" else " node=\($node)" end)
            + (if $labels == ""   then "" else " labels=\($labels)" end)
            + (if $own    == null then "" else " owner=\($own.kind)/\($m.namespace)/\($own.name)/\($own.uid)" end)
            + " container=\(.name) image=\(.image)"
            + " limitsCpu=\(.resources.limits.cpu // "unset") limitsMemory=\(.resources.limits.memory // "unset")"
            + " is_compliant=\(if $ok then "true" else "false" end)"
          ] as $rows
          | if ($rows | length) == 0 then "is_compliant=true" else $rows[] end'
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
