> 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/self-managed-enterprise-edition/use-self-managed-enterprise-edition/smp-installationupgrade/operator-installation/install/install-with-smp-operator.md).

# Install with the SMP Operator

This topic covers deploying the Harness SMP Operator into an existing Kubernetes cluster using either **helm** or the **clustermgr** CLI.

You can install the Harness SMP Operator in two ways:

* **helm** - Use the `platform-installer` Helm chart directly with `helm install`.
* **clustermgr** - Use the `clustermgr` CLI, which bootstraps the Helm chart for you.

Both methods deploy the same underlying Helm chart. Choose the method that fits your operational workflow.

***

### Prerequisites <a href="#prerequisites" id="prerequisites"></a>

The following table lists what each installation method requires, since `kubectl` and `helm` are mandatory for a Helm install but optional with clustermgr.

| Requirement         | Helm     | clustermgr | Description                                                       |
| ------------------- | -------- | ---------- | ----------------------------------------------------------------- |
| **kubectl**         | Required | Optional   | Configured with cluster admin access                              |
| **helm**            | Required | Optional   | for Helm install                                                  |
| **Storage Class**   | Required | Required   | Dynamic volume provisioning                                       |
| **DNS**             | Required | Required   | Externally resolvable hostname                                    |
| **Registry Access** | Required | Required   | Access to `pkg.harness.io` (online) or private registry (air-gap) |

***

### Step 1: Get the Installer <a href="#step-1-get-the-installer" id="step-1-get-the-installer"></a>

{% tabs %}
{% tab title="helm" %}
The `platform-installer` Helm chart can be downloaded in one of two ways:

**Option 1: Pull the chart and store it on the VM**

From a machine with internet access:

```bash
helm pull oci://pkg.harness.io/g_ix-gj_sisgiituoe06eq/cxe-public-helm/platform-installer --version <VERSION> -d .
```

This downloads `platform-installer-<VERSION>.tgz`. Transfer the `.tgz` to your jump box / VM that can reach the target cluster.

**Option 2: Push the chart to your own Helm repository**

```bash
helm pull oci://pkg.harness.io/g_ix-gj_sisgiituoe06eq/cxe-public-helm/platform-installer --version <VERSION> -d .
helm push platform-installer-<VERSION>.tgz oci://<your-registry>/<repo>
```

Then install from your own repository:
{% endtab %}

{% tab title="clustermgr" %}
Download and extract the `clustermgr` CLI:

```bash
curl -L -o harness-clustermgr.tgz \
  "https://pkg.harness.io/pkg/G_iX-gJ_SiSGIITUoe06eQ/platform-installer/files/harness-clustermgr/<VERSION>/harness-clustermgr-<VERSION>-linux-amd64.tgz"

tar -xzf harness-clustermgr.tgz
cd harness-clustermgr
```

**Package contents:**

```
harness-clustermgr/
├── clustermgr                    # CLI binary
├── config.yaml
├── harness/
│   ├── pi-values.yaml            # Go template, resolved at install time
│   └── platform-installer-*.tgz  # Helm chart
├── values-migrator/
│   └── generate-harness-values.sh  # Migration script for existing Harness installs
└── tools/
    ├── kubectl
    ├── helm
    └── yq
```

{% endtab %}
{% endtabs %}

***

### Step 2: Configure and Install <a href="#step-2-configure-and-install" id="step-2-configure-and-install"></a>

{% tabs %}
{% tab title="helm" %}
Create `override.yaml` with your base settings:

```yaml
# override.yaml <a href="#overrideyaml" id="overrideyaml"></a>
cluster:
  version: <WORKFLOW_VERSION>
  name: harness
  profile: <PROFILE>
  tfi:
    dns: <DNS>
    userEmail: <EMAIL>
    userPassword: <PASSWORD>
    storageClass: <STORAGE_TYPE>
    ingressType: <INGRESS_TYPE>
```

{% endtab %}

{% tab title="clustermgr" %}
Replace the placeholders with your actual values:

```bash
./clustermgr install-pi \
  --dns <DNS> \
  --namespace <NAMESPACE> \
  --email <EMAIL> \
  --password '<PASSWORD>' \
  --version <WORKFLOW_VERSION> \
  --pi-set cluster.tfi.storageClass=<STORAGE_TYPE> \
  --pi-set cluster.profile=<PROFILE_TYPE>
```

{% endtab %}
{% endtabs %}

***

#### Install with Nginx <a href="#install-with-nginx" id="install-with-nginx"></a>

There are two Nginx scenarios depending on whether you want the installer to deploy `harness-nginx` or use an existing ingress controller.

**Scenario 1: Deploy harness-nginx**

Use this when you do not already have an ingress controller for the platform. The installer deploys `harness-nginx` for you, and by default it is exposed as LoadBalancer.

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

```yaml
# override-nginx-deploy.yaml <a href="#override-nginx-deployyaml" id="override-nginx-deployyaml"></a>
harness-nginx:
  enabled: true
ingressClassName: harness
```

```bash
helm install platform-installer ./platform-installer-<VERSION>.tgz -n harness --create-namespace \
  -f override.yaml \
  -f override-nginx-deploy.yaml
```

{% endtab %}

{% tab title="clustermgr" %}

```bash
./clustermgr install-pi \
  --dns platform.example.com \
  --namespace harness \
  --email admin@example.com \
  --password 'SecurePassword123!' \
  --version 0.43.0 \
  --pi-set cluster.tfi.storageClass=gp2 \
  --pi-set cluster.profile=medium
```

{% endtab %}
{% endtabs %}

**Scenario 2: Use existing nginx ingress controller**

Use this when your cluster already has an nginx ingress controller and you want to reuse it. In this case, keep `harness-nginx` disabled and point the installer to your existing ingress class (for example, `nginx`).

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

```yaml
# override-nginx-existing.yaml <a href="#override-nginx-existingyaml" id="override-nginx-existingyaml"></a>
ingressClassName: nginx
```

```bash
helm install platform-installer ./platform-installer-<VERSION>.tgz -n harness --create-namespace \
  -f override.yaml \
  -f override-nginx-existing.yaml
```

{% endtab %}

{% tab title="clustermgr" %}
Use the same install command and set the existing ingress class:

```bash
./clustermgr install-pi \
  --dns platform.example.com \
  --namespace harness \
  --email admin@example.com \
  --password 'SecurePassword123!' \
  --version 0.43.0 \
  --pi-set cluster.tfi.storageClass=gp2 \
  --pi-set cluster.profile=medium \
  --pi-set ingressClassName=nginx
```

{% endtab %}
{% endtabs %}

#### Install with Istio <a href="#install-with-istio" id="install-with-istio"></a>

There are three Istio deployment scenarios depending on your cluster state.

**Scenario 1: Fresh Istio install**

No Istio exists in the cluster. The installer deploys the full Istio stack (istio-base, istiod, ingressgateway) along with the Gateway and VirtualService.

{% tabs %}
{% tab title="helm" %}
Use `override-istio-full.yaml` and install with Helm:

```yaml
# override-istio-full.yaml <a href="#override-istio-fullyaml" id="override-istio-fullyaml"></a>
cluster:
  workflows:
    IstioInstall:
      items:
        - name: istio-base
          initCfg:
            runOnInit: true
        - name: istiod
          initCfg:
            runOnInit: true
        - name: istio-ingressgateway
          initCfg:
            runOnInit: true
        - name: istio-gateway
          initCfg:
            runOnInit: true
        - name: istio-virtualservice
          initCfg:
            runOnInit: true
```

```bash
helm install platform-installer ./platform-installer-<VERSION>.tgz -n harness --create-namespace \
  -f override.yaml \
  -f override-istio-full.yaml
```

{% endtab %}

{% tab title="clustermgr" %}

```bash
./clustermgr install-pi \
  --dns platform.example.com \
  --email admin@example.com \
  --password 'SecurePassword123!' \
  --version 0.43.0 \
  --pi-set cluster.profile=medium \
  -i ingressType=istio \
  -i istioInstall=true
```

{% endtab %}
{% endtabs %}

**Scenario 2: Existing Istio, no Gateway/VirtualService**

Istio is already installed in the cluster but there is no Gateway or VirtualService configured for the platform. The installer skips Istio deployment and creates only the Gateway and VirtualService resources.

For certificate handling requirements with existing Istio, see [User has existing Istio](/self-managed-enterprise-edition/use-self-managed-enterprise-edition/smp-installationupgrade/operator-installation/prerequisites/dns-and-tls-certificates.md#scenario-2-you-have-existing-istio).

{% tabs %}
{% tab title="helm" %}
Use `override-istio-gateway-vs.yaml` and install with Helm:

```yaml
# override-istio-gateway-vs.yaml <a href="#override-istio-gateway-vsyaml" id="override-istio-gateway-vsyaml"></a>
cluster:
  workflows:
    IstioInstall:
      items:
        - name: istio-gateway
          initCfg:
            runOnInit: true
        - name: istio-virtualservice
          initCfg:
            runOnInit: true
```

```bash
helm install platform-installer ./platform-installer-<VERSION>.tgz -n harness --create-namespace \
  -f override.yaml \
  -f override-istio-gateway-vs.yaml
```

{% endtab %}

{% tab title="clustermgr" %}

```bash
./clustermgr install-pi \
  --dns platform.example.com \
  --email admin@example.com \
  --password 'SecurePassword123!' \
  --version 0.43.0 \
  --pi-set cluster.profile=medium \
  -i ingressType=istio
```

{% endtab %}
{% endtabs %}

**Scenario 3: Existing Istio + Gateway**

Istio and a Gateway already exist. The installer only creates the VirtualService, using the existing Gateway by name.

For certificate handling requirements with existing Istio, see [User has existing Istio](/self-managed-enterprise-edition/use-self-managed-enterprise-edition/smp-installationupgrade/operator-installation/prerequisites/dns-and-tls-certificates.md#scenario-2-you-have-existing-istio).

{% tabs %}
{% tab title="helm" %}
Use `override-istio-vs.yaml` and install with Helm:

```yaml
# override-istio-vs.yaml <a href="#override-istio-vsyaml" id="override-istio-vsyaml"></a>
cluster:
  tfi:
    istioGatewayName: my-gateway
    istioGatewayNamespace: istio-system
  workflows:
    IstioInstall:
      items:
        - name: istio-virtualservice
          initCfg:
            runOnInit: true
```

```bash
helm install platform-installer ./platform-installer-<VERSION>.tgz -n harness --create-namespace \
  -f override.yaml \
  -f override-istio-vs.yaml
```

{% endtab %}

{% tab title="clustermgr" %}

```bash
./clustermgr install-pi \
  --dns platform.example.com \
  --email admin@example.com \
  --password 'SecurePassword123!' \
  --version 0.43.0 \
  --pi-set cluster.profile=medium \
  -i ingressType=istio \
  -i istioGatewayName=my-gateway \
  -i istioGatewayNamespace=istio-system
```

{% endtab %}
{% endtabs %}

#### Air-Gapped Install <a href="#air-gapped-install" id="air-gapped-install"></a>

After [mirroring images](/self-managed-enterprise-edition/use-self-managed-enterprise-edition/smp-installationupgrade/operator-installation/prerequisites/registry-setup.md) to your private registry:

{% tabs %}
{% tab title="helm" %}
Set private registry values in `override.yaml`:

```yaml
cluster:
  version: <WORKFLOW_VERSION>
  name: harness
  profile: <PROFILE>
  airGapEnabled: true
  imageRegistryHost: <YOUR_REGISTRY_HOST>
  imageRegistryPathPrefix: <YOUR_REGISTRY_PREFIX>
  imageRegistryUsername: <YOUR_REGISTRY_USERNAME>
  imageRegistryPassword: <YOUR_REGISTRY_PASSWORD>
  tfi:
    dns: <DNS>
    userEmail: <EMAIL>
    userPassword: <PASSWORD>
    storageClass: <STORAGE_TYPE>
    ingressType: <INGRESS_TYPE>
```

Then run:

```bash
helm install platform-installer ./platform-installer-<VERSION>.tgz -n harness --create-namespace \
  -f override.yaml \
  -f override-<ingress>.yaml
```

Replace `override-<ingress>.yaml` with one of: `override-nginx-deploy.yaml`, `override-nginx-existing.yaml`, `override-istio-full.yaml`, `override-istio-gateway-vs.yaml`, or `override-istio-vs.yaml`.
{% endtab %}

{% tab title="clustermgr" %}
For nginx ingress:

```bash
./clustermgr install-pi \
  --dns platform.internal.example.com \
  --namespace harness \
  --registry-host harbor.internal.example.com \
  --registry-prefix harness-platform \
  --registry-user admin \
  --registry-password 'Harbor@123' \
  --email admin@example.com \
  --password 'SecurePass123!' \
  --version 0.43.0 \
  --pi-set cluster.triggerInstall=false \
  --pi-set cluster.tfi.storageClass=local-path \
  --pi-set cluster.profile=medium \
  -i airgap=true
```

For Istio:

```bash
./clustermgr install-pi \
  --dns platform.internal.example.com \
  --registry-host harbor.internal.example.com \
  --registry-prefix harness-platform \
  --registry-user admin \
  --registry-password 'Harbor@123' \
  --version 0.43.0 \
  --pi-set cluster.triggerInstall=false \
  --pi-set cluster.profile=medium \
  -i ingressType=istio \
  -i istioInstall=true \
  -i airgap=true
```

{% endtab %}
{% endtabs %}

***

### Step 3: Access the Installer UI <a href="#step-3-access-the-installer-ui" id="step-3-access-the-installer-ui"></a>

After successful installation, access the UI at:

```
https://<YOUR_DNS>/pi
```

Login with the email and password provided during installation.

***

### Step 4: Verify Installation <a href="#step-4-verify-installation" id="step-4-verify-installation"></a>

```bash
# Check pods <a href="#check-pods" id="check-pods"></a>
kubectl get pods -n harness | grep platform-installer

# Check statefulset <a href="#check-statefulset" id="check-statefulset"></a>
kubectl get statefulset -n harness

# View logs <a href="#view-logs" id="view-logs"></a>
kubectl logs -n harness platform-installer-0 -c pi
```

If all pods are **Running** and the UI is reachable over HTTPS at `/pi`, the installation is complete.

***

### Step 5: Configure and Deploy Harness <a href="#step-5-configure-and-deploy-harness" id="step-5-configure-and-deploy-harness"></a>

#### Login to the Operator <a href="#login-to-the-operator" id="login-to-the-operator"></a>

In the browser, open `https://<YOUR_DNS>/pi` and enter the username and password provided during installation.

[![Operator Login](/files/uEqsA0KD5BxeRsFI0BIj)](https://github.com/iKettles/harness-gitbook/tree/main/self-managed-enterprise-edition/use-self-managed-enterprise-edition/smp-installationupgrade/operator-installation/install/static/operator-login.png)

#### Enable Modules and Add License <a href="#enable-modules-and-add-license" id="enable-modules-and-add-license"></a>

Once logged in, navigate to **Global Variables**. Enable the required modules (e.g., `ci`, `ccm`, `cd`, etc.) and add the license key in the `harnessLicense` field.

[![Global Variables](/files/gBSHvDjgwsMweep04yVW)](https://github.com/iKettles/harness-gitbook/tree/main/self-managed-enterprise-edition/use-self-managed-enterprise-edition/smp-installationupgrade/operator-installation/install/static/global-variables.png)

#### Start the Install Workflow <a href="#start-the-install-workflow" id="start-the-install-workflow"></a>

Save the details and proceed to **Install Workflow**. If any updates are needed to the Harness `values.yaml`, click on **harness** and edit the values in `user/values.yaml` before starting.

Click **Start Install** to begin the Harness platform deployment.

[![Install Workflow](/files/ZNcQaTEi3w8TOWHXPJB0)](https://github.com/iKettles/harness-gitbook/tree/main/self-managed-enterprise-edition/use-self-managed-enterprise-edition/smp-installationupgrade/operator-installation/install/static/install-workflow.png)

#### Monitor Progress <a href="#monitor-progress" id="monitor-progress"></a>

View the progress in **Harness Pods**. Wait for all pods to reach `Running` status.

[![Harness Pods](/files/FlAIQHSabolABB17CNQv)](https://github.com/iKettles/harness-gitbook/tree/main/self-managed-enterprise-edition/use-self-managed-enterprise-edition/smp-installationupgrade/operator-installation/install/static/harness-pods.png)

#### Create Harness Account <a href="#create-harness-account" id="create-harness-account"></a>

Once all pods are running, open the following URL in your browser to sign up and create credentials:

```
https://<YOUR_DNS>/auth/#/signup
```

Once the account is created successfully, it redirects to the sign-in page to log in.

***

### Uninstall the operator <a href="#uninstall-the-operator" id="uninstall-the-operator"></a>

Remove the `platform-installer` Helm release when you no longer need the operator. Deleting the release preserves your data, so delete the persistent volume claim as well only if you want to remove the data too.

```bash
# Uninstall the installer (preserves data) <a href="#uninstall-the-installer-preserves-data" id="uninstall-the-installer-preserves-data"></a>
helm uninstall platform-installer -n harness

# Uninstall and delete PVC (removes all data) <a href="#uninstall-and-delete-pvc-removes-all-data" id="uninstall-and-delete-pvc-removes-all-data"></a>
helm uninstall platform-installer -n harness
kubectl delete pvc data-platform-installer-0 -n harness
```
