> ## 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.

# FusionAuth

> Deploy FusionAuth on Control Plane using the Template Catalog. Covers configuration, volumes, scaling, and identity management with PostgreSQL and optional backups.

## Overview

FusionAuth is a modern, self-hosted identity and access management platform that provides user authentication, authorization, and secure single sign-on. It supports OAuth2, OpenID Connect, and SAML. This template deploys FusionAuth with a PostgreSQL database that is automatically configured and connected — no manual database setup required.

### What Gets Created

* **FusionAuth Workload** — The main identity platform with configurable CPU and memory.
* **PostgreSQL Workload** — The backing database, automatically connected to FusionAuth on startup.
* **Volume Set** — Persistent storage for PostgreSQL data, with optional autoscaling.
* **Backup Cron Job** (optional) — A scheduled job that dumps the PostgreSQL database to an S3 or GCS bucket when `postgres.backup.enabled` is `true`.
* **Secrets** — An opaque PostgreSQL credentials secret, and a startup script secret that waits for database readiness before launching FusionAuth.
* **Identity & Policy** — An identity bound to the workloads with `reveal` access to the PostgreSQL credentials and startup script secrets. When backup is enabled, the identity also provides the backup cron job with access to the configured object storage bucket.

<Note>
  This template does not create a GVC. You must deploy it into an existing GVC.
</Note>

## Prerequisites

Prerequisites are only required if you plan to enable PostgreSQL backups (`postgres.backup.enabled: true`). Skip this section if backups are not needed.

### AWS S3

1. Create an S3 bucket. Set `postgres.backup.aws.bucket` and `postgres.backup.aws.region` in your values file.

2. If you do not have a Control Plane Cloud Account set up, follow the [Create a Cloud Account](https://docs.controlplane.com/guides/create-cloud-account) guide. Set `postgres.backup.aws.cloudAccountName` to the name of your Cloud Account.

3. Create an IAM policy with the following JSON, replacing `YOUR_BUCKET_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/*"
            ]
        }
    ]
}
```

4. Set `postgres.backup.aws.policyName` to the name of the policy created in step 3.

### GCS

1. Create a GCS bucket. Set `postgres.backup.gcp.bucket` in your values file.

2. If you do not have a Control Plane Cloud Account set up, follow the [Create a Cloud Account](https://docs.controlplane.com/guides/create-cloud-account) guide. Set `postgres.backup.gcp.cloudAccountName` to the name of your Cloud Account.

3. Add the **Storage Admin** role to the GCP service account associated with the Cloud Account.

## 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}
resources:
  cpu: 512m
  memory: 1024Mi

firewall:
  external:
    inboundAllowCIDR:
      - 0.0.0.0/0
    outboundAllowCIDR: [] # Set to 0.0.0.0/0 to allow outbound traffic if communicating with an IdP
  internal:
    type: same-gvc # options: same-gvc, same-org, workload-list
    workloads: # Note: can only be used if type is same-gvc or workload-list
      #- //gvc/GVC_NAME/workload/WORKLOAD_NAME

# Database configuration
postgres:
  image: postgres:18
  config:
    username: username
    password: password
    database: test
  resources:
    minCpu: 200m
    minMemory: 128Mi
    maxCpu: 500m
    maxMemory: 256Mi
  volumeset:
    capacity: 10 # initial capacity in GiB (minimum is 10)
    autoscaling:
      enabled: false
      maxCapacity: 100 # Maximum capacity in GiB
      minFreePercentage: 10 # Trigger scaling when free space drops below this percentage
      scalingFactor: 1.2 # Multiply current capacity by this factor when scaling up

  internalAccess:
    type: same-gvc # options: none, same-gvc, same-org, workload-list
    workloads: # Note: can only be used if type is same-gvc or workload-list
      #- //gvc/GVC_NAME/workload/WORKLOAD_NAME

  # Postgres backup configuration (compatible with Postgres 17+)
  backup:
    enabled: false
    image: controlplanecorporation/pg-backup:18.1.0 # tag 18.1.0 = Postgres 18, 17.1.0 = Postgres 17
    schedule: "0 2 * * *" # daily at 2am UTC

    resources:
      cpu: 100m
      memory: 128Mi

    provider: aws # Options: aws or gcp

    aws:
      bucket: my-backup-bucket
      region: us-east-1
      cloudAccountName: my-backup-cloudaccount
      policyName: my-backup-policy
      prefix: postgres/backups # Folder path within the bucket

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

### FusionAuth Resources

* `resources.cpu` / `resources.memory` — CPU and memory allocated to the FusionAuth workload.

### Firewall

* `firewall.external.inboundAllowCIDR` — CIDR ranges allowed to reach FusionAuth from the internet (default: `0.0.0.0/0`).
* `firewall.external.outboundAllowCIDR` — CIDR ranges FusionAuth is allowed to reach externally. Empty by default. Set to `0.0.0.0/0` (or a specific CIDR) if FusionAuth needs to communicate with an external Identity Provider such as Google OAuth.
* `firewall.internal.type` — Controls which internal workloads can reach FusionAuth (`same-gvc`, `same-org`, or `workload-list`).

### PostgreSQL

* `postgres.config.username` / `postgres.config.password` — Database credentials. **Change before deploying to production.**
* `postgres.config.database` — Name of the database created on startup.

<Note>
  These values are only applied on first startup when the data directory is empty. Updating them after the initial deployment will have no effect on the running database. To change credentials or the database name on an existing instance, use PostgreSQL's native commands (e.g. `ALTER USER`, `ALTER DATABASE`).
</Note>

* `postgres.resources` — Min/max CPU and memory bounds for the PostgreSQL workload.
* `postgres.internalAccess.type` — Controls which workloads can reach PostgreSQL. Set to `none` to isolate replicas from each other, or use `same-gvc`, `same-org`, or `workload-list`.

### PostgreSQL Storage

* `postgres.volumeset.capacity` — Initial volume size in GiB (minimum 10).
* `postgres.volumeset.autoscaling.enabled` — Automatically expand the volume as it fills. When enabled:
  * `maxCapacity` — Maximum volume size in GiB.
  * `minFreePercentage` — Trigger a scale-up when free space drops below this percentage.
  * `scalingFactor` — Multiply the current capacity by this factor when scaling up.

### PostgreSQL Backup

Set `postgres.backup.enabled: true` to enable scheduled database dumps to object storage. The backup image tag corresponds to the PostgreSQL version (`18.1.0` for Postgres 18, `17.1.0` for Postgres 17).

Set `postgres.backup.provider` to `aws` or `gcp` and fill in the corresponding section. The `prefix` field controls the folder path within the bucket where backups are stored.

### Restoring a Backup

To restore from a backup, run the following from a client with access to the bucket:

**AWS S3**

```bash theme={null}
export PGPASSWORD="PASSWORD"

aws s3 cp "s3://BUCKET_NAME/PREFIX/BACKUP_FILE.sql.gz" - \
  | gunzip \
  | psql \
      --host=WORKLOAD_NAME \
      --port=5432 \
      --username=USERNAME \
      --dbname=postgres

unset PGPASSWORD
```

**GCS**

```bash theme={null}
export PGPASSWORD="PASSWORD"

gsutil cp "gs://BUCKET_NAME/PREFIX/BACKUP_FILE.sql.gz" - \
  | gunzip \
  | psql \
      --host=WORKLOAD_NAME \
      --port=5432 \
      --username=USERNAME \
      --dbname=postgres

unset PGPASSWORD
```

### Post-Deployment Setup

After deployment, open the FusionAuth admin panel to complete setup:

1. Use the setup wizard to create your application.
2. Configure your application with the `origin`, `redirect`, and `logout` URLs for your code.
3. Set the tenant issuer to match your deployment URL (e.g., `my-fusionauth-app.io`).
4. If using an external Identity Provider, configure it in the admin panel and ensure `firewall.external.outboundAllowCIDR` allows traffic to the IdP.

## External References

<CardGroup cols={2}>
  <Card title="FusionAuth Documentation" icon="book" href="https://fusionauth.io/docs/">
    Official FusionAuth documentation
  </Card>

  <Card title="OAuth2 & OpenID Connect" icon="key" href="https://fusionauth.io/docs/lifecycle/authenticate-users/oauth/">
    FusionAuth OAuth2 and OpenID Connect guide
  </Card>

  <Card title="Cloud Accounts" icon="cloud" href="https://docs.controlplane.com/guides/create-cloud-account">
    Create a Control Plane Cloud Account for backup storage access
  </Card>

  <Card title="Backup Image Source" icon="github" href="https://github.com/controlplane-com/backup-images/tree/main/postgres-backup">
    Source code for the PostgreSQL backup container image
  </Card>

  <Card title="FusionAuth Template" icon="github" href="https://github.com/controlplane-com/templates/tree/main/fusionauth">
    View the source files, default values, and chart definition
  </Card>
</CardGroup>
