> For the complete documentation index, see [llms.txt](https://metalbear.com/mirrord/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://metalbear.com/mirrord/docs/getting-started/installing-mirrord/operator.md).

# mirrord Operator

The mirrord Operator is a Kubernetes operator that runs persistently in your cluster and manages mirrord sessions. It's the central component that enables all **\[Teams]** features.

## Why the Operator?

In the open-source version of mirrord, each session is standalone - mirrord injects itself into the local process and creates an agent pod directly. This works well for individual use, but doesn't support coordination between users.

The Operator solves this by acting as a centralized control plane:

* **Better security** - Users no longer need permissions to create privileged pods. Only the Operator does. Permissions are managed through Kubernetes RBAC.
* **Concurrent use** - The Operator coordinates multiple mirrord sessions on the same cluster, preventing conflicts.
* **Advanced features** - Support for [policies](/mirrord/docs/sharing-the-cluster/policies.md), [profiles](/mirrord/docs/sharing-the-cluster/profiles.md), [queue splitting](/mirrord/docs/sharing-the-cluster/queue-splitting.md), [DB branching](/mirrord/docs/sharing-the-cluster/db-branching.md), and more.

![mirrord for Teams - Architecture](/files/SGg8znUGUuZgcFn4kCJm)

## Installation

You'll need a mirrord for Teams license. [Register here](https://app.metalbear.com) to get started.

### Helm

Add the MetalBear Helm repository:

```bash
helm repo add metalbear https://metalbear-co.github.io/charts
```

Download the accompanying `values.yaml`:

```bash
curl https://raw.githubusercontent.com/metalbear-co/charts/main/mirrord-operator/values.yaml --output values.yaml
```

### Cloud API key

The Operator authenticates to the mirrord cloud with a **cloud API key** and uses it to obtain its license over the API. This is the default way to install the Operator. Generate a key in the dashboard under **Settings** at [app.metalbear.com](https://app.metalbear.com) — it's shown only once, so store it then.

Provide the key to the chart in one of three ways:

**Kubernetes secret (recommended)** — create a secret in your cluster and reference it via `cloud.apiKey.keyRef`, so the key never lives in your `values.yaml`:

```bash
kubectl create secret generic mirrord-operator-cloud-api-key \
  --namespace mirrord \
  --from-literal=apiKey=<your API key>
```

```yaml
cloud:
  apiKey:
    keyRef: mirrord-operator-cloud-api-key
```

**Google Secret Manager** — store the key in GSM and reference it via `cloud.apiKey.gsmRef`. The Operator reads it using Application Default Credentials (see `sa.gcpSa`):

```yaml
cloud:
  apiKey:
    gsmRef: projects/PROJECT_ID/secrets/SECRET_NAME/versions/latest
```

**Inline (dev/test)** — set the value directly, keeping in mind it then lives in your Helm values:

```yaml
cloud:
  apiKey:
    key: <your API key>
```

Then install:

```bash
helm install -f values.yaml mirrord-operator metalbear/mirrord-operator
```

Rotating and revoking the key are done from the dashboard. When you revoke, you can choose a grace window so the current key keeps working while you roll the Operator over to the new one.

## Air-gapped / offline clusters (Enterprise)

Air-gapped or offline clusters can't reach the cloud to exchange an API key for a license, so Enterprise deployments in that situation use an offline **license certificate** instead. If you have a `license.pem` file, set `license.file.secret.data.license.pem` in `values.yaml` using a YAML literal block:

```yaml
license:
  file:
    secret:
      data:
        license.pem: |
          -----BEGIN CERTIFICATE-----
          <contents of your license.pem file>
          -----END CERTIFICATE-----
```

Alternatively, create a Kubernetes secret and reference it via `license.pemRef` in `values.yaml`:

```bash
kubectl create secret generic mirrord-operator-license-pem \
  --namespace mirrord \
  --from-file=license.pem=/path/to/license.pem
```

Then install:

```bash
helm install -f values.yaml mirrord-operator metalbear/mirrord-operator
```

For a fully self-hosted setup, see the [license server](/mirrord/docs/managing-mirrord/license-server.md).

## License key

{% hint style="warning" %}
**⚠️ Deprecated for cloud authentication**

For clusters that reach the mirrord cloud, the license key is being replaced by the [cloud API key](#cloud-api-key), which is now the default way the Operator authenticates and obtains its license. Existing cloud license-key installations keep working, but new ones should use a cloud API key.

This does not apply to the [license server](/mirrord/docs/managing-mirrord/license-server.md): if you run your own license server, the license key is still the shared secret the Operator uses to authenticate to it (a value you choose, not a mirrord-issued credential) and remains required.
{% endhint %}

Set `license.key` to your key, then install:

```bash
helm install -f values.yaml mirrord-operator metalbear/mirrord-operator
```

Alternatively, create a Kubernetes secret with your license key and reference it via `license.keyRef` in `values.yaml`:

```bash
kubectl create secret generic mirrord-operator-license \
  --namespace mirrord \
  --from-literal=OPERATOR_LICENSE_KEY=<your license key>
```

## Using an Internal Registry (Optional)

Using an internal registry reduces startup time, ingress costs, and removes dependency on GitHub's registry.

### Feature-specific images

These images are only pulled when the corresponding feature is enabled:

| Image                   | Default                                     | Tag              | Description                                                                                   | Override                                                                  |
| ----------------------- | ------------------------------------------- | ---------------- | --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
| Kafka splitting sidecar | `ghcr.io/metalbear-co/operator-kafka-proxy` | Same as operator | JVM sidecar for Kafka splitting (only when `operator.kafkaSplittingSidecar.enabled` is true). | `operator.kafkaSplittingSidecar.image`                                    |
| MSSQL tools             | `ghcr.io/metalbear-co/mssql-tools`          | `latest`         | Sidecar for MSSQL DB branching (provides `sqlcmd`, `sqlpackage`, `bcp`).                      | Env `MSSQL_TOOLS_IMAGE` via `operator.extraEnv`                           |
| Flyway                  | `flyway/flyway`                             | `12`             | Flyway migration runner for DB branching.                                                     | Per-branch `migrations.image`, or `dbPod.migrationImages.flyway.registry` |

### DB branching default database images

DB branch pods pull a database image matching the engine. These are the defaults when no custom image is specified in the branch config:

| Engine     | Default image                                      | Override                                          |
| ---------- | -------------------------------------------------- | ------------------------------------------------- |
| PostgreSQL | `docker.io/library/postgres:{version}`             | `operator.pgBranchConfig` - `dbPod.image`         |
| MySQL      | `docker.io/library/mysql:{version}`                | `operator.mysqlBranchConfig` - `dbPod.image`      |
| MongoDB    | `docker.io/library/mongo:{version}`                | `operator.mongodbBranchConfig` - `dbPod.image`    |
| MSSQL      | `mcr.microsoft.com/mssql/server:{version}`         | `operator.mssqlBranchConfig` - `dbPod.image`      |
| Redis      | `docker.io/library/redis:{version}`                | `operator.redisBranchConfig` - `dbPod.image`      |
| DynamoDB   | `amazon/dynamodb-local:{version}`                  | `operator.dynamodbBranchConfig` - `dbPod.image`   |
| ClickHouse | `docker.io/clickhouse/clickhouse-server:{version}` | `operator.clickhouseBranchConfig` - `dbPod.image` |
| Spanner    | `gcr.io/cloud-spanner-emulator/emulator:{version}` | `operator.spannerBranchConfig` - `dbPod.image`    |

[Generic branches](/mirrord/docs/sharing-the-cluster/db-branching/generic.md) have no default image - the user supplies the full image reference per branch. Admins can restrict which images are allowed with the `allowedImages` glob list under `operator.genericBranchConfig` - `dbPod.allowedImages` (when absent, all images are allowed), and `imagePullSecrets` in the same config covers private registries.

### Copying images

We recommend [regctl](https://regclient.org/) for copying multi-arch images:

```sh
# Get the operator image version
IMAGE_VERSION=$(helm show chart metalbear/mirrord-operator | grep 'appVersion:' | awk '{print $2}')

# Copy operator image
regctl image copy ghcr.io/metalbear-co/operator:$IMAGE_VERSION your-registry/operator:$IMAGE_VERSION

# Get and copy agent image
AGENT_IMAGE_VERSION=$(regctl image config ghcr.io/metalbear-co/operator:$IMAGE_VERSION | jq -r '.config.Labels."metalbear.mirrord.version"')
regctl image copy ghcr.io/metalbear-co/mirrord:$AGENT_IMAGE_VERSION your-registry/mirrord:$AGENT_IMAGE_VERSION
```

Then set in your `values.yaml`:

```yaml
operator:
  image: your-registry/operator
agent:
  image:
    registry: your-registry/mirrord
```

### OpenShift

Apply the following SecurityContextConstraints:

```yaml
kind: SecurityContextConstraints
apiVersion: security.openshift.io/v1
metadata:
  name: scc-mirrord
allowHostPID: true
allowPrivilegedContainer: false
allowHostDirVolumePlugin: true
allowedCapabilities: ["SYS_ADMIN", "SYS_PTRACE", "NET_RAW", "NET_ADMIN"]
runAsUser:
  type: RunAsAny
seLinuxContext:
  type: MustRunAs
users:
  - system:serviceaccount:mirrord:mirrord-operator
  - system:serviceaccount:mirrord:default
```

### GKE Autopilot

In GKE Autopilot the mirrord Operator can be run as a [customer-owned privileged workload](https://docs.cloud.google.com/kubernetes-engine/docs/concepts/about-autopilot-privileged-workloads#customer-owned-privileged-workloads).

Apply the following [WorkloadAllowlist](https://docs.cloud.google.com/kubernetes-engine/docs/how-to/autopilot-privileged-allowlists):

```yaml
apiVersion: auto.gke.io/v1
kind: WorkloadAllowlist
metadata:
  name: mirrord-agent
  annotations:
    autopilot.gke.io/no-connect: "true"
exemptions:
  - autogke-default-linux-capabilities
  - autogke-disallow-hostnamespaces
  - autogke-no-write-mode-hostpath
  - autogke-node-affinity-selector-limitation
matchingCriteria:
  hostPID: true
  containers:
    - name: mirrord-agent
      image: ghcr.io/metalbear-co/mirrord
      command:
        - ./mirrord-agent
      args:
        - "^.*$"
      env:
        - name: "^.*$"
      securityContext:
        capabilities:
          add:
            - SYS_ADMIN
            - SYS_PTRACE
            - NET_ADMIN
        privileged: false
      volumeMounts:
        - name: hostrun
          mountPath: /host/run
        - name: hostvar
          mountPath: /host/var
  volumes:
    - name: hostrun
      hostPath:
        path: /run
    - name: hostvar
      hostPath:
        path: /var
```

**Note:** some Operator configurations might produce mirrord-agent pods that don't match this specification. When that happens, you'll see agent spawn errors in the Operator logs. To get the correct WorkloadAllowlist embedded in those error messages, merge this snippet into your mirrord Operator `values.yaml`:

```yaml
agent:
  annotations:
    cloud.google.com/generate-allowlist: "true"
```

### Verifying the Installation

```bash
mirrord operator status
```

All mirrord clients will now use the Operator automatically when running against the cluster.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://metalbear.com/mirrord/docs/getting-started/installing-mirrord/operator.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
