---
name: containers-cs_app_deploy
title: Deploying Kubernetes-native apps in clusters
description: Deploy containerized apps in IBM Cloud&reg; Kubernetes Service using Kubernetes techniques. Perform rolling updates and rollbacks without downtime for your users.
last-updated: 2026-08-06
---

> ## Documentation Index
> The table of contents for this documentation set is at https://cloud.ibm.com/docs/containers?format=markdown
> The index for all IBM Cloud docs is at: https://cloud.ibm.com/docs/llms.txt
> Use these files to discover more information as needed.

# Deploying Kubernetes-native apps in clusters
{: #deploy_app}

Deploy containerized apps in IBM Cloud&reg; Kubernetes Service using Kubernetes techniques. Perform rolling updates and rollbacks without downtime for your users.
{: shortdesc}

Learn more about creating configuration files in the [Configuration Best Practices](https://kubernetes.io/blog/2025/11/25/configuration-good-practices/){: external} guide.

## Launching the Kubernetes dashboard
{: #cli_dashboard}

Access the Kubernetes dashboard to view cluster and worker node information through the [IBM Cloud console](#db_gui) or [CLI](#db_cli).
{: shortdesc}

Before you begin, verify you have the appropriate [access role](https://cloud.ibm.com/docs/containers?topic=containers-iam-platform-access-roles&format=markdown). [Log in to your account. If applicable, target the appropriate resource group. Set the context for your cluster.](https://cloud.ibm.com/docs/containers?topic=containers-access_cluster&format=markdown)

### Launching the Kubernetes dashboard from the IBM Cloud console
{: #db_gui}


1. Log in to the [IBM Cloud console](https://cloud.ibm.com/).
2. From the menu bar, select the account that you want to use.
3. From the menu ![Menu icon](../icons/icon_hamburger.svg "Menu icon"), click **Containers** > **Clusters**.
4. On the **Clusters** page, click the cluster that you want to access.
5. From the cluster detail page, click the **Kubernetes Dashboard** button.


### Launching the Kubernetes dashboard from the CLI
{: #db_cli}

The CLI method enables automation and CI/CD integration. [Install the CLI](https://cloud.ibm.com/docs/containers?topic=containers-cli-install&format=markdown) before you begin.

1. Get your Kubernetes credentials.

    ```sh
    kubectl config view -o jsonpath='{.users[0].user.auth-provider.config.id-token}'
    ```
    {: pre}

2. Copy the **id-token** value from the output.

3. Start the proxy.

    ```sh
    kubectl proxy
    ```
    {: pre}

    Example output

    ```sh
    Starting to serve on 127.0.0.1:8001
    ```
    {: screen}

4. Sign in to the dashboard.

    1. Navigate to the following URL in your browser:

        ```sh
        http://localhost:8001/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy/
        ```
        {: codeblock}

    2. Select the **Token** authentication method on the sign-on page.

    3. Paste the **id-token** value into the **Token** field and click **SIGN IN**.

Use `CTRL+C` to exit the `proxy` command. Run `kubectl proxy` again to restart the dashboard.


## Deploying apps with the Kubernetes dashboard
{: #app_ui}

Deploy apps through the dashboard by entering configuration details or uploading a YAML file.
{: shortdesc}

Before you begin, [open the dashboard](#cli_dashboard) and verify you have a [service access role](https://cloud.ibm.com/docs/containers?topic=containers-iam-platform-access-roles&format=markdown). [Log in to your account. If applicable, target the appropriate resource group. Set the context for your cluster.](https://cloud.ibm.com/docs/containers?topic=containers-access_cluster&format=markdown)

To deploy your app

1. Click **+ Create**.
2. Choose a deployment method:
    - Select **Specify app details** and enter the details.
    - Select **Upload a YAML or JSON file** to upload your app [configuration file](https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/){: external}.

3. Click **Deployments** to verify your app deployed successfully.

## Deploying apps with the CLI
{: #app_cli}

The CLI method provides precise control and enables automation. You'll create configuration files that define your app's resources and can be version controlled.
{: shortdesc}

Before you begin, [install the CLI](https://cloud.ibm.com/docs/containers?topic=containers-cli-install&format=markdown) and verify you have a [service access role](https://cloud.ibm.com/docs/containers?topic=containers-iam-platform-access-roles&format=markdown). [Log in to your account. If applicable, target the appropriate resource group. Set the context for your cluster.](https://cloud.ibm.com/docs/containers?topic=containers-access_cluster&format=markdown)

To deploy your app

1. Create a configuration file that includes [Deployment](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/){: external}, [Service](https://kubernetes.io/docs/concepts/services-networking/service/){: external}, and [Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/){: external} resources as needed. Learn more about [securing your personal information](https://cloud.ibm.com/docs/containers?topic=containers-security&format=markdown#pi) when you work with Kubernetes resources.

2. Apply the configuration file.

    ```sh
    kubectl apply -f config.yaml
    ```
    {: pre}

3. Verify you can access your app.

## Deploying apps to specific worker nodes by using labels
{: #node_affinity}

When you deploy an app, the app pods indiscriminately deploy to various worker nodes in your cluster. Sometimes, you might want to restrict the worker nodes that the app pods to deploy to. For example, you might want app pods to deploy to only worker nodes in a certain worker pool because those worker nodes are on bare metal machines. To designate the worker nodes that app pods must deploy to, add an affinity rule to your app deployment.
{: shortdesc}

Before you begin
- [Log in to your account. If applicable, target the appropriate resource group. Set the context for your cluster.](https://cloud.ibm.com/docs/containers?topic=containers-access_cluster&format=markdown)
- **Optional**: [Set a label for the worker pool](https://cloud.ibm.com/docs/containers?topic=containers-worker-tag-label&format=markdown) that you want to run the app on.

To deploy apps to specific worker nodes,

1. Get the ID of the worker pool that you want to deploy app pods to.
    ```sh
    ibmcloud ks worker-pool ls --cluster CLUSTER_NAME_OR_ID
    ```
    {: pre}

2. List the worker nodes that are in the worker pool, and note one of the **Private IP** addresses.
    ```sh
    ibmcloud ks worker ls --cluster CLUSTER_NAME_OR_ID --worker-pool WORKER_POOL_NAME_OR_ID
    ```
    {: pre}

3. Describe the worker node. In the **Labels** output, note the worker pool ID label, `ibm-cloud.kubernetes.io/worker-pool-id`.

    The steps in this topic use a worker pool ID to deploy app pods only to worker nodes within that worker pool. To deploy app pods to specific worker nodes by using a different label, note this label instead. For example, to deploy app pods only to worker nodes on a specific private VLAN, use the `privateVLAN=` label.
    {: tip}

    ```sh
    kubectl describe node <worker_node_private_IP>
    ```
    {: pre}

    Example output

    ```sh
    NAME:               10.xxx.xx.xxx
    Roles:              <none>
    Labels:             arch=amd64
                        beta.kubernetes.io/arch=amd64
                        beta.kubernetes.io/instance-type=b3c.4x16.encrypted
                        beta.kubernetes.io/os=linux
                        failure-domain.beta.kubernetes.io/region=us-south
                        failure-domain.beta.kubernetes.io/zone=dal10
                        ibm-cloud.kubernetes.io/encrypted-docker-data=true
                        ibm-cloud.kubernetes.io/ha-worker=true
                        ibm-cloud.kubernetes.io/iaas-provider=softlayer
                        ibm-cloud.kubernetes.io/machine-type=b3c.4x16.encrypted
                        ibm-cloud.kubernetes.io/sgx-enabled=false
                        ibm-cloud.kubernetes.io/worker-pool-id=00a11aa1a11aa11a1111a1111aaa11aa-11a11a
                        ibm-cloud.kubernetes.io/worker-version=1.35_1534
                        kubernetes.io/hostname=10.xxx.xx.xxx
                        privateVLAN=1234567
                        publicVLAN=7654321
    Annotations:        node.alpha.kubernetes.io/ttl=0
    ...
    ```
    {: screen}

4. [Add an affinity rule](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/){: external} for the worker pool ID label to the app deployment.

    Example YAML

    ```yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: with-node-affinity
    spec:
      template:
        spec:
          affinity:
            nodeAffinity:
              requiredDuringSchedulingIgnoredDuringExecution:
                nodeSelectorTerms:
                - matchExpressions:
                  - key: ibm-cloud.kubernetes.io/worker-pool-id
                    operator: In
                    values:
                    - <worker_pool_ID>
    ...
    ```
    {: codeblock}

    In the **affinity** section of the example YAML, `ibm-cloud.kubernetes.io/worker-pool-id` is the `key` and `<worker_pool_ID>` is the `value`.

5. Apply the updated deployment configuration file.
    ```sh
    kubectl apply -f with-node-affinity.yaml
    ```
    {: pre}

6. Verify that the app pods deployed to the correct worker nodes.

    1. List the pods in your cluster.
        ```sh
        kubectl get pods -o wide
        ```
        {: pre}

        Example output
        ```sh
        NAME                   READY     STATUS              RESTARTS   AGE       IP               NODE
        cf-py-d7b7d94db-vp8pq  1/1       Running             0          15d       172.30.xxx.xxx   10.176.48.78
        ```
        {: screen}

    2. In the output, identify a pod for your app. Note the **NODE** private IP address of the worker node that the pod is on.

        In the previous example output, the app pod `cf-py-d7b7d94db-vp8pq` is on a worker node with the IP address `10.xxx.xx.xxx`.

    3. List the worker nodes in the worker pool that you designated in your app deployment.

        ```sh
        ibmcloud ks worker ls --cluster CLUSTER_NAME_OR_ID --worker-pool WORKER_POOL_NAME_OR_ID
        ```
        {: pre}

        Example output

        ```sh
        ID                                                 Public IP       Private IP     Machine Type      State    Status  Zone    Version
        kube-dal10-crb20b637238bb471f8b4b8b881bbb4962-w7   169.xx.xxx.xxx  10.176.48.78   b3c.4x16          normal   Ready   dal10   1.8.6_1504
        kube-dal10-crb20b637238bb471f8b4b8b881bbb4962-w8   169.xx.xxx.xxx  10.176.48.83   b3c.4x16          normal   Ready   dal10   1.8.6_1504
        kube-dal12-crb20b637238bb471f8b4b8b881bbb4962-w9   169.xx.xxx.xxx  10.176.48.69   b3c.4x16          normal   Ready   dal12   1.8.6_1504
        ```
        {: screen}

        If you created an app affinity rule based on another factor, get that value instead. For example, to verify that the app pod deployed to a worker node on a specific VLAN, view the VLAN that the worker node is on by running `ibmcloud ks worker get --cluster CLUSTER_NAME_OR_ID --worker WORKER_ID`.
        {: tip}

    4. In the output, verify that the worker node with the private IP address that you identified in the previous step is deployed in this worker pool.

## Deploying an app on an NVIDIA GPU machine
{: #gpu_app}

If you have a GPU machine type, you can speed up the processing time required for compute intensive workloads such as AI, machine learning, inferencing, and more.
{: shortdesc}




**Important driver changes starting with Kubernetes version 1.36**: IBM Cloud Kubernetes Service no longer installs NVIDIA GPU drivers on worker nodes with GPUs starting from Kubernetes version 1.36. If you plan to run GPU workloads on version 1.36 or later clusters, you must manage GPU driver installation and lifecycle on your own worker nodes. For clusters running version 1.35 or earlier, IBM-provided GPU drivers continue to be available. For migration guidance, see [Migrating to self-managed NVIDIA GPU drivers](#gpu-migrate-136).
{: important}



In the following steps, you learn how to deploy workloads that require the GPU. However, you can also deploy apps that don't need to process their workloads across both the GPU and CPU. 

You can also try mathematically intensive workloads such as the [TensorFlow](https://www.tensorflow.org/){: external} machine learning framework with [this Kubernetes demo](https://github.com/pachyderm/pachyderm/tree/master/examples/ml/tensorflow){: external}.
{: tip}

### Prerequisites
{: #gpu-prereqs}

Before you begin
- Create a [cluster](https://cloud.ibm.com/docs/containers?topic=containers-clusters&format=markdown) or worker pool that uses a GPU flavor. Keep in mind that setting up a bare metal machine can take more than one business day to complete. For a list of available flavors, see the following links.
    - [Classic flavors](https://cloud.ibm.com/docs/containers?topic=containers-classic-flavors&format=markdown)
    - [VPC flavors](https://cloud.ibm.com/docs/containers?topic=containers-vpc-flavors&format=markdown)

- Make sure that you are assigned a [service access role](https://cloud.ibm.com/docs/containers?topic=containers-iam-platform-access-roles&format=markdown) that grants the appropriate Kubernetes RBAC role so that you can work with Kubernetes resources in the cluster.



**For Kubernetes version 1.36 and later**: You must install and manage the NVIDIA GPU driver stack on your own. IBM Cloud Kubernetes Service no longer provides pre-installed GPU drivers on worker nodes. Follow the [NVIDIA GPU Operator installation guide](https://docs.nvidia.com/datacenter/cloud-native/gpu-operator/latest/getting-started.html){: external} to install the required components:
- NVIDIA kernel driver
- Container runtime components (e.g., nvidia-container-toolkit)
- Kubernetes device plugin

Until the driver is installed, pods requesting GPUs will remain in `Pending` state. They will transition to `Running` automatically after a compatible driver becomes available on the node.
{: important}

**For Kubernetes version 1.35 and earlier**: IBM-provided GPU drivers are automatically installed on GPU worker nodes. No additional driver installation is required.





### Deploying a workload
{: #gpu-workload}

1. Create a YAML file. In this example, a `Job` YAML manages batch-like workloads by making a short-lived pod that runs until the command completes and successfully terminates.

    For GPU workloads, you must specify the `resources: limits: nvidia.com/gpu` field in the job YAML.
    {: note}

    ```yaml
    apiVersion: batch/v1
    kind: Job
    metadata:
      name: nvidia-devicequery
      labels:
        name: nvidia-devicequery
    spec:
      template:
        metadata:
          labels:
            name: nvidia-devicequery
        spec:
          containers:
          - name: nvidia-devicequery
            image: nvcr.io/nvidia/k8s/cuda-sample:devicequery-cuda11.7.1-ubuntu20.04
            imagePullPolicy: IfNotPresent
            resources:
              limits:
                nvidia.com/gpu: 2
          restartPolicy: Never
    ```
    {: codeblock}
    
    | Component | Description | 
    |--- | --- |
    | Metadata and label names | Enter a name and a label for the job, and use the same name in both the file's metadata and the `spec template` metadata. For example, `nvidia-devicequery`. |
    | `containers.image` | Provide the image that the container is a running instance of. In this example, the value is set to use the DockerHub CUDA device query image:`nvcr.io/nvidia/k8s/cuda-sample:devicequery-cuda11.7.1-ubuntu20.04`. |
    | `containers.imagePullPolicy` | To pull a new image only if the image is not currently on the worker node, specify `IfNotPresent`. |
    | `resources.limits` | For GPU machines, you must specify the resource limit. The Kubernetes [Device Plug-in](https://kubernetes.io/docs/concepts/extend-kubernetes/compute-storage-net/device-plugins/){: external} sets the default resource request to match the limit. \n * You must specify the key as `nvidia.com/gpu`. \n * Enter the whole number of GPUs that you request, such as `2`. Note that container pods don't share GPUs and GPUs can't be overcommitted. For example, if you have only 1 `mg1c.16x128` machine, then you have only 2 GPUs in that machine and can specify a maximum of `2`. |
    {: caption="Understanding your YAML components" caption-side="bottom"}

2. Apply the YAML file. For example:

    ```sh
    kubectl apply -f nvidia-devicequery.yaml
    ```
    {: pre}

3. Check the job pod by filtering your pods by the `nvidia-devicequery` label. Verify that the **STATUS** is **Completed**.

    ```sh
    kubectl get pod -A -l 'name in (nvidia-devicequery)'
    ```
    {: pre}

    Example output

    ```sh
    NAME                  READY     STATUS      RESTARTS   AGE
    nvidia-devicequery-ppkd4      0/1       Completed   0          36s
    ```
    {: screen}

4. Describe the pod to see how the GPU device plug-in scheduled the pod.
    - In the `Limits` and `Requests` fields, see that the resource limit that you specified matches the request that the device plug-in automatically set.
    - In the events, verify that the pod is assigned to your GPU worker node.

        ```sh
        kubectl describe pod nvidia-devicequery-ppkd4
        ```
        {: pre}

        Example output
        ```sh
        NAME:           nvidia-devicequery-ppkd4
        Namespace:      default
        ...
        Limits:
            nvidia.com/gpu:  1
        Requests:
            nvidia.com/gpu:  1
        ...
        Events:
        Type    Reason                 Age   From                     Message
        ----    ------                 ----  ----                     -------
        Normal  Scheduled              1m    default-scheduler        Successfully assigned nvidia-devicequery-ppkd4 to 10.xxx.xx.xxx
        ...
        ```
        {: screen}

5. To verify that the job used the GPU to compute its workload, you can check the logs.

    ```sh
    kubectl logs nvidia-devicequery-ppkd4
    ```
    {: pre}

    Example output

    ```sh
    /cuda-samples/sample Starting...
    
    CUDA Device Query (Runtime API) version (CUDART static linking)
    
    Detected 1 CUDA Capable device(s)
    
    Device 0: "Tesla P100-PCIE-16GB"
    CUDA Driver Version / Runtime Version          11.4 / 11.7
    CUDA Capability Major/Minor version number:    6.0
    Total amount of global memory:                 16281 MBytes (17071734784 bytes)
    (056) Multiprocessors, (064) CUDA Cores/MP:    3584 CUDA Cores
    GPU Max Clock rate:                            1329 MHz (1.33 GHz)
    Memory Clock rate:                             715 Mhz
    Memory Bus Width:                              4096-bit
    L2 Cache Size:                                 4194304 bytes
    Maximum Texture Dimension Size (x,y,z)         1D=(131072), 2D=(131072, 65536), 3D=(16384, 16384, 16384)
    Maximum Layered 1D Texture Size, (num) layers  1D=(32768), 2048 layers
    Maximum Layered 2D Texture Size, (num) layers  2D=(32768, 32768), 2048 layers
    Total amount of constant memory:               65536 bytes
    Total amount of shared memory per block:       49152 bytes
    Total shared memory per multiprocessor:        65536 bytes
    Total number of registers available per block: 65536
    Warp size:                                     32
    Maximum number of threads per multiprocessor:  2048
    Maximum number of threads per block:           1024
    Max dimension size of a thread block (x,y,z): (1024, 1024, 64)
    Max dimension size of a grid size    (x,y,z): (2147483647, 65535, 65535)
    Maximum memory pitch:                          2147483647 bytes
    Texture alignment:                             512 bytes
    Concurrent copy and kernel execution:          Yes with 2 copy engine(s)
    Run time limit on kernels:                     No
    Integrated GPU sharing Host Memory:            No
    Support host page-locked memory mapping:       Yes
    Alignment requirement for Surfaces:            Yes
    Device has ECC support:                        Enabled
    Device supports Unified Addressing (UVA):      Yes
    Device supports Managed Memory:                Yes
    Device supports Compute Preemption:            Yes
    Supports Cooperative Kernel Launch:            Yes
    Supports MultiDevice Co-op Kernel Launch:      Yes
    Device PCI Domain ID / Bus ID / location ID:   0 / 175 / 0
    Compute Mode:
    < Default (multiple host threads can use ::cudaSetDevice() with device simultaneously) >
    
    deviceQuery, CUDA Driver = CUDART, CUDA Driver Version = 11.4, CUDA Runtime Version = 11.7, NumDevs = 1
    Result = PASS
    ```
    {: screen}

    In this example, you see a GPU was used to execute the job because the GPU was scheduled in the worker node. If the limit is set to 2, only 2 GPUs are shown.

Now that you deployed a test GPU workload, you might want to set up your cluster to run a tool that relies on GPU processing, such as [IBM Maximo Visual Inspection](https://www.ibm.com/products/maximo/asset-performance-management){: external}.



## Migrating to self-managed NVIDIA GPU drivers
{: #gpu-migrate-136}

For detailed guidance on migrating from IBM-provided GPU drivers to self-managed drivers when upgrading to Kubernetes version 1.36, see [Migrating to self-managed NVIDIA GPU drivers for Kubernetes 1.36](https://cloud.ibm.com/docs/containers?topic=containers-gpu-migrate-136&format=markdown).