> 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/deploy-services-on-different-platforms/kubernetes/cd-k8s-ref/example-kubernetes-manifests-using-go-templating.md).

# Example Kubernetes manifests using Go templating

Example Kubernetes manifests using Go templating and Harness variables, covering public images, private artifacts, and template functions.

Harness CD supports [Go templating](https://godoc.org/text/template) in Kubernetes manifests, which lets you keep your manifests reusable and dynamic using Harness built-in variables. You define variable values in a `values.yaml` file and reference them across your deployment, service, and namespace manifests.

This topic covers ready-to-use manifest examples you can adapt for your own Kubernetes deployments.

***

## What you will learn from this topic

* **Go templating syntax:** How to reference Harness variables and Go template expressions in Kubernetes manifests.
* **Public image example:** A complete `values.yaml` and manifest set for deployments using public container images.
* **Private artifact example:** How to use `<+artifact.imagePullSecret>` for private registries.
* **Template functions:** Quotation, verbatim output with `toYaml`, and indexing into maps and slices.
* **Skip rendering:** How to bypass Go templating for manifests that use a different format.

***

## Public image example

This example uses the Harness artifact reference `<+artifact.image>` and is suitable when the image is not hardcoded in your manifests.

Go to [Add container images as artifacts for Kubernetes deployments](/continuous-delivery/use-continuous-delivery/deploy-services-on-different-platforms/kubernetes/cd-kubernetes-category/add-artifacts-for-kubernetes-deployments.md) to configure the artifact source in your Service Definition.

The manifests use a `values.yaml` file and separate template files for deployment, namespace, and service. Place the template files in a `templates` folder alongside `values.yaml`.

<details>

<summary>values.yaml</summary>

This file uses `<+artifact.image>` to identify the primary artifact added in the Harness Service Definition **Artifacts** section. It also uses `<+stage.name>` to reference a stage variable and `<+infra.namespace>` to reference the namespace from the stage Infrastructure Definition. Service type and ports are hardcoded.

The `name`, `image`, and `namespace` values are referenced in the manifests described below.

```yaml
name: <+stage.name>
replicas: 2

image: <+artifact.image>
# dockercfg: <+artifact.imagePullSecret>

createNamespace: true
namespace: <+infra.namespace>

# Service Type allows you to specify what kind of service you want.
# Possible values for ServiceType are:
# ClusterIP | NodePort | LoadBalancer | ExternalName
serviceType: LoadBalancer

# A Service can map an incoming port to any targetPort.
# targetPort is where the application is listening inside the container.
servicePort: 80
serviceTargetPort: 80

# Specify all environment variables to be added to the container.
# The following two maps, config and secrets, are put into a ConfigMap
# and a Secret, respectively.
# Both are added to the container environment in podSpec as envFrom source.
env:
  config:
    key1: value10
  secrets:
    key2: value2
```

</details>

<details>

<summary>templates/deployment.yaml</summary>

The deployment manifest references the `name` and `image` values from `values.yaml`. It also contains the ConfigMap and Secret objects.

```yaml
{{- if .Values.env.config}}
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{.Values.name}}
data:
{{.Values.env.config | toYaml | indent 2}}
---
{{- end}}

{{- if .Values.env.secrets}}
apiVersion: v1
kind: Secret
metadata:
  name: {{.Values.name}}
stringData:
{{.Values.env.secrets | toYaml | indent 2}}
---
{{- end}}

{{- if .Values.dockercfg}}
apiVersion: v1
kind: Secret
metadata:
  name: {{.Values.name}}-dockercfg
  annotations:
    harness.io/skip-versioning: true
data:
  .dockercfg: {{.Values.dockercfg}}
type: kubernetes.io/dockercfg
---
{{- end}}

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{.Values.name}}-deployment
spec:
  replicas: {{int .Values.replicas}}
  selector:
    matchLabels:
      app: {{.Values.name}}
  template:
    metadata:
      labels:
        app: {{.Values.name}}
    spec:
      {{- if .Values.dockercfg}}
      imagePullSecrets:
      - name: {{.Values.name}}-dockercfg
      {{- end}}
      containers:
      - name: {{.Values.name}}
        image: {{.Values.image}}
        {{- if or .Values.env.config .Values.env.secrets}}
        envFrom:
        {{- if .Values.env.config}}
        - configMapRef:
            name: {{.Values.name}}
        {{- end}}
        {{- if .Values.env.secrets}}
        - secretRef:
            name: {{.Values.name}}
        {{- end}}
        {{- end}}
```

</details>

<details>

<summary>templates/namespace.yaml</summary>

The namespace manifest references the `namespace` value from `values.yaml`.

```yaml
{{- if .Values.createNamespace}}
apiVersion: v1
kind: Namespace
metadata:
  name: {{.Values.namespace}}
{{- end}}
```

</details>

<details>

<summary>templates/service.yaml</summary>

The service manifest references the hardcoded service type and ports from `values.yaml`.

```yaml
apiVersion: v1
kind: Service
metadata:
  name: {{.Values.name}}-svc
spec:
  type: {{.Values.serviceType}}
  ports:
  - port: {{.Values.servicePort}}
    targetPort: {{.Values.serviceTargetPort}}
    protocol: TCP
  selector:
    app: {{.Values.name}}
```

</details>

***

## Private artifact example

When the image is in a private registry, use the expression `<+artifact.imagePullSecret>` in the Secret and Deployment objects in your manifest.

This key imports the credentials from the Docker credentials file in the artifact. It is simpler to add `<+artifact.imagePullSecret>` to `values.yaml` and then reference it in other manifests.

Using the `values.yaml` from the public image example, remove the comment in front of `dockercfg: <+artifact.imagePullSecret>`:

```yaml
name: <+stage.name>
replicas: 2

image: <+artifact.image>
dockercfg: <+artifact.imagePullSecret>

createNamespace: true
namespace: <+infra.namespace>
...
```

You do not need to make changes to the deployment manifest from the previous example. It uses [Go templating](https://godoc.org/text/template) to check for the `dockercfg` value in `values.yaml` and applies it to Secret and Deployment.

The condition `{{- if .Values.dockercfg}}` checks for `dockercfg` in `values.yaml`:

```yaml
...
{{- if .Values.dockercfg}}
apiVersion: v1
kind: Secret
metadata:
  name: {{.Values.name}}-dockercfg
  annotations:
    harness.io/skip-versioning: true
data:
  .dockercfg: {{.Values.dockercfg}}
type: kubernetes.io/dockercfg
---
{{- end}}

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{.Values.name}}-deployment
spec:
  replicas: {{int .Values.replicas}}
  selector:
    matchLabels:
      app: {{.Values.name}}
  template:
    metadata:
      labels:
        app: {{.Values.name}}
    spec:
      {{- if .Values.dockercfg}}
      imagePullSecrets:
      - name: {{.Values.name}}-dockercfg
      {{- end}}
      containers:
      - name: {{.Values.name}}
        image: {{.Values.image}}
...
```

***

## Go templating techniques

The following sections describe Go template functions you can use in your Kubernetes manifests.

### Quotation marks

Wrap a value in `quote` to put quotation marks around whatever string is in the value. This handles values that could otherwise be interpreted as numbers, or empty values, which would cause a parsing error.

```yaml
{{.Values.something | quote}}
```

Use single quotes if the value might contain a YAML-like structure that could cause issues for the YAML parser.

### Verbatim output

Use `indent` and `toYaml` to insert a block from `values.yaml` into the manifest exactly as written.

```yaml
{{.Values.env.config | toYaml | indent 2}}
```

### Indexing structures in templates

If the data passed to the template is a map, slice, or array, you can index into it from the template using the `index` keyword.

The syntax is `{{index x number}}`, where `index` is the keyword, `x` is the data, and `number` is an integer for the index value. For example, `{{index names 2}}` is equivalent to `names[2]`. Add more integers to index deeper: `{{index names 2 3 4}}` is equivalent to `names[2][3][4]`.

The following example uses `index` to merge environment-specific config values with a base config:

<details>

<summary>YAML example</summary>

```yaml
{{- if .Values.env.config}}
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{.Values.name}}-{{.Values.track}}
  labels:
    app: {{.Values.name}}
    track: {{.Values.track}}
  annotations:
    harness.io/skip-versioning: "true"
data:
{{- if hasKey .Values.env .Values.track}}
{{index .Values.env .Values.track "config" | mergeOverwrite .Values.env.config | toYaml | indent 2}}
{{- else }}
{{.Values.env.config | toYaml | indent 2}}
{{- end }}
---
{{- end}}

{{- if .Values.env.secrets}}
apiVersion: v1
kind: Secret
metadata:
  name: {{.Values.name}}-{{.Values.track}}
  labels:
    app: {{.Values.name}}
    track: {{.Values.track}}
stringData:
{{- if hasKey .Values.env .Values.track}}
{{index .Values.env .Values.track "secrets" | mergeOverwrite .Values.env.secrets | toYaml | indent 2}}
{{- else }}
{{.Values.env.secrets | toYaml | indent 2}}
{{- end }}
---
{{- end}}
```

</details>

***

## Skip rendering

In some cases, you might not want to use Go templating because your manifests use a different format.

Select the **Skip Rendering K8s manifest files** option in the [Kubernetes Apply step](/continuous-delivery/use-continuous-delivery/deploy-services-on-different-platforms/kubernetes/cd-k8s-ref/kubernetes-apply-step.md) to have Harness skip Go templating for your manifest files.

***

## Important notes

The following considerations apply when using Go templating in Harness Kubernetes deployments:

* [Harness variables and expressions](https://developer.harness.io/docs/platform/variables-and-expressions/harness-variables) may be added to `values.yaml`, not to the manifests themselves. This provides more flexibility.
* The `values.yaml` file used in a stage Service does not support Helm templating. Use Go templating in `values.yaml`. Helm templating is fully supported in remote Helm charts added to your Service.
* Harness uses Go template version 0.4.5. If you are used to Helm templates, download Go template and test it locally to confirm your manifests will work before adding them to Harness.
* Install Go template version 0.4.5 locally to test your manifests:

  * Mac OS:
    * AMD64: `curl -O https://app.harness.io/public/shared/tools/go-template/release/v0.4.15/bin/darwin/amd64/go-template`
    * ARM64: `curl -O https://app.harness.io/public/shared/tools/go-template/release/v0.4.15/bin/darwin/arm64/go-template`
  * Linux:
    * AMD64: `curl -O https://app.harness.io/public/shared/tools/go-template/release/v0.4.15/bin/linux/amd64/go-template`
    * ARM64: `curl -O https://app.harness.io/public/shared/tools/go-template/release/v0.4.15/bin/linux/arm64/go-template`
  * Windows: `curl -O https://app.harness.io/public/shared/tools/go-template/release/v0.4.15/bin/windows/amd64/go-template.exe`

  After installing, run `./go-template` to view usage:

  ```
  Usage:
    Template: ./go-template <-t templateFilePath> [-f valuesFilePath] [-s variableOverride] [-o outputFolder]
    Version : ./go-template -v
  ```
* Harness uses an internal build of Go templating that cannot be upgraded. It includes [Sprig template functions](http://masterminds.github.io/sprig/), excluding functions that provide access to the underlying OS (`env`, `expandenv`) for security reasons. Additional functions supported are `ToYaml`, `FromYaml`, `ToJson`, and `FromJson`.

***

## Next steps

Explore related Kubernetes deployment topics:

* [Add container images as artifacts for Kubernetes deployments](/continuous-delivery/use-continuous-delivery/deploy-services-on-different-platforms/kubernetes/cd-kubernetes-category/add-artifacts-for-kubernetes-deployments.md): Configure artifact sources in your Service Definition.
* [Kubernetes Apply step](/continuous-delivery/use-continuous-delivery/deploy-services-on-different-platforms/kubernetes/cd-k8s-ref/kubernetes-apply-step.md): Deploy manifests directly with the Apply step.
* [Kubernetes rollback](/continuous-delivery/use-continuous-delivery/deploy-services-on-different-platforms/kubernetes/cd-k8s-ref/kubernetes-rollback.md): Understand how Harness handles rollbacks for Kubernetes deployments.

{% @harness-feedback/feedback %}
