> 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-gitops/application/use-harness-expressions-in-gitops-applications.md).

# Use Harness expressions in GitOps applications

Inject Harness service variables, environment variables, and secrets into Argo CD manifests at manifest generation time.

Harness GitOps supports Harness expressions in Argo CD application manifests. You can inject dynamic values from your service and environment configurations into Kubernetes manifests during the manifest generation phase, before deployment.

Instead of maintaining separate manifest files for each environment or hardcoding values, use expressions to reference variables defined in Harness services and environments. This gives you a single source of truth for configuration and simplifies deployments across multiple environments.

Expressions resolve during manifest generation through the ConfigManagementPlugin, so values are injected before the manifest is applied to your cluster. This approach improves security, traceability, and consistency across GitOps deployments.

***

## What you will learn from this topic

* How to use [supported expression types](#supported-expression-types) for service variables, environment variables, environment properties, fixed variables, and secrets
* How to [define and reference service variables](#use-service-variables) and [environment variables](#use-environment-variables) in manifests
* How [secret variables](#use-secret-variables) resolve in two stages for secure manifest generation
* How to [override variables per environment](#override-variables-per-environment) at ENV\_SERVICE and ENV\_GLOBAL scope
* How [GitOps expressions differ from pipeline expressions](#gitops-expressions-vs-pipeline-expressions) and when to use each

***

## Before you begin

Complete the following prerequisites before you use Harness expressions in GitOps manifests:

* **GitOps application:** A Harness GitOps application. Go to [Manage GitOps applications](/continuous-delivery/use-gitops/application/manage-gitops-applications.md) to create one.
* **Service variables:** A Harness service with variables defined. Go to [Services overview](/continuous-delivery/use-continuous-delivery/cd-building-blocks/services/services-overview.md) to set up service variables.
* **Minimum agent version:** v0.105.x or later.
* **Minimum gitops-agent-installer-helper version:** v0.0.11 or later.
* **Argo CD Harness Plugin:** When you install the GitOps agent, select **Enable ArgoCD Harness Plugin (Required for Harness expression resolution)**.

![Enable Argo CD Harness Plugin checkbox during GitOps agent installation](https://3694223630-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fy1JhZ4oKIppwY7d5AhPj%2Fuploads%2FySs7lF9D4Ta8pZ5tNud0%2Fagent-checkbox.png?alt=media\&token=95c1995b-312a-4750-86b8-72eb1271333d)

{% hint style="warning" %}
**Existing agent installations**

For existing agent installations (BYOA or Harness-installed Argo CD), you cannot change the **Enable ArgoCD Harness Plugin** checkbox after the initial installation. Configure the Harness Argo CD plugin by running a patch script on your existing Argo CD installation. Go to [Enable Harness expression resolution for existing installations](/continuous-delivery/use-gitops/connect-and-manage/multiple-argo-to-single-harness.md#enable-harness-expression-resolution-for-existing-installations) to review the setup steps.

For new agent installations, select **Enable ArgoCD Harness Plugin (Required for Harness expression resolution)** during GitOps agent installation to use this feature.
{% endhint %}

***

## Supported expression types

Harness GitOps supports the following expression types in Argo CD manifests:

* **Service variables:** `<+serviceVariables.*>`: Variables defined at the service level.
* **Environment variables:** `<+env.variables.*>`: Variables defined at the environment level.
* **Environment properties:** `<+env.*>`: Environment metadata (name, type, identifier, and related fields).
* **Fixed variables:** `<+variable.*>`: Account, organization, or project-level variables.
* **Secrets:** `<+serviceVariables.*>` or `<+env.variables.*>` when the variable type is Secret. Supported only in Kubernetes objects of `kind: Secret`. Resolution uses a two-stage process: the expression is rewritten at manifest generation time, and the secret value is injected at deployment time.

**Resolution timing:** Manifest generation time (pre-deployment, not runtime).

**Override priority:** ENV\_SERVICE > Service > ENV\_GLOBAL > Environment.

***

## Use service variables

Service variables are defined at the service level and are specific to that service. Use them for values that vary by service and may need different values per environment.

### Define service variables

Perform the following steps to add a service variable in Harness:

1. In your Harness project, go to **Deployments** -> **Services**.
2. Select your service, then go to the **Configuration** tab.
3. Under **Variables**, click **Add Variable**.
4. Enter a name, select a type (String, Number, Secret, and so on), and provide a value.
5. Save the service.

### Use service variables in manifests

Reference service variables in your manifests with the `<+serviceVariables.variableName>` syntax:

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
spec:
  replicas: <+serviceVariables.replicas>
  template:
    spec:
      containers:
      - name: myapp
        image: myapp:<+serviceVariables.imageTag>
        env:
        - name: API_KEY
          value: <+serviceVariables.apiKey>
```

Common use cases for service variables include the following:

* Replica counts
* Image tags
* Service-specific configuration values
* API keys or tokens

***

## Use environment variables

Environment variables are defined at the environment level and apply to all services deployed to that environment. Use them for values that are consistent across all services in an environment.

### Define environment variables

Perform the following steps to add an environment variable in Harness:

1. In your Harness project, go to **Deployments** -> **Environments**.
2. Select your environment, then go to the **Configuration** tab.
3. Under **Variables**, click **Add Variable**.
4. Enter a name, select a type, and provide a value.
5. Save the environment.

### Use environment variables in manifests

Reference environment variables with the `<+env.variables.variableName>` syntax:

```yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
  namespace: <+env.variables.namespace>
data:
  log-level: <+env.variables.logLevel>
  cluster-name: <+env.variables.clusterName>
```

Common use cases for environment variables include the following:

* Namespace names
* Cluster names
* Environment-specific configuration (log levels, feature flags)
* Database connection strings

***

## Use environment properties

Environment properties provide access to environment metadata that is automatically available in Harness. These are read-only values that describe the environment itself.

The following environment properties are available:

* `<+env.name>`: Environment display name
* `<+env.identifier>`: Environment identifier
* `<+env.type>`: Environment type (`Production` or `PreProduction`)
* `<+env.description>`: Environment description
* `<+env.color>`: Environment color code

The following example uses environment properties in a ConfigMap:

```yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  environment-name: <+env.name>
  environment-type: <+env.type>
  environment-id: <+env.identifier>
```

***

## Use fixed variables

Fixed variables are defined at the account, organization, or project level and are accessible across all services and environments. You cannot override them at the service or environment level, so use them for global configuration values.

### Define fixed variables

Perform the following steps to define a fixed variable:

1. Go to **Account Settings**, **Organization Settings**, or **Project Settings** (depending on scope).
2. Select **Variables**.
3. Click **Add Variable** and define your variable.
4. Save the settings.

### Use fixed variables in manifests

Reference fixed variables with the appropriate scope prefix:

```yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  account-id: <+variable.account.companyId>
  org-region: <+variable.org.defaultRegion>
  project-name: <+variable.projectCode>
```

Keep these variable type distinctions in mind:

* `<+env.variables.xyz>`: Environment variables (can be overridden at ENV\_GLOBAL level)
* `<+env.name>`: Environment metadata properties (fixed, read-only)
* `<+variable.account.xyz>`: Fixed variables (cannot be overridden)

***

## Use secret variables

When you define a variable as type **Secret** in Harness, it follows a two-stage resolution process so sensitive values stay secure.

### How secret resolution works

Secret variables resolve in two stages:

1. **Expression resolution (manifest generation):** The expression `<+serviceVariables.secretName>` is converted to `<+secrets.getValue('secretReference')>` format.
2. **Secret retrieval (deployment time):** The actual secret value is injected when the manifest is applied to the cluster.

This two-stage approach ensures that:

* Secret references are properly formatted during manifest generation.
* Actual secret values are retrieved only at deployment time.
* Harness does not need to access your secret manager during the manifest generation phase.

### Define a secret variable

Perform the following steps to define a secret variable:

1. In your service or environment, add a variable with type **Secret**.
2. In the value field, provide the reference to your Harness secret (for example, `account.prodDbPassword` or `org.apiKey`).
3. Save the configuration.

### Use secret variables in manifests

The following example shows a secret variable definition and its use in a Kubernetes Secret:

```yaml
# Service variable definition in Harness
variables:
  - name: dbPassword
    type: Secret
    value: account.prodDbPassword

# In your Kubernetes manifest
apiVersion: v1
kind: Secret
metadata:
  name: app-secrets
type: Opaque
stringData:
  db-password: <+serviceVariables.dbPassword>
```

**Resolution process:**

1. After expression resolution (Stage 1):

```yaml
stringData:
  db-password: <+secrets.getValue('account.prodDbPassword')>
```

2. After secret retrieval (Stage 2, deployment time):

```yaml
stringData:
  db-password: actual-secret-value-from-harness
```

{% hint style="info" %}
**Secret objects only**

Use secret variables only in Kubernetes `Secret` objects. The ConfigManagementPlugin resolves `<+secrets.getValue()>` expressions only when they appear within a Secret resource.
{% endhint %}

***

## Override variables per environment

You can override variable values for specific service and environment combinations. Use overrides when the same variable needs different values depending on where the service is deployed.

### Override service variables (ENV\_SERVICE)

Service variables can be overridden for a specific service and environment combination. This is useful when a service needs different configuration values in different environments.

**Override priority:** ENV\_SERVICE override > Base service variable

**Example:** Override replica count for production

1. In your service, define a base variable:

```yaml
variables:
  - name: replicas
    value: "3"
```

2. Create an ENV\_SERVICE override for the production environment:
   * Go to **Deployments** -> **Environments**, then select your environment.
   * Go to **Service Overrides**.
   * Select your service.
   * Add an override for the `replicas` variable with value `"10"`.
3. When deploying to production:

```yaml
spec:
  replicas: <+serviceVariables.replicas>  # Resolves to "10" in production, "3" elsewhere
```

### Override environment variables (ENV\_GLOBAL)

Environment variables can be overridden at the environment level, affecting all services deployed to that environment.

**Override priority:** ENV\_GLOBAL override > Base environment variable

**Example:** Override log level for all services in production

1. Define a base environment variable:

```yaml
# Environment: production
variables:
  - name: logLevel
    value: "INFO"
```

2. Create an ENV\_GLOBAL override:
   * Go to **Deployments** -> **Environments**, then select your environment.
   * Go to **Configuration** -> **Variables**.
   * Override the `logLevel` variable with value `"DEBUG"`.
3. All services in production use:

```yaml
env:
- name: LOG_LEVEL
  value: <+env.variables.logLevel>  # Resolves to "DEBUG" for all services in production
```

### Override scope summary

The following table summarizes override scope by variable type:

| Variable type         | Override level | Scope                             |
| --------------------- | -------------- | --------------------------------- |
| Service variables     | ENV\_SERVICE   | Service-specific per environment  |
| Environment variables | ENV\_GLOBAL    | Environment-wide for all services |

***

## GitOps expressions vs pipeline expressions

GitOps expressions differ from pipeline expressions in several important ways. Understanding these differences helps you choose the right expression type for your use case.

### Key differences

The following table compares GitOps expressions and pipeline expressions:

| Aspect                    | GitOps expressions                                                | Pipeline expressions                                                                     |
| ------------------------- | ----------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| **Resolution time**       | Manifest generation (pre-deployment)                              | Runtime (during pipeline execution)                                                      |
| **Context**               | Service + Environment + Metadata only                             | Full pipeline context (stage, step, infrastructure, and related fields)                  |
| **Override types**        | ENV\_SERVICE, ENV\_GLOBAL                                         | Multiple override levels (infrastructure, stage, service, and related levels)            |
| **Available expressions** | `serviceVariables.*`, `env.*`, `secrets.getValue()`, `variable.*` | Full library (`pipeline.*`, `artifact.*`, `infra.*`, `stage.*`, and related expressions) |

### Limitations

GitOps expressions do not support the following:

* Pipeline-specific expressions (`<+pipeline.*>`, `<+stage.*>`)
* Artifact expressions (`<+artifact.*>`)
* Infrastructure expressions (`<+infra.*>`)
* Output variables from previous pipeline steps
* Runtime pipeline context (execution details, step outputs, and related context)

### When to use GitOps expressions

Use GitOps expressions in the following cases:

* You need values that are known at manifest generation time.
* Values are tied to service or environment configuration.
* You want to inject values into Kubernetes manifests before deployment.
* You need environment-specific or service-specific configuration.

### When to use pipeline expressions

Use pipeline expressions in the following cases:

* You need runtime values (artifact versions, build numbers, and related values).
* Values depend on pipeline execution context.
* You need step outputs or infrastructure details.
* Values are generated during pipeline execution.

***

## Example: Complete application manifest

This example shows a complete Kubernetes deployment manifest that uses multiple Harness expression types.

### Deployment with multiple expression types

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
  namespace: <+env.variables.namespace>  # Environment variable
  labels:
    environment: <+env.type>  # Environment property (Production/PreProduction)
    app: myapp
spec:
  replicas: <+serviceVariables.replicas>  # Service variable (Number type)
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - name: myapp
        image: myapp:<+serviceVariables.imageTag>  # Service variable (String)
        env:
        - name: LOG_LEVEL
          value: <+env.variables.logLevel>  # Environment variable
        - name: API_KEY
          valueFrom:
            secretKeyRef:
              name: app-secrets
              key: api-key
        - name: REGION
          value: <+serviceVariables.region>  # Service variable
        - name: ENVIRONMENT_NAME
          value: <+env.name>  # Environment property
        resources:
          limits:
            memory: <+serviceVariables.memoryLimit>
            cpu: <+serviceVariables.cpuLimit>
          requests:
            memory: <+serviceVariables.memoryRequest>
            cpu: <+serviceVariables.cpuRequest>
```

### ConfigMap example

```yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
  namespace: <+env.variables.namespace>
data:
  # Environment metadata
  environment-name: <+env.name>
  environment-type: <+env.type>
  environment-id: <+env.identifier>
  
  # Service variables
  max-connections: "<+serviceVariables.maxConnections>"
  cache-ttl: <+serviceVariables.cacheTtl>
  
  # Environment variables
  log-level: <+env.variables.logLevel>
  cluster-name: <+env.variables.clusterName>
  
  # Nested expressions
  api-endpoint: "https://api.<+serviceVariables.region>.example.com"
  
  # Fixed variables
  company-id: <+variable.account.companyId>
```

### Secret example

```yaml
apiVersion: v1
kind: Secret
metadata:
  name: app-secrets
  namespace: <+env.variables.namespace>
type: Opaque
stringData:
  # Secret variables resolve in two stages:
  # 1. Expression resolution: <+serviceVariables.apiKey> → <+secrets.getValue('account.myApiKey')>
  # 2. Secret retrieval: Actual value injected at deployment time
  api-key: <+serviceVariables.apiKey>
  db-password: <+serviceVariables.dbPassword>
  oauth-token: <+serviceVariables.oauthToken>
```

***

## Troubleshooting

Use the following entries when expressions do not resolve as expected. Try a hard refresh of the application to invalidate cache before deeper debugging; that step resolves many issues.

<details>

<summary>Expression not resolving</summary>

**Symptom:** The expression appears literally in the deployed manifest instead of the resolved value (for example, `<+serviceVariables.replicas>` instead of `3`).

**Possible causes and solutions:**

1. **Variable does not exist:**
   * Verify the variable exists in the Harness UI (Service -> Variables or Environment -> Variables).
   * Check that the variable name matches exactly (expressions are case-sensitive).
   * Ensure you are using the correct scope (service variable versus environment variable).
2. **ConfigManagementPlugin not configured:**
   * Verify the Argo CD Harness Plugin is enabled in your GitOps agent.
   * Check that your application uses the Harness plugin for manifest generation.
   * Review the agent installation logs for plugin configuration errors.
3. **Typo in expression:**
   * Confirm the expression syntax: `<+serviceVariables.variableName>`.
   * Ensure there are no extra spaces or special characters.
   * Verify the variable name matches the exact spelling in Harness.

</details>

<details>

<summary>Override not working</summary>

**Symptom:** A variable override is configured but the original value is still used.

**Possible causes and solutions:**

1. **Wrong override scope:**
   * Override service variables at ENV\_SERVICE level (Service Overrides in the environment).
   * Override environment variables at ENV\_GLOBAL level (Environment Variables).
   * You cannot override service variables using ENV\_GLOBAL overrides.
2. **Override not saved:**
   * Verify the override was saved in the Harness UI.
   * Check that you selected the correct service and environment combination for ENV\_SERVICE overrides.
   * Ensure the variable name in the override matches exactly (case-sensitive).
3. **Override value is invalid:**
   * Check that the override value is not null or empty.
   * Verify the value type matches the variable type (Number versus String).
   * Ensure the override is enabled and active.

</details>

<details>

<summary>Secret not resolving</summary>

**Symptom:** The secret variable shows the expression or `<+secrets.getValue()>` in the deployed pod instead of the actual secret value.

**Troubleshooting steps:**

1. **Check variable type:**
   * Verify the variable is defined as type **Secret** (not String).
   * Secret variables must be type Secret to trigger the two-stage resolution.
2. **Verify secret reference:**
   * Check that the secret exists in Harness Secrets Manager.
   * Verify the secret reference format is correct (`account.secretName` or `org.secretName`).
   * Ensure you have access to the secret at the appropriate scope.
3. **Check pod environment:**

```bash
# Check actual pod environment
kubectl exec pod-name -- env | grep API_KEY
# Should show actual secret value, not expression
```

* If the literal expression appears in the pod, ConfigManagementPlugin secret resolution failed.
* If the value is null or empty, the secret does not exist or access is denied.
* Review ConfigManagementPlugin logs for secret resolution errors.

</details>

<details>

<summary>Numeric variable becomes string</summary>

**Symptom:** A numeric value appears as a string in the manifest (for example, `replicas: "3"` instead of `replicas: 3`).

**Cause:** The variable was defined as String type instead of Number type.

**Solution:**

1. Go to your service or environment variables.
2. Edit the variable and change the type from **String** to **Number**.
3. Save the configuration.
4. The value is preserved as a number in the manifest.

```yaml
# Correct variable definition
variables:
  - name: replicas
    type: Number  # Not String
    value: 3
```

</details>

***

## Next steps

* [Manage GitOps applications](/continuous-delivery/use-gitops/application/manage-gitops-applications.md): Create and configure applications that consume these expressions.
* [Application configuration options](/continuous-delivery/use-gitops/application/manage-gitops-applications.md#application-configuration-options): Review secret expression options in application manifests.
* [Secret Injection Harness Plugin](/continuous-delivery/use-gitops/security/secret-injection-harness-plugin.md): Inject secrets with the Harness plugin for GitOps.

{% @harness-feedback/feedback %}
