> ## Documentation Index
> Fetch the complete documentation index at: https://docs.controlplane.com/llms.txt
> Use this file to discover all available pages before exploring further.

# MongoDB Cluster

> Deploy a highly available MongoDB replica set cluster on Control Plane using Percona Server for MongoDB. Covers single and multi-location topologies, HAProxy write routing, logical and physical backups, and scaling.

## Overview

MongoDB Cluster deploys a highly available MongoDB replica set using Percona Server for MongoDB 8.0. The cluster provides automatic leader election, self-healing replica membership, and seamless failover across one or more locations. An optional HAProxy sidecar provides a stable write endpoint that always routes to the current primary.

### Architecture

* **MongoDB Replica Set** — Multi-replica cluster with keyfile authentication and automatic replica set initialization. All replicas participate in elections; only the primary accepts writes.
* **HAProxy** *(optional, enabled by default)* — Leader-routing proxy that performs active health checks across all replicas and routes write traffic to the current primary. Provides a stable connection endpoint that survives failover.
* **Backup** *(optional)* — Logical backup via `mongodump` or physical backup via Percona Backup for MongoDB (PBM).

### What Gets Created

* **Stateful MongoDB Workload** — A multi-replica MongoDB container per configured location with persistent storage per replica.
* **Standard HAProxy Proxy Workload** *(optional, enabled by default)* — Routes write traffic to the current primary replica.
* **Volume Set** — One persistent volume per MongoDB replica for data storage.
* **Identity & Policy** — An identity bound to the workload with `reveal` access to the credential secrets, and cloud storage access when backup is enabled.
* **Secrets** — A dictionary secret holding admin credentials injected at startup.
* **Cron Backup Workload** *(optional, logical mode)* — A scheduled `mongodump` job that writes compressed archives to AWS S3 or GCS.
* **PBM Agent Sidecar + Cron Trigger** *(optional, physical mode)* — A continuously-running `pbm-agent` sidecar on each MongoDB replica, plus a lightweight cron workload that triggers the backup on schedule.
* **GVC** — A GVC spanning all configured locations.

<Note>
  This template creates its own GVC. Configure `gvc.locations` in `values.yaml` before installing.
</Note>

## Prerequisites

* At least one Control Plane [location](/reference/location) to deploy into.
* A unique `replicaSetKey` generated before deploying: `openssl rand -base64 32`
* For backup: an AWS or GCP [cloud account](/guides/create-cloud-account) and a storage bucket.

### Installation

To install, follow the instructions for your preferred method:

<CardGroup cols={2}>
  <Card title="UI" href="/template-catalog/install-manage/ui" icon="laptop">
    Browse, install, and manage templates visually
  </Card>

  <Card title="CLI" href="/template-catalog/install-manage/cli" icon="terminal">
    Manage templates from your terminal
  </Card>

  <Card title="Terraform" href="/template-catalog/install-manage/terraform" icon={<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128"><g fill-rule="evenodd"><path d="M77.941 44.5v36.836L46.324 62.918V26.082zm0 0" fill="#5c4ee5"/><path d="M81.41 81.336l31.633-18.418V26.082L81.41 44.5zm0 0" fill="#4040b2"/><path d="M11.242 42.36L42.86 60.776V23.941L11.242 5.523zm0 0M77.941 85.375L46.324 66.957v36.82l31.617 18.418zm0 0" fill="#5c4ee5"/></g></svg>}>
    Declare templates in your Terraform configurations
  </Card>

  <Card
    title="Pulumi"
    href="/template-catalog/install-manage/pulumi"
    icon={<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" id="Pulumi-Icon--Streamline-Svg-Logos" height="24" width="24">
    <desc>
        Pulumi Icon Streamline Icon: https://streamlinehq.com
    </desc>
    <path fill="#f26e7e" d="M4.683025 13.3318c0.869125 -0.5018 0.870575 -2.1264 0.003225 -3.62865s-2.27504 -2.313275 -3.1441725 -1.811475C0.672945 8.3935 0.6715 10.0181 1.53885 11.52035c0.86735 1.502275 2.27505 2.313275 3.144175 1.81145Zm0.0052 3.2167c0.86735 1.502275 0.865925 3.126875 -0.003225 3.628675 -0.86915 0.5018 -2.2768275 -0.309225 -3.144175 -1.81145 -0.8673525 -1.50225 -0.8659075 -3.126875 0.003225 -3.628675 0.8691325 -0.5018 2.276825 0.309225 3.144175 1.81145Zm5.922875 3.4243c0.86735 1.50225 0.8659 3.126775 -0.003225 3.62875 -0.869125 0.501775 -2.27685 -0.309325 -3.1442 -1.81155 -0.867325 -1.50225 -0.865875 -3.12685 0.00325 -3.628675 0.869125 -0.5018 2.276825 0.309225 3.144175 1.811475Zm-0.001925 -6.845275c0.86735 1.50225 0.8659 3.12685 -0.003225 3.628675 -0.869125 0.5018 -2.276825 -0.309225 -3.144175 -1.811475 -0.86735 -1.50225 -0.8659 -3.12685 0.003225 -3.62865 0.869125 -0.501825 2.276825 0.3092 3.144175 1.81145Z" stroke-width="0.25"></path>
    <path fill="#8a3391" d="M22.45775 11.524125c0.86725 -1.502225 0.865925 -3.12685 -0.003225 -3.62865 -0.869125 -0.501825 -2.276825 0.3092 -3.144175 1.811475 -0.86735 1.50225 -0.8659 3.126825 0.003225 3.62865 0.869125 0.501825 2.276825 -0.3092 3.144175 -1.811475Zm0.000175 3.2151c0.869075 0.5018 0.870625 2.1264 0.003225 3.62865 -0.86735 1.50225 -2.27505 2.313275 -3.144175 1.81145 -0.869125 -0.5018 -0.870575 -2.126425 -0.003225 -3.62865 0.86735 -1.50225 2.27505 -2.313275 3.144175 -1.81145ZM16.536225 18.157875c0.86915 0.501825 0.8706 2.126425 0.00325 3.628675 -0.86735 1.502125 -2.275075 2.313225 -3.1442 1.81145 -0.869125 -0.50175 -0.870575 -2.126425 -0.003225 -3.62865 0.867375 -1.502275 2.27505 -2.3133 3.144175 -1.811475Zm-0.003325 -6.843775c0.869125 0.5018 0.870575 2.126425 0.003225 3.628675s-2.27505 2.313275 -3.1442 1.811475c-0.869125 -0.501825 -0.870575 -2.126425 -0.003225 -3.628675 0.86735 -1.502275 2.27505 -2.313275 3.1442 -1.811475Z" stroke-width="0.25"></path>
    <path fill="#f7bf2a" d="M15.138225 2.06721c0 1.003615 -1.40625 1.817215 -3.14095 1.817215 -1.7347 0 -3.14095 -0.8136 -3.14095 -1.817215C8.856325 1.06359 10.262575 0.25 11.997275 0.25c1.7347 0 3.14095 0.81359 3.14095 1.81721ZM9.2166 5.482375c0 1.003625 -1.40625 1.8172 -3.14095 1.8172 -1.7347 0 -3.14095 -0.813575 -3.14095 -1.8172s1.40625 -1.817225 3.14095 -1.817225c1.7347 0 3.14095 0.8136 3.14095 1.817225Zm8.71005 1.8172c1.7347 0 3.14095 -0.813575 3.14095 -1.8172s-1.40625 -1.817225 -3.14095 -1.817225c-1.7347 0 -3.14095 0.8136 -3.14095 1.817225s1.40625 1.8172 3.14095 1.8172Zm-2.788425 1.605625c0 1.003625 -1.40625 1.8172 -3.14095 1.8172 -1.7347 0 -3.14095 -0.813575 -3.14095 -1.8172 0 -1.0036 1.40625 -1.8172 3.14095 -1.8172 1.7347 0 3.14095 0.8136 3.14095 1.8172Z" stroke-width="0.25"></path>
    </svg>}
  >
    Declare templates in your Pulumi programs
  </Card>
</CardGroup>

## Configuration

The default `values.yaml` for this template:

```yaml theme={null}
gvc:
  name: mongodb-gvc
  locations:
    - name: aws-us-east-1
      replicas: 3
    - name: aws-us-west-2
      replicas: 3
    - name: aws-eu-central-1
      replicas: 3

image: percona/percona-server-mongodb:8.0

multiZone: false

resources:
  cpu: 1
  memory: 2Gi

mongodb:
  username: admin
  password: mypassword
  database: mydatabase
  # REQUIRED: Generate with `openssl rand -base64 32`
  replicaSetKey: "Ol0GnqpqntkcnjprS+Pu/1Ji8fcSEKb8f4zkF5c+dEQ="

volumeset:
  capacity: 10 # initial capacity in GiB (minimum is 10)
  autoscaling:
    enabled: false
    maxCapacity: 100
    minFreePercentage: 10
    scalingFactor: 1.2

firewall:
  internalAllowType: same-gvc # options: same-gvc, same-org, workload-list
  workloads: []
  # - //gvc/GVC_NAME/workload/WORKLOAD_NAME

proxy:
  enabled: true
  image: haproxy:2.9
  resources:
    cpu: 100m
    memory: 128Mi
  minReplicas: 2
  maxReplicas: 2

backup:
  enabled: false
  mode: logical # options: logical, physical
  schedule: "0 2 * * *" # daily at 2am UTC
  provider: aws # options: aws or gcp

  logical:
    image: ghcr.io/controlplane-com/backup-images/mongo-backup:8.0
    resources:
      cpu: 100m
      memory: 128Mi

  physical:
    image: percona/percona-backup-mongodb:2.14.0
    resources:      # pbm-agent sidecar (runs continuously)
      cpu: 100m
      memory: 128Mi
    cron:
      resources:    # backup trigger job (runs briefly on schedule)
        cpu: 50m
        memory: 64Mi

  aws:
    bucket: my-backup-bucket
    region: us-east-1
    cloudAccountName: my-backup-cloudaccount
    policyName: my-backup-policy
    prefix: mongodb-cluster/backups

  gcp:
    bucket: my-backup-bucket
    cloudAccountName: my-backup-cloudaccount
    prefix: mongodb-cluster/backups
```

### Locations

Configure which Control Plane locations to deploy into and how many replicas to run per location. Each entry in `gvc.locations` creates a set of MongoDB replicas at that location.

**Single Location** — all replicas in one location. A minimum of 3 replicas is required for majority quorum:

```yaml theme={null}
gvc:
  locations:
    - name: aws-us-east-1
      replicas: 3
```

**Multi-Location** — distribute replicas across locations for resilience to a full location outage. For production, use 3 locations with 3 replicas each (9 total). Losing an entire location leaves 6 of 9 replicas online — a clear majority — and the cluster continues serving traffic:

```yaml theme={null}
gvc:
  locations:
    - name: aws-us-east-1
      replicas: 3
    - name: aws-us-west-2
      replicas: 3
    - name: aws-eu-central-1
      replicas: 3
```

<Note>
  Aim for an odd total replica count (3, 5, 7, 9) across all locations to guarantee a clear majority in all split-brain scenarios.
</Note>

### Multi-Zone

Set `multiZone: true` to spread replicas across availability zones within each location, protecting against zone-level failures. Verify your selected location(s) support multi-zone before enabling.

### MongoDB Settings

* `mongodb.username` / `mongodb.password` — Admin credentials. Change these before deploying to production.
* `mongodb.database` — Default database name created at startup.
* `mongodb.replicaSetKey` — Shared secret used to authenticate replica set members to each other. **Must be generated before deploying** with `openssl rand -base64 32`. This value must not be changed after the cluster is initialized — doing so requires a full cluster restart.
* `resources.cpu` / `resources.memory` — Resource limits applied to each MongoDB replica.
* `image` — MongoDB image. Defaults to `percona/percona-server-mongodb:8.0`.

### HAProxy Proxy

HAProxy is enabled by default and strongly recommended. In a MongoDB replica set, only the primary accepts writes — HAProxy provides a stable endpoint that routes all write traffic to the current primary automatically, surviving failover without any client-side changes.

* `proxy.enabled` — Enable or disable the proxy workload.
* `proxy.minReplicas` / `proxy.maxReplicas` — Number of HAProxy instances. Defaults to 2 for availability.
* `proxy.resources` — CPU and memory limits for the proxy workload.

<Note>
  HAProxy is required when using logical backups (`backup.mode: logical`). Physical (PBM) backups connect directly to replicas and do not require the proxy.
</Note>

### Storage

* `volumeset.capacity` — Initial volume size in GiB. Minimum is 10.
* `volumeset.autoscaling.enabled` — Set to `true` to automatically expand volumes as data grows.
* `volumeset.autoscaling.maxCapacity` — Maximum volume size in GiB.
* `volumeset.autoscaling.minFreePercentage` — Percentage of free space that triggers a scale-up.
* `volumeset.autoscaling.scalingFactor` — Multiplier applied to the current capacity when scaling up.

### Firewall

* `firewall.internalAllowType` — Controls which workloads can reach MongoDB:
  * `same-gvc` — All workloads in the same GVC (default).
  * `same-org` — All workloads in the org.
  * `workload-list` — Only workloads listed in `firewall.workloads`.
* `firewall.workloads` — List of specific workload paths when using `workload-list`.

## Connecting to MongoDB

Connect using the appropriate endpoint depending on your setup:

| Setup                                      | Hostname                                                       | Port    |
| ------------------------------------------ | -------------------------------------------------------------- | ------- |
| Via HAProxy (writes + reads)               | `{release-name}-mongo-proxy.{gvc}.cpln.local`                  | `27017` |
| Direct per-replica (read-only or internal) | `replica-{N}.{release-name}-mongo.{location}.{gvc}.cpln.local` | `27017` |

Example connection string via proxy:

```
mongodb://admin:mypassword@{release-name}-mongo-proxy.{gvc}.cpln.local:27017/mydatabase?authSource=admin
```

To offload reads from the primary, add `readPreference=secondaryPreferred` to your connection string and connect directly to a replica:

```
mongodb://admin:mypassword@replica-0.{release-name}-mongo.{location}.{gvc}.cpln.local:27017/mydatabase?authSource=admin&readPreference=secondaryPreferred
```

<Note>
  Secondary reads may be slightly stale due to replication lag. Use `readPreference=primary` (the default) when read-your-own-writes consistency is required.
</Note>

Configure `maxPoolSize` in your MongoDB driver to prevent connection exhaustion. A per-app-replica pool of 10–50 connections is a reasonable starting point for most workloads.

## Backing Up

Two backup modes are available:

| Mode       | Mechanism                        | Best For                                                                   |
| ---------- | -------------------------------- | -------------------------------------------------------------------------- |
| `logical`  | `mongodump` (BSON archives)      | Smaller databases, cross-version migrations, selective collection restores |
| `physical` | Percona Backup for MongoDB (PBM) | Large databases — all replicas participate and upload concurrently         |

Set `backup.enabled: true`, choose a `mode`, set `backup.provider`, and fill in the corresponding provider block.

The backup job region determines where the backup cron workload runs. For AWS, the Control Plane location is automatically derived from `backup.aws.region` (e.g., `us-east-1` → `aws-us-east-1`). For GCP, the job runs in the first location listed in `gvc.locations`.

### AWS S3

Complete the following in your AWS account before installing:

<Steps>
  <Step title="Create a bucket">
    Create an S3 bucket and set `backup.aws.bucket` and `backup.aws.region` to match.
  </Step>

  <Step title="Set up a Cloud Account">
    If you do not have one, [create a Cloud Account](https://docs.controlplane.com/guides/create-cloud-account). Set `backup.aws.cloudAccountName` to its name.
  </Step>

  <Step title="Create an IAM policy">
    Create an IAM policy with the following JSON (replace `YOUR_BUCKET_NAME`) and set `backup.aws.policyName` to its name:

    ```json theme={null}
    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Allow",
                "Action": [
                    "s3:GetObject",
                    "s3:PutObject",
                    "s3:DeleteObject",
                    "s3:ListBucket",
                    "s3:GetObjectVersion",
                    "s3:DeleteObjectVersion"
                ],
                "Resource": [
                    "arn:aws:s3:::YOUR_BUCKET_NAME",
                    "arn:aws:s3:::YOUR_BUCKET_NAME/*"
                ]
            }
        ]
    }
    ```
  </Step>
</Steps>

### GCS

Complete the following in your GCP account before installing:

<Steps>
  <Step title="Create a bucket">
    Create a GCS bucket and set `backup.gcp.bucket` to its name.
  </Step>

  <Step title="Set up a Cloud Account">
    If you do not have one, [create a Cloud Account](https://docs.controlplane.com/guides/create-cloud-account). Set `backup.gcp.cloudAccountName` to its name.
  </Step>
</Steps>

<Warning>
  You must add the `Storage Admin` role to the GCP service account created for the Cloud Account.
</Warning>

## Restoring a Backup

### Logical Restore

Run the following from a client with network access to the cluster and access to the bucket. Connect to the **proxy** workload so writes land on the current primary.

**AWS S3:**

```sh theme={null}
mongorestore \
  --uri="mongodb://USERNAME:PASSWORD@{release-name}-mongo-proxy.{gvc}.cpln.local:27017/?authSource=admin" \
  --gzip \
  --archive=<(aws s3 cp s3://BUCKET_NAME/PREFIX/BACKUP_FILE.gz -)
```

**GCS:**

```sh theme={null}
mongorestore \
  --uri="mongodb://USERNAME:PASSWORD@{release-name}-mongo-proxy.{gvc}.cpln.local:27017/?authSource=admin" \
  --gzip \
  --archive=<(gsutil cp gs://BUCKET_NAME/PREFIX/BACKUP_FILE.gz -)
```

### Physical Restore (PBM)

Physical restores require stopping the MongoDB workload and restoring data files directly. All replicas must participate.

<Steps>
  <Step title="Exec into a pbm-agent container">
    ```sh theme={null}
    cpln workload exec {release-name}-mongo --gvc {gvc} --location {location} --replica 0 --container pbm-agent -- /bin/sh
    ```
  </Step>

  <Step title="List available backups">
    ```sh theme={null}
    MONGO_URI="mongodb://${MONGO_INITDB_ROOT_USERNAME}:${MONGO_INITDB_ROOT_PASSWORD}@localhost:27017/admin?replicaSet=rs0&authSource=admin&authMechanism=SCRAM-SHA-256"
    pbm list --mongodb-uri="${MONGO_URI}"
    ```
  </Step>

  <Step title="Stop the MongoDB workload">
    Stop the workload via the Control Plane console or CLI before restoring.
  </Step>

  <Step title="Run the restore">
    ```sh theme={null}
    pbm restore BACKUP_NAME --mongodb-uri="${MONGO_URI}"
    ```
  </Step>

  <Step title="Restart the workload">
    Restart the MongoDB workload. All replicas will resync automatically.
  </Step>
</Steps>

## Scaling

### Scaling Up

Increase `replicas` for a location in `values.yaml` and apply the template upgrade. New replicas connect to the primary via seed nodes and self-register into the replica set automatically.

### Scaling Down

<Warning>
  Scaling down requires manually removing departing replicas from the replica set configuration first. If stale members are not removed, the replica set config will reference non-existent hosts, which can affect elections and quorum calculations.
</Warning>

Before reducing the replica count, connect to the primary and remove each departing replica:

```js theme={null}
rs.remove("replica-{N}.{release-name}-mongo.{location}.{gvc}.cpln.local:27017")
```

After all departing replicas are removed from the config, apply the template upgrade to reduce the count.

## Important Notes

* **Minimum replicas**: Use at least 3 replicas per location for HA. A 2-replica cluster cannot maintain quorum if one replica fails.
* **Replica set key**: The `replicaSetKey` must be generated before deployment and must not be changed after the cluster is initialized. Changing it requires a full cluster restart.
* **Odd total replica count**: Aim for an odd total number of replicas across all locations (3, 5, 7, 9) to guarantee a clear majority in all failover scenarios.
* **Read from secondaries**: To offload reads from the primary, use `readPreference=secondaryPreferred` in your connection string. Secondary reads may be slightly stale due to replication lag.
* **Connection pooling**: Configure `maxPoolSize` in your MongoDB driver to prevent connection exhaustion. A per-app-replica pool of 10–50 is a reasonable starting point.

## External References

<CardGroup cols={2}>
  <Card title="Percona Server for MongoDB" href="https://docs.percona.com/percona-server-for-mongodb/" icon="database">
    Official documentation for Percona Server for MongoDB
  </Card>

  <Card title="Percona Backup for MongoDB" href="https://docs.percona.com/percona-backup-mongodb/" icon="box-archive">
    Documentation for PBM physical backup and restore
  </Card>

  <Card title="MongoDB Replica Set" href="https://www.mongodb.com/docs/manual/replication/" icon="circle-nodes">
    MongoDB replica set concepts and administration
  </Card>

  <Card title="MongoDB Connection String" href="https://www.mongodb.com/docs/manual/reference/connection-string/" icon="link">
    Connection string URI format and options
  </Card>
</CardGroup>
