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

# Accelerate migration to K8s

export const RecipeBadges = ({badges = []}) => {
  const map = {
    "recipe": {
      label: "Recipe",
      color: "#A86400"
    },
    "ai-excellence": {
      label: "AI excellence",
      color: "#653EE8"
    },
    "production-readiness": {
      label: "Production readiness",
      color: "#226686"
    },
    "incident-mgmt": {
      label: "Incident management",
      color: "#86198F"
    },
    "migrations": {
      label: "Migrations",
      color: "#0E7C66"
    },
    "compliance": {
      label: "Compliance",
      color: "#475569"
    },
    "dora": {
      label: "DORA metrics",
      color: "#1D4ED8"
    },
    "security": {
      label: "Security",
      color: "#B42318"
    },
    "operational-readiness": {
      label: "Operational readiness",
      color: "#B54708"
    },
    "dev-onboarding": {
      label: "Developer onboarding",
      color: "#C11574"
    }
  };
  const items = badges.map(key => map[key]).filter(Boolean);
  if (items.length === 0) return null;
  return <div className="not-prose flex flex-wrap gap-2 mb-6">
      {items.map(b => <span key={b.label} className="rounded-lg px-2 py-1 text-xs font-medium" style={{
    backgroundColor: b.color,
    color: b.textColor || "#FFFFFF"
  }}>
          {b.label}
        </span>)}
    </div>;
};

<RecipeBadges badges={["recipe", "migrations"]} />

Organizations migrate to Kubernetes because it solves critical scalability, reliability, security, and developer-velocity challenges that modern teams face. At its core, Kubernetes provides a standardized, automated, self-healing platform for running applications.

Accelerating a Kubernetes migration is often a strategic engineering decision made to meet certain business requirements, such as lowering infrastructure costs or improving MTTR, in a shorter timeframe.

To accelerate a migration to Kubernetes:

* [Launch a Kubernetes Deployment Baseline Scorecard](/guides/migrations-and-modernization/accelerate-migration-to-k8s#create-a-kubernetes-scorecard). Cortex provides a prebuilt Kubernetes [Scorecard](/standardize/scorecards) template that you can use to track migration-related controls and standards.
* [Launch an Initiative](/guides/migrations-and-modernization/accelerate-migration-to-k8s#create-a-k8s-acceleration-initiative) associated with the Scorecard, which gives your engineers a deadline for when to complete certain goals.
* [Use reports and Cortex MCP](/guides/migrations-and-modernization/accelerate-migration-to-k8s#measuring-success) to better understand progress and next steps.

<Check>
  Cortex customer LetsGetCheck cut their Kubernetes migration timeline from 24 months down to 16 months by targeting goals with Initiatives. [Read the full case study here](https://www.cortex.io/case-studies/letsgetchecked).
</Check>

## Create a Kubernetes Scorecard

### Prerequisites

Before using this Scorecard template:

* Ensure you have configured integrations for:
  * Version control: [Azure DevOps](/ingesting-data-into-cortex/integrations/azuredevops), [Bitbucket](/ingesting-data-into-cortex/integrations/bitbucket), [GitHub](/ingesting-data-into-cortex/integrations/github), or [GitLab](/ingesting-data-into-cortex/integrations/gitlab).
  * [Kubernetes](/ingesting-data-into-cortex/integrations/kubernetes)

### Step 1: Create the Scorecard and configure its basic settings

You can create a Scorecard in the Cortex UI, or you can add it to your workspace via GitOps or the Cortex API.

<Tabs>
  <Tab title="Cortex UI">
    #### Create Scorecard in the Cortex UI

    1. On the [**Scorecards** page](https://app.getcortexapp.com/admin/scorecards) in your workspace, click **Create Scorecard**.
    2. On the `Kubernetes Deployment Baseline` template, click **Use**.
    3. Configure basic settings, including the Scorecard's name, unique identifier, description, and more.
       * Learn about configuring the basic settings in the [Creating a Scorecard documentation](/standardize/scorecards/create).
  </Tab>

  <Tab title="GitOps or API">
    #### Create Scorecard via GitOps or API

    When following a [GitOps approach](/configure/gitops), you can add a Scorecard YAML file to your `.cortex/scorecards` directory in your version control repository. Note that GitOps must be enabled for Scorecards in your [GitOps settings](/configure/settings/gitops-settings).

    You could also use the [Cortex API,](/api/rest/scorecards) where you can submit a Scorecard definition in YAML.

    <Accordion title="Kubernetes Scorecard YAML">
      Use the YAML file below to add this Scorecard to your workspace via the API or via a GitOps flow.

      ```yaml theme={null}
      tag: kubernetes-deployment-baseline
      name: Kubernetes Deployment Baseline
      description: |-
        This Scorecard enforces a minimum set of standards for services running on Kubernetes. It validates resource requests/limits, probes, container image hygiene, and Git branch protection so that workloads are reliable, predictable, and safe to operate in production.
      draft: true
      notifications:
        enabled: true
        scoreDropNotificationsEnabled: false
      exemptions:
        enabled: true
        autoApprove: false
        userSpecificNotifications: false
      ladder:
        name: Default Ladder
        levels:
        - name: Foundational container hygiene
          rank: 1
          description: The service is at least containerized in a basic way.
          color: "#c38b5f"
        - name: Runtime ready
          rank: 2
          description: This container behaves predictably when scheduled on Kubernetes.
          color: "#8c9298"
        - name: Kubernetes production ready
          rank: 3
          description: This service is safe to run in production from a governance and reliability
            perspective.
          color: "#cda400"
      rules:
      - title: Memory limit is set
        description: "Prevents a single pod from consuming all node memory. To ensure all\
          \ containers have memory limits set, this rule compares the total number of containers\
          \ with the number of containers that have memory limits."
        expression: |-
          jq(k8s.spec(), "[.[].template.spec.containers[]] | length") ==
          jq(k8s.spec(), "[.[].template.spec.containers[] | select(.resources.limits.memory != null)] | length")
        identifier: 17376f1a-bfbe-361b-86d7-eac192f729d9
        weight: 1
        level: Runtime ready
      - title: Git branch protection
        description: Prevent direct pushes and unsafe merges to the main branch; enforce
          reviews and status checks.
        expression: git.branchProtection() != null
        identifier: 21c30af3-6dd6-3bce-bbe5-56e43549015f
        weight: 1
        level: Kubernetes production ready
        failureMessage: Configure Git branch protection.
      - title: CPU request is set
        description: "This rule checks if CPU resource requests are defined for all containers\
          \ in your Kubernetes deployments, which is important for: \n- Kubernetes scheduler\
          \ to properly place pods on nodes\n- CPU throttling behavior (requests affect\
          \ CPU shares)\n- Cluster capacity planning\n- QoS classification (along with limits\
          \ and memory requests)\n\nTo ensure all containers have CPU resource requests,\
          \ this rule compares the total number of containers with the number of containers\
          \ that have CPU resource requests."
        expression: |-
          jq(k8s.spec(), "[.[].template.spec.containers[]] | length") ==
          jq(k8s.spec(), "[.[].template.spec.containers[] | select(.resources.requests.cpu != null)] | length")
        identifier: 22d76d97-33a5-358a-8694-cf35909acc71
        weight: 1
        level: Runtime ready
      - title: Memory request is set
        description: "Prevents a single pod from consuming all node memory. To ensure all\
          \ containers have memory limits set, this rule compares the total number of containers\
          \ with the number of containers that have memory limits."
        expression: "jq(k8s.spec(), \"[.[].template.spec.containers[]] | length\") == \n\
          jq(k8s.spec(), \"[.[].template.spec.containers[] | select(.resources.requests.memory\
          \ != null)] | length\")"
        identifier: 46f4d17e-ceab-37dd-9a64-ddac9ee4aa73
        weight: 1
        level: Runtime ready
      - title: Runs in cluster
        description: This indicates operational use of Kubernetes.
        expression: k8s != null
        identifier: 6cfcc6f2-53ea-3da8-afaf-8659177d1896
        weight: 1
        level: Foundational container hygiene
      - title: No "latest" tag for base image
        description: |-
          This is a best practice rule that ensures all base images have explicit version tags instead of using `:latest`, which:
          - Prevents unpredictable builds when `:latest` changes
          - Ensures build reproducibility
          - Makes it clear what version is being used
        expression: "git.fileContents(\"Dockerfile\").matchesIn(\"^FROM[ \\t]+(?!.*:latest\\\
          b).*$\")"
        identifier: 873c36b3-5a18-3a09-9c58-bff967ffd6bd
        weight: 1
        level: Kubernetes production ready
      - title: Liveness probe is set
        description: |-
          This ensures your services have liveness probes configured, which are critical for:

          - Detecting when a container is stuck or deadlocked
          - Automatically restarting unhealthy containers
          - Improving service reliability and reducing MTTR
          - Preventing containers that are "running" but not actually functioning

          To ensure all containers have liveness probes, this rule compares the total number of containers with the number of containers that have liveness probes.
        expression: |-
          jq(k8s.spec(), "[.[].template.spec.containers[]] | length") ==
          jq(k8s.spec(), "[.[].template.spec.containers[] | select(.livenessProbe != null)] | length")
        identifier: b1f477fc-2ec5-39b0-9d48-b9a2922d7623
        weight: 1
        level: Runtime ready
      - title: Readiness probe is set
        description: |-
          Ensures the service defines a Kubernetes readiness probe so that traffic is only routed to containers that are fully initialized and able to serve requests. A readiness probe prevents the service mesh or load balancer from sending user traffic to a pod that is still starting up, misconfigured, or temporarily degraded. This improves request success rates, reduces startup-induced errors, and helps maintain overall service reliability.

          To ensure all containers have readiness probes, this rule compares the total number of containers with the number of containers that have readiness probes.
        expression: "jq(k8s.spec(), \"[.[].template.spec.containers[]] | length\") == jq(k8s.spec(),\
          \ \"[.[].template.spec.containers[] | select(.readinessProbe != null)] | length\"\
          )"
        identifier: b4bed489-db7d-3566-826a-c78f6da22190
        weight: 1
        level: Runtime ready
        failureMessage: "This service does not have a Kubernetes readiness probe configured.\
          \ Without a readiness probe, the load balancer may send traffic to pods before\
          \ they are ready, leading to failed requests and degraded reliability. Add a readiness\
          \ probe to ensure only healthy, ready pods receive traffic."
      - title: Dockerignore file exists
        description: Keep images lean and builds fast by excluding unnecessary files.
        expression: git.fileExists(".dockerignore")
        identifier: deb30726-2692-3a2d-8858-a828d33fea17
        weight: 1
        level: Foundational container hygiene
      - title: Dockerfile exists
        description: Verify the service is containerized using a maintained Dockerfile.
          This enables consistency across different environments.
        expression: git.fileExists("Dockerfile")
        identifier: df928975-90e7-35ab-a05c-67ca7cb034a2
        weight: 1
        level: Foundational container hygiene
      filter:
        kind: GENERIC
        types:
          include:
          - service
      ```
    </Accordion>
  </Tab>
</Tabs>

### Step 2: Review and modify rules

Cortex's templated rules are based on common industry standards. See the template in-app for more context on each rule:

<AccordionGroup>
  <Accordion title="Level 1: Foundational container hygiene">
    * Runs in cluster<br />
      `k8s != null`
    * Dockerignore file exists<br />
      `git.fileExists(".dockerignore")`
    * Dockerfile exists<br />
      `git.fileExists("Dockerfile")`
  </Accordion>

  <Accordion title="Level 2: Runtime ready">
    * Memory request is set<br />
      `jq(k8s.spec(), "[.[].template.spec.containers[]] | length") == jq(k8s.spec(), "[.[].template.spec.containers[] | select(.resources.requests.memory != null)] | length")`
    * Memory limit is set<br />
      `jq(k8s.spec(), "[.[].template.spec.containers[]] | length") == jq(k8s.spec(), "[.[].template.spec.containers[] | select(.resources.limits.memory != null)] | length")`
    * Liveness probe is set<br />
      `jq(k8s.spec(), "[.[].template.spec.containers[]] | length") == jq(k8s.spec(), "[.[].template.spec.containers[] | select(.livenessProbe != null)] | length")`
    * Readiness probe is set<br />
      `jq(k8s.spec(), "[.[].template.spec.containers[]] | length") == jq(k8s.spec(), "[.[].template.spec.containers[] | select(.readinessProbe != null)] | length")`
    * CPU request is set<br />
      `jq(k8s.spec(), "[.[].template.spec.containers[]] | length") == jq(k8s.spec(), "[.[].template.spec.containers[] | select(.resources.requests.cpu != null)] | length")`
  </Accordion>

  <Accordion title="Level 3: Kubernetes production ready">
    * Git branch protection<br />
      `git.branchProtection() != null`
    * No "latest" tag for base image<br />
      `git.fileContents("Dockerfile").matchesIn("^FROM[ \t]+(?!.*:latest\b).$")`
  </Accordion>
</AccordionGroup>

You can reorder, delete, and edit rules, add more rules to a level, and assign more points to a rule to signify its importance. Behind each rule is a [Cortex Query Language (CQL) ](/standardize/cql)query; you can edit the existing CQL or write your own queries to further refine your rules.

## Create a K8s acceleration Initiative

If you notice the team isn't making progress as quickly as expected, or you need to complete certain rules within a specific timeframe, you can create an Initiative to motivate completion.

Follow the steps below to create an Initiative:

<Accordion title="Create a Kubernetes Initiative">
  1. While viewing your Kubernetes Deployment Baseline Scorecard, click **Create Initiative** in the upper right.
  2. Configure the Initiative fields, including a descriptive name so your team members understand the purpose of the Initiative. For example, `Accelerate K8s migration`.
     * Make sure to enable notifications so users are notified if an entity they own is failing the Initiative's goal.
     * For the goal, you might want to ask your team to complete a certain level, or specific rules. Examples: `Complete all "Runtime ready" level rules by the end of the quarter` or `Make sure a Dockerfile exists for every service by end of month`.
  3. Save the Initiative.

  After the Initiative is published, entity owners will be notified if their entity is not meeting the goal. They will see action items listed on their [engineering homepage](/streamline/homepage) and they will automatically receive a weekly progress update from the Initiative.

  Learn more about [creating Initiatives in the docs](/improve/initiatives).
</Accordion>

## Measuring success

To understand progress of your Scorecard:

* Ask [Cortex MCP,](/get-started/cortex-ai-assistant/mcp) "How is my Kubernetes Deployment Baseline Scorecard doing?" The MCP will respond with information on the entities that are failing rules and suggested next steps.
* Review reports: The [Bird's Eye report](/improve/reports/birds-eye) gives insight into how entities are performing against the Scorecard by visualizing the data as a heat map:<br />

  <img src="https://mintcdn.com/cortex-290c0c42/qRY2zZcIyXqFVeCq/guides/images/kubernetes-birds-eye.jpg?fit=max&auto=format&n=qRY2zZcIyXqFVeCq&q=85&s=e8de31484db809953274df430021a857" alt="The bird's eye report shows Scorecard progress as a heat map." width="2164" height="1076" data-path="guides/images/kubernetes-birds-eye.jpg" />

You can also review your Engineering Intelligence metrics for impact on key engineering metrics, such as:

* **MTTR**: Kubernetes best practices, such as liveness probes in place, can result in faster diagnosis and auto-recovery. You may see MTTR decrease.
* **Change failure rate**: With resource limits reducing pod crashes and no "latest" image tag resulting in deterministic builds, you may see more successful deploys on first attempt, thus decreasing change failure rate.


## Related topics

- [Initiatives and Action items](/improve/initiatives.md)
- [Scorecards](/guides/product-areas/scorecards.md)
- [Validate data after migration](/guides/migrations-and-modernization/validate-data.md)
