> For the complete documentation index, see [llms.txt](https://developer.harness.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developer.harness.io/continuous-delivery/use-continuous-delivery/cd-building-blocks/cd-steps/utilities/container-step.md).

# Container step

Run a Docker container in a CD stage.

{% hint style="warning" %}
**RECOMMENDATION**

Use a Step Group and Run step instead of Container steps. The supported method is a [Run step](/continuous-delivery/use-continuous-delivery/cd-building-blocks/cd-steps/containerized-steps/run-step.md) in a [containerized step group](/continuous-delivery/use-continuous-delivery/cd-building-blocks/cd-steps/step-groups.md). It uses the containerized step group framework and has the same capabilities as Harness CI.
{% endhint %}

The Container step lets you run any Docker container in your Kubernetes cluster as part of your continuous deployment (CD) stage.

Harness orchestrates the container in your cluster as part of your Harness deployment.

## What you will learn from this topic

* How to add a Container step to a CD stage.
* How to configure the image, connector, resources, and security context for the container.
* How to reference output variables and environment variables between steps.

## Before you begin

Before you begin, make sure you have the following:

* Review [Kubernetes deployments](/continuous-delivery/use-continuous-delivery/deploy-services-on-different-platforms/kubernetes.md) to understand how Harness orchestrates containers in a Kubernetes cluster.
* Ensure the delegate connected to the target cluster is at version 1.0.780xx or later.
* Create a Harness connector for the container registry and the Kubernetes cluster you plan to use.

## Use cases <a href="#use-cases" id="use-cases"></a>

Typically, the primary deployment operations are handled by the default Harness deployment steps, such as the [Kubernetes Rollout step](/continuous-delivery/use-continuous-delivery/deploy-services-on-different-platforms/kubernetes/cd-k8s-ref/kubernetes-rollout-step.md).

The Container step can be used for secondary options. There are several secondary scripts that DevOps teams commonly run in a Kubernetes container as part of a CD pipeline. These scripts can be used to perform various tasks such as configuration, data migration, database schema updates, and more.

<details>

<summary>Common secondary script examples</summary>

Some common secondary scripts that are run in a Kubernetes container as part of CD are:

* Database migrations. Update the database schema and apply any necessary data migrations.
* Configuration updates. Update the configuration of the application or service being deployed.
* Health checks. Perform health checks on the application or service being deployed, ensuring that it is running correctly and responding to requests.
* Load testing. Perform load testing on the application or service, ensuring that it can handle the expected traffic and load.
* Monitoring setup. Set up monitoring and logging for the application or service, so that any issues can be quickly detected and addressed.
* Smoke tests. Perform simple tests to confirm that the application or service is running correctly after deployment.
* Cleanup. Clean up any resources or files that were created during the deployment process.

These secondary scripts are usually run as part of the CD pipeline, either as part of the build process or as separate jobs. They can be written in a variety of scripting languages. In many cases, these scripts are run in containers within the Kubernetes cluster, so that the necessary dependencies and tools are available.

</details>

### Mobile Device Management (MDM)/User Experience Management (UEM) <a href="#mobile-device-management-mdmuser-experience-management-uem" id="mobile-device-management-mdmuser-experience-management-uem"></a>

Harness supports MDM/UEM through interaction using MDM/UEM APIs.

You can use the Container step to call the MDM/UEM APIs from Harness.

Here are some example scripts for MDM and UEM that you might use as part of a DevOps continuous delivery pipeline:

<details>

<summary>MDM script example</summary>

This script sets some variables for the MDM server URL, the MDM username and password, and the configuration file to install. It then uses the curl command to send the configuration file to the MDM server and install it on devices. Finally, it checks for errors and reports success or failure.

```bash
# This script deploys a new Mobile Device Management (MDM) configuration to devices

# Set variables
MDM_SERVER="https://your-mdm-server.com"
MDM_USERNAME="your-mdm-username"
MDM_PASSWORD="your-mdm-password"
MDM_CONFIG_FILE="your-mdm-config-file.plist"

# Install the MDM configuration on devices
curl --request POST \
     --user "$MDM_USERNAME:$MDM_PASSWORD" \
     --header "Content-Type: application/xml" \
     --data-binary "@$MDM_CONFIG_FILE" \
     "$MDM_SERVER/devicemanagement/api/mdm/profiles"

# Check for errors
if [ $? -eq 0 ]; then
  echo "MDM configuration installed successfully."
else
  echo "ERROR: MDM configuration failed to install."
fi
```

</details>

<details>

<summary>UEM script example</summary>

This script sets similar variables for the UEM server URL, username, password, and configuration file to install. It then uses curl to send the configuration file to the UEM server and install it on endpoints. Finally, it checks for errors and reports success or failure.

```bash
# This script deploys a new Unified Endpoint Management (UEM) configuration to endpoints

# Set variables
UEM_SERVER="https://your-uem-server.com"
UEM_USERNAME="your-uem-username"
UEM_PASSWORD="your-uem-password"
UEM_CONFIG_FILE="your-uem-config-file.json"

# Install the UEM configuration on endpoints
curl --request POST \
     --user "$UEM_USERNAME:$UEM_PASSWORD" \
     --header "Content-Type: application/json" \
     --data-binary "@$UEM_CONFIG_FILE" \
     "$UEM_SERVER/api/config"

# Check for errors
if [ $? -eq 0 ]; then
  echo "UEM configuration installed successfully."
else
  echo "ERROR: UEM configuration failed to install."
fi

```

</details>

These scripts are examples and may need to be modified to fit your specific use case. You may also want to include additional steps in your pipeline, such as testing and verification, before deploying MDM or UEM configurations to production devices.

### Edge and IoT deployments <a href="#edge-and-iot-deployments" id="edge-and-iot-deployments"></a>

You can use Harness continuous delivery to automate the process of deploying new versions of applications and services to edge and IoT devices, reducing the risk of human error, minimizing downtime, and improving the security of your IoT ecosystem.

<details>

<summary>Edge script example</summary>

You can use the following script to deploy a new version of an application to an edge device. The script assumes that the device is already set up with the necessary dependencies, and that it is accessible via SSH.

```bash
# Define variables
APP_NAME="my-app"
REMOTE_HOST="my-edge-device"
REMOTE_USER="root"
REMOTE_DIR="/opt/$APP_NAME"

# Copy the new version of the application to the remote host
scp -r ./build $REMOTE_USER@$REMOTE_HOST:$REMOTE_DIR

# Restart the application on the remote host
ssh $REMOTE_USER@$REMOTE_HOST "systemctl restart $APP_NAME.service"
```

</details>

<details>

<summary>IoT script example</summary>

You can use the following script to deploy a new version of an IoT device firmware. The script assumes that the device is connected to the network and can be accessed via SSH.

```bash
# Define variables
DEVICE_IP="192.168.1.100"
DEVICE_USER="pi"
DEVICE_PASS="<DEVICE_PASSWORD>"
FIRMWARE_FILE="firmware-v2.0.bin"

# Copy the new firmware to the device
sshpass -p $DEVICE_PASS scp $FIRMWARE_FILE $DEVICE_USER@$DEVICE_IP:/home/$DEVICE_USER/

# Install the new firmware on the device
sshpass -p $DEVICE_PASS ssh $DEVICE_USER@$DEVICE_IP "sudo flashrom -w /home/$DEVICE_USER/$FIRMWARE_FILE"
```

</details>

## Support and requirements <a href="#important-notes" id="important-notes"></a>

The Container step has the following infrastructure and registry requirements.

### Infrastructure provider support <a href="#infrastructure-provider-support" id="infrastructure-provider-support"></a>

The Harness Container step supports Kubernetes infrastructures only.

### Registry support <a href="#registry-support" id="registry-support"></a>

Harness can fetch containers from the following registries:

* Docker Hub
* Google Container Registry
* AWS Elastic Container Registry
* Azure Container Registry
* Artifactory
* Nexus

## Add a Container step <a href="#add-a-container-step" id="add-a-container-step"></a>

You add the Container step in the CD stage **Execution** section. The Container step is supported in all the deployment strategies (rolling, blue green, canary, custom). This step requires delegate version 1.0.780xx or later.

{% tabs %}
{% tab title="Visual" %}

1. In the CD stage **Execution** section, select **Add Step**, and then select **Container Step**.
2. Enter the following settings and select **Apply Changes**.
   {% endtab %}

{% tab title="YAML" %}

1. In **Pipeline Studio**, select **YAML**.
2. Paste the following YAML example and select **Save**:

```yaml
              - step:
                  type: Container
                  name: Test
                  identifier: Test
                  spec:
                    connectorRef: Docker_Hub_with_Pwd
                    image: maven:3.6.3-jdk-8
                    command: echo "Run some smoke tests"
                    shell: Sh
                    infrastructure:
                      type: KubernetesDirect
                      spec:
                        connectorRef: K8s_Cluster_1
                        namespace: default
                        resources:
                          limits:
                            cpu: "0.5"
                            memory: 500Mi
                    outputVariables: []
                    envVariables: {}
                  timeout: 1d
```

{% endtab %}
{% endtabs %}

***

### Name <a href="#name" id="name"></a>

Use the following steps to name the step:

1. Enter a name for the step.

### Timeout <a href="#timeout" id="timeout"></a>

You can use:

* `w` for weeks.
* `d` for days.
* `h` for hours.
* `m` for minutes.
* `s` for seconds.
* `ms` for milliseconds.

The maximum is `53w`.

Timeouts can be set at the pipeline level also, in the pipeline **Advanced Options**.

### Container registry <a href="#container-registry" id="container-registry"></a>

Use the following steps to configure the container registry:

1. Select or create a Harness connector that connects to the Docker registry where your container is hosted.
2. Ensure that the user account used in the connector has permission to fetch the container.

### Image <a href="#image" id="image"></a>

Use the following steps to specify the image:

1. Enter the image and tag to use for this container, for example: `maven:3.6.3-jdk-8` or `curlimages/curl:7.73.0`.

If you are fetching the image from a provider that requires the Docker image's FQN (fully-qualified name), such as GCR or ECR, use the FQN here.

Here are some examples:

* Docker Hub: `maven:3.6.3-jdk-8`
* GCR: `us.gcr.io/<PROJECT_ID>/my-image`
* ECR: `<ACCOUNT_ID>.dkr.ecr.us-east-1.amazonaws.com/todolist:0.2`

### Shell <a href="#shell" id="shell"></a>

Use the following steps to select the shell:

1. Select the type of shell you want to run your script.

### Command <a href="#command" id="command"></a>

Use the following steps to specify the command:

1. Enter the commands you want to run in the container.

You do not need to create a Kubernetes deployment and service for the container in your script, because Harness handles Kubernetes orchestration.

You also do not need to add `docker run` to your script.

Instead, add the contents of the script you want to execute.

For example, if you would normally run this:

```bash
docker run --rm -it -v $(pwd):/app -w /app maven:3.6.3-jdk-8 bash -c "..."
```

Enter what you would write in `...`.

### Connector <a href="#connector" id="connector"></a>

Use the following steps to select the connector:

1. Select or create a Harness connector to connect to the cluster and namespace where you want to run the step.

### Namespace <a href="#namespace" id="namespace"></a>

Use the following steps to specify the namespace:

1. Enter the namespace in the cluster where you want to run the step.

### Set container resources <a href="#set-container-resources" id="set-container-resources"></a>

Use the following steps to set container resources:

1. Set the maximum resources limit values for the resources used by the container at runtime.

* **Limit Memory:** The maximum memory that the container can use. You may express memory as a plain integer or as a fixed-point number using the suffixes G or M. You may also use the power-of-two equivalents Gi and Mi.
* **Limit CPU:** The maximum number of cores that the container can use. CPU limits are measured in cpu units. Fractional requests are allowed; you can specify one hundred millicpu as 0.1 or 100m.

For more information, go to [resource units in Kubernetes](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/#resource-units-in-kubernetes) from Kubernetes.

### Image pull policy <a href="#image-pull-policy" id="image-pull-policy"></a>

Use the following steps to image pull policy:

1. Select an option to set the pull policy for the image:

* **Always:** The kubelet queries the container image registry to resolve the name to an image digest every time the kubelet launches a container. If the kubelet encounters an exact digest cached locally, it uses its cached image; otherwise, the kubelet downloads (pulls) the image with the resolved digest, and uses that image to launch the container.
* **If Not Present:** The image is pulled only if it is not already present locally.
* **Never:** The image is assumed to exist locally. No attempt is made to pull the image.

### Output variables <a href="#output-variables" id="output-variables"></a>

Output variables are outputs that are defined and captured after the Container step execution.

Output variables expose environment variables for use by other steps/stages of the pipeline. You may reference the output variable of a step using the step Id and the name of the variable in output variables.

The following example shows how to use output variables.

1. In the Command in a step, export a new variable using the following syntax:

   ```bash
   export myVar=varValue
   ```

   ![picture 1](https://3694223630-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fy1JhZ4oKIppwY7d5AhPj%2Fuploads%2Fgit-blob-e5c8ff72765bb7d6881b27657e03acd5a7ad0469%2Fbeced40976dc4e9f479faaa2a93c1d8f6e7b7a7ed20ca412cc2afd1cf8f7c1a1.png?alt=media)
2. In a later Shell Script step, reference the output variable:

   ```
   echo <+steps.S1.ContainerStep.output.outputVariables.myVar>
   ```

   The syntax for referencing output variables between steps in the same stage looks similar to the example below.

   ```
   <+[stepId].ContainerStep.output.outputVariables.[varName]>
   ```

   The syntax for referencing output variables between steps in different stages looks similar to the example below.

   ```
   <+stages.[stageID].execution.steps.[stepId].ContainerStep.output.outputVariables.[varName]>
   ```

### Environment variables <a href="#environment-variables" id="environment-variables"></a>

Environment variables may be injected into the container and used in the script in **Command**.

When using these environment variables, make sure to enter a **Name** and **Value** for each variable.

You may also reference environment variables in the script by their name. For example, in Bash, this would be `$var_name` or `${var_name}`. In Windows PowerShell, the reference would be `$Env:varName`.

For **Value**, you may enter [fixed values, runtime inputs, and expressions](https://developer.harness.io/harness-platform/use-harness-platform/variables-and-expressions/runtime-inputs).

For example, if you created the environment variables `DB_HOST`, `DB_URL`, and `DB_PASSWORD`, your script could reference them like this:

```bash
DB_URL=$DB_URL
user=$DB_HOST
password=$DB_PASSWORD
```

For example, you can set **Value** as an expression and reference the value of some other setting in the stage or pipeline.

{% hint style="info" %}
**Allowed values** for environment variables are not supported in container step configuration.

**Workaround**: To enforce allowed values, define a stage variable with constraints and reference that variable in the container step's environment variable field.
{% endhint %}

### Select the operating system <a href="#select-the-operating-system" id="select-the-operating-system"></a>

Select the OS for the container.

### Volumes <a href="#volumes" id="volumes"></a>

This setting is the same as Kubernetes volumeMounts. Harness supports Host Path (hostPath), Empty Directory (emptyDir), and Persistent Volume Claim (persistentVolumeClaim).

### Service account name <a href="#service-account-name" id="service-account-name"></a>

Specify a Kubernetes service account for step containers to use when communicating with the Kubernetes API server. Leave blank to use the namespace's default service account.

### Automount service account token <a href="#automount-service-account-token" id="automount-service-account-token"></a>

An application running inside a pod can access the Kubernetes API using automatically mounted service account credentials. For more information, go to [Accessing the cluster](https://kubernetes.io/docs/tasks/access-application-cluster/access-cluster/) from Kubernetes.

To use a different service account, enter its name here.

### Labels <a href="#labels" id="labels"></a>

Enter any labels to apply to the pods.

### Annotations <a href="#annotations" id="annotations"></a>

Enter any annotations to apply to the pods.

### Privileged <a href="#privileged" id="privileged"></a>

The standard `privileged` property for Kubernetes `securityContext`.

When this setting is enabled, it grants the container elevated privileges within the underlying host environment. This means that the container has access to all Linux kernel capabilities and devices, similar to running processes outside the container. It effectively removes the isolation provided by the container runtime and can potentially pose security risks if not used carefully.

### Allow privilege escalation <a href="#allow-privilege-escalation" id="allow-privilege-escalation"></a>

The standard `allowPrivilegeEscalation` property for Kubernetes `securityContext`.

When this setting is enabled, it allows the container to gain additional privileges beyond those initially granted during container startup.

### Add capabilities <a href="#add-capabilities" id="add-capabilities"></a>

The standard `add` setting for the `capabilities` property in the Kubernetes `securityContext`.

### Drop capabilities <a href="#drop-capabilities" id="drop-capabilities"></a>

The standard `drop` setting for the `capabilities` property in the Kubernetes `securityContext`.

### Run as non-root <a href="#run-as-non-root" id="run-as-non-root"></a>

Enable this setting to run the container as a non-root user.

### Read-only root filesystem <a href="#read-only-root-filesystem" id="read-only-root-filesystem"></a>

The standard `readOnlyRootFilesystem` setting for the `securityContext` property.

Enable this setting to ensure that the root filesystem of the container is mounted as read-only.

### Run as user <a href="#run-as-user" id="run-as-user"></a>

The standard `runAsUser` setting for the `securityContext` property.

Specify the user ID (UID) under which the container should run.

### Priority class <a href="#priority-class" id="priority-class"></a>

The standard Kubernetes `PriorityClass`.

Enter a standard `priorityClassName` like `system-node-critical`.

### Node selector <a href="#node-selector" id="node-selector"></a>

The standard Kubernetes `nodeSelector`.

Enter a key like `disktype` and value like `ssd`.

### Tolerations <a href="#tolerations" id="tolerations"></a>

The standard Kubernetes `tolerations`. Use the settings to specify that a pod can tolerate (or ignore) the effects of node taints.

### Init timeout <a href="#init-timeout" id="init-timeout"></a>

The standard Kubernetes `timeoutSeconds` for `initContainers`.

### Override image connector <a href="#override-image-connector" id="override-image-connector"></a>

By default, at pipeline runtime, Harness pulls certain images from public Docker Hub repos. These images are only used for backend processes. At runtime, the Harness Delegate makes an outbound connection to the public repo and pulls the images.

The Harness Container Image Registry is dedicated exclusively to Harness-supported images.

You might want to override the default behavior and download your build images from this repo instead. To view the list of images in this registry, enter the following command.

```bash
curl -X  GET https://app.harness.io/registry/_catalog
```

Add or select a connector to use instead of the default connector used for the Harness Container Image Registry.

For more information, go to [Connect to the Harness container image registry](https://developer.harness.io/harness-platform/use-harness-platform/connectors/artifact-repositories/add-a-docker-registry-connector/connect-to-harness-container-image-registry-using-docker-connector).

### Advanced settings <a href="#advanced-settings" id="advanced-settings"></a>

In **Advanced**, you can use the following options:

* [Delegate Selector](https://developer.harness.io/harness-platform/use-harness-platform/delegates/delegate/manage-delegates/select-delegates-with-selectors)
* [Conditional Execution](https://developer.harness.io/harness-platform/use-harness-platform/pipelines/step-skip-condition-settings)
* [Failure Strategy](https://developer.harness.io/harness-platform/use-harness-platform/pipelines/failure-handling/define-a-failure-strategy-on-stages-and-steps)
* [Looping Strategy](https://developer.harness.io/harness-platform/use-harness-platform/pipelines/looping-strategies/looping-strategies-matrix-repeat-and-parallelism)
* [Policy Enforcement](https://developer.harness.io/harness-platform/use-harness-platform/governance/policy-as-code/harness-governance-overview)

## FAQs <a href="#faqs" id="faqs"></a>

<details>

<summary>How can I find the delegate task ID for a failed container step in a Harness pipeline execution?</summary>

The delegate task ID for a failed container step can be found by examining the Harness logs for the specific execution. The logs often contain the task ID within the execution details, particularly within sections related to the failed container step. Harness Cloud Logging provides a user interface to search and filter logs based on execution ID and specific nodes within the pipeline execution graph.

</details>

## Next steps

* Go to [Run step](/continuous-delivery/use-continuous-delivery/cd-building-blocks/cd-steps/containerized-steps/run-step.md) to use a containerized step group instead of the Container step.
* Go to [Kubernetes Rollout step](/continuous-delivery/use-continuous-delivery/deploy-services-on-different-platforms/kubernetes/cd-k8s-ref/kubernetes-rollout-step.md) for the primary Kubernetes deployment step.

{% @harness-feedback/feedback %}
