Overview
FusionAuth is a self-hosted identity and access management platform: a user store with hosted login and registration pages, OAuth2 and OpenID Connect token issuance, SAML, MFA, and federation to external identity providers, managed through a browser admin UI. This template deploys one FusionAuth node together with a bundled PostgreSQL database that it creates, wires up and waits for — the only thing to prepare before installing is the secret holding the database credentials. The FusionAuth workload is publicly reachable by default so that applications can redirect users to it, and it keeps no state of its own: every user, tenant and application lives in the bundled database.What Gets Created
Prerequisites
One dictionary secret must exist before you install — the database credentials. The bundled PostgreSQL initializes with them and FusionAuth connects with them, and the password never passes through Helm values:postgres.config.credentialsSecretName to that name. Secret names are org-wide, so give each FusionAuth release its own secret.
databasemust be URL-safe (letters, digits,_): FusionAuth’s JDBC URL is assembled from it at container start. The snippet’s hex password is URL-safe too.- PostgreSQL reads the secret only when its data directory is first initialized — to change the password later, see Rotating the Database Password.
Installation
Once the database credentials secret exists, install the chart from the marketplace registry with its name set:UI
CLI
Terraform
Pulumi
Configuration
Each block below is the shipped default for that part ofvalues.yaml.
Image and Resources
The image is a Control Plane-published build of FusionAuth 1.60.2 that includes the PostgreSQL client used by the start script. The block exposes only limits, so the names are the platform’s owncpu and memory.
Firewall
firewall.external.inboundAllowCIDR— who can reach the login pages and admin UI from the internet. Open to everyone by default; restrict it to your own address range while you complete the setup wizard if you cannot do so immediately after installing.firewall.external.outboundAllowCIDR— egress is closed by default. Any external identity provider (Google, Microsoft, a remote SAML IdP), outbound email and webhooks to internet destinations need this opened —0.0.0.0/0or the provider’s address ranges.firewall.internal.type— which workloads in your org can reach port9011directly:
Bundled PostgreSQL
The database is the PostgreSQL template deployed as a dependency. It reads the credentials secret you created under Prerequisites, named bypostgres.config.credentialsSecretName.
postgres.config.credentialsSecretName— the name of your prerequisite dictionary secret (username,password,database). Its values are applied only when the volume is first initialized; changing the secret afterwards does not change the database — see Rotating the Database Password.databasealso forms the JDBC URL FusionAuth connects with.postgres.internalAccess.type— which workloads can reach port5432. The defaultsame-gvcis what the FusionAuth workload needs;nonewould cut it off from its own database.postgres.resources— on a stateful workloadmaxCpumay not exceed four timesminCpu; raising one without the other is rejected when the workload is applied.
postgres template. A node failure reschedules the server and reattaches the same volume, so the exposure is minutes of downtime, not data loss.PostgreSQL Backup
A cron workload runspg_dumpall on the schedule and uploads a gzip-compressed SQL dump of the database to your bucket, authenticating with the username and password from your credentials secret. Provider setup steps are under Backing Up.
postgres.backup.image must match the server’s major version: tag 18.1.0 for postgres:18, 17.1.0 for postgres:17. Backups require PostgreSQL 17 or later.Connecting
/api/status answers 200 without an API key once the node is serving:
/.well-known/openid-configuration on either host, and at /TENANT_ID/.well-known/openid-configuration once you have more than one tenant.
First Run
Open the public endpoint in a browser; with no users yet, FusionAuth shows its setup wizard instead of a login page.Create the administrator
Set the tenant issuer
iss claim in every token FusionAuth signs, so set it before applications start validating tokens, and configure those applications to expect the same value.Register your application
Create an API key
Add external identity providers (optional)
firewall.external.outboundAllowCIDR first, or every social login silently does nothing.Operations
Backing Up
Backups are off by default and are provided by the bundled PostgreSQL template’s backup job. Enable them withpostgres.backup.enabled: true, pick a postgres.backup.provider, and complete that provider’s setup first. The job writes postgres-TIMESTAMP.sql.gz objects under the configured prefix in your bucket on every run of postgres.backup.schedule. The dump is a pg_dumpall of the whole server — every FusionAuth tenant, application, user and key.
AWS S3
-
Create an S3 bucket. Set
postgres.backup.aws.bucketto its name andpostgres.backup.aws.regionto its region. -
If you do not have one yet, create a Control Plane cloud account for the AWS account holding the bucket. Set
postgres.backup.aws.cloudAccountNameto its name. -
Create an IAM policy scoped to exactly that bucket, replacing
YOUR_BUCKET_NAME:
- Set
postgres.backup.aws.policyNameto that policy’s name. The template attaches it to the database identity and nothing else — the bucket in your policy is the only storage the backup job can reach.
GCS
-
Create a GCS bucket. Set
postgres.backup.gcp.bucketto its name. -
Create a Control Plane cloud account for the GCP project holding the bucket, and set
postgres.backup.gcp.cloudAccountNameto its name. -
Grant the cloud account’s service account the Storage Admin (
roles/storage.admin) role. The template additionally binds the identity toroles/storage.objectAdminon exactly the bucket inpostgres.backup.gcp.bucket.
Restoring a Backup
Each backup is a gzip-compressedpg_dumpall script that recreates the roles and databases it contains, so it is restored by connecting to the postgres maintenance database as the username from your credentials secret. Because the script creates the FusionAuth database rather than merging into it, restore into a server where that database does not exist — a fresh install, or one whose database you have dropped.
Run the following from a client that can reach both the bucket and the database. The simplest client is your own machine with a tunnel to the database workload open in another terminal, using --host=127.0.0.1:
Stop FusionAuth from holding connections and drop the database
FORCE (PostgreSQL 13 and later), substituting the database value from your credentials secret:Stream the dump into the server
- AWS S3
- GCS
already exists error for the role the server created at first boot is expected; the rest of the script loads normally.Redeploy FusionAuth
Rotating the Database Password
The database reads the password from your credentials secret only when its volume is first initialized, and FusionAuth resolves the secret only when a replica starts. So rotate in three moves: change it inside PostgreSQL, update the secret, then right away restart FusionAuth.Change the password inside PostgreSQL
cpln port-forward RELEASE_NAME-postgres 5432:5432 --gvc GVC_NAME), connect with the current password and run:Update the secret
password, and apply it:Restart FusionAuth so it picks the new value up
ALTER ROLE; new ones fail until its redeploy finishes, so redeploy right away and rotate in a quiet window.
Upgrading From 2.3.x
postgres.config.username, postgres.config.password and postgres.config.database were removed. A values file that still carries an old key fails the render. Put the values your database was initialized with into the database credentials secret under Prerequisites (keys username, password and database), set postgres.config.credentialsSecretName to its name, and remove the three keys.
Upgrading From 2.4.0
postgres.credentials was removed in 2.5.0: the database credentials are now a prerequisite secret, the same one the postgres template requires on its own. Carrying the old key forward fails the render. This applies to upgrades from 2.4.0 and 2.4.1.
2.4.x created the database secret itself, so this upgrade deletes it. Before upgrading, create a secret under a new name holding the values your database was initialized with — not new ones, or FusionAuth loses its database connection:
username and test were the 2.4.x defaults; use your own values if you overrode them.) Your current values are in postgres.credentials of the installed release, and also in the old secret (cpln secret reveal my-fusionauth-db-credentials -o yaml, or the name your release set in credentialsSecretName) until the upgrade deletes it. If you installed 2.4.0 and never overrode the password, it is literally password — put that in the new secret, and rotate it afterwards as described in Rotating the Database Password.
Then upgrade with postgres.config.credentialsSecretName set to the new name and postgres.credentials removed.
The upgrade restarts the bundled PostgreSQL too (it now reads the new secret), so FusionAuth can briefly lose its database; it reconnects by itself. The new version of a workload can also wait several minutes with is not allowed to reveal the secret in get-deployments while access to the new secret propagates; the previous version keeps serving meanwhile, and it clears by itself.
Scaling and Availability
- The FusionAuth tier is a serverless workload that autoscales on request concurrency between one and three replicas; this is fixed in the template, not a value. Running more than one FusionAuth node has not been verified with this template; FusionAuth’s own multi-node guidance expects every node to share the same database and matching configuration, so treat the single-node shape as the supported one.
- The database is a single instance — there is no high-availability option in this template. A node failure reschedules it onto the same volume: minutes of downtime, no data loss.
- The first
cpln helm upgradeafter an install can restart the bundled database, and FusionAuth is unreachable while it comes back. Later upgrades with unchanged values do not. - Every upgrade that touches the database restarts it, and FusionAuth with it. Plan upgrades as a short login outage.
Troubleshooting
FusionAuth container appears stuck right after install
FusionAuth container appears stuck right after install
Starting FusionAuth.... If the script instead exits with PostgreSQL did not become ready after 300s. Exiting., the database itself is the problem: check cpln workload get-deployments RELEASE_NAME-postgres --gvc GVC_NAME -o yaml and its logs with cpln logs '{gvc="GVC_NAME", workload="RELEASE_NAME-postgres"}' --limit 50 --since 10m.The deployment never starts and cpln logs is empty
The deployment never starts and cpln logs is empty
RELEASE_NAME-fusionauth and RELEASE_NAME-postgres never reach ready, and cpln logs returns zero lines.Cause: the database credentials secret named by postgres.config.credentialsSecretName does not exist. The containers never start, so there is nothing to log.Fix: read status.versions[].message, which names the missing secret, then create it as in Prerequisites. The deployment recovers on its own after several minutes, or force a redeployment to skip the wait.Upgrade fails telling you to create a dictionary secret
Upgrade fails telling you to create a dictionary secret
cpln helm upgrade from 2.3.x stops at render with a message from the PostgreSQL template saying that config.username (or config.password, config.database) was removed and that you should create a dictionary secret.Cause: your values still carry postgres.config.username, postgres.config.password or postgres.config.database, which were removed.Fix: create the database credentials secret with the values your database was initialized with, as shown in Upgrading From 2.3.x, and upgrade again. The running release is unaffected until the upgrade succeeds.FusionAuth cannot authenticate to the database after upgrading
FusionAuth cannot authenticate to the database after upgrading
password.Fix: put the password the database was initialized with into the secret, as in Upgrading From 2.4.0, then force a redeployment of RELEASE_NAME-fusionauth.Tokens are rejected because the issuer does not match
Tokens are rejected because the issuer does not match
iss mismatch, or an OIDC library refuses the discovery document.Cause: the tenant Issuer (Tenants → Default → General) does not match the URL the application was configured to expect — commonly because it was left at its default rather than set to the public endpoint or custom domain.Fix: set the issuer to the HTTPS URL applications use, as in First Run, and make the application’s expected issuer identical. Tokens issued before the change carry the old value.Important Notes
- Complete the setup wizard immediately after installing. Inbound is open to
0.0.0.0/0by default and there is no administrator until the wizard is finished — whoever gets there first creates that account. - Create the database credentials secret before installing. A missing secret leaves the workloads waiting with zero log lines;
status.versions[].messagefromcpln workload get-deployments RELEASE_NAME-fusionauth --gvc GVC_NAME -o yamlnames it. - Rotating the database password needs
ALTER ROLEplus a forced redeployment — PostgreSQL keeps the password it was initialized with, and a running workload keeps the values it started with; see Rotating the Database Password. - Egress is closed by default. External identity providers, outbound email and internet webhooks need
firewall.external.outboundAllowCIDRopened. - Set the tenant issuer before applications validate tokens — it is the
issclaim in every token, and changing it later invalidates the expectation in every client. cpln helm uninstalldeletes the volume set and with it every tenant, application and user. Take a backup first; the volume set keeps only a final snapshot for seven days.- Upgrading from 2.3.x or 2.4.x needs a new secret and a values change — see Upgrading From 2.3.x and Upgrading From 2.4.0.
firewall.external.outboundAllowCIDRis empty by default, so FusionAuth has no internet egress.Fix: setfirewall.external.outboundAllowCIDRto0.0.0.0/0or the provider’s address ranges and upgrade. Allow a few minutes for the change to take effect.