Deploy with k8s

On this page

Kubernetes is an extensible and open-source container-orchestration system used for automating application deployments. Some of the most notable features it provides include self-healing, auto-scaling, and automated rollouts and rollbacks.

Since the Kubernetes container runtime supports Docker, FusionAuth Docker containers can be deployed to any Kubernetes cluster.

The following guide demonstrates how to deploy FusionAuth containers using kubectl, the Kubernetes command-line tool that enables you to send commands to a cluster through the API server. You'll need a cluster running to work through this guide.

Requirements#

Adding the Helm Chart Repository#

To get started, the first thing to do is add the FusionAuth Helm Chart repository. This can be done with the following.

Add a chart repository
helm repo add fusionauth https://fusionauth.github.io/charts

To view the list of repositories that you have added use the following command:

List chart repositories
helm repo list
Output from listing chart repositories
NAME      	URL
fusionauth	https://fusionauth.github.io/charts

To search repositories:

Search chart repositories
helm search repo fusionauth
Output from search
NAME                 	CHART VERSION	APP VERSION	DESCRIPTION
fusionauth/fusionauth	1.69.4       	1.69.4     	Helm chart for FusionAuth

Helm Chart Configuration#

Before you install, create a values.yaml file that holds the chart values you want to change. Helm merges this file with the chart's defaults, so it only needs the values that differ. This guide covers the values you must set to deploy FusionAuth.

To review every option and its default, save the chart's default values to a separate file:

Save the chart default values
helm show values fusionauth/fusionauth > chart-values.yaml

Replicas#

The replicaCount value indicates the number of instances of FusionAuth to run in a ReplicaSet. Kubernetes will monitor the cluster to ensure that this number of instances of FusionAuth is running at all times.

By default, the replicaCount value is set to 1 which typically works well for development and testing environments, but you will likely want to adjust this based on your system and application requirements.

Replica count example configuration
# replicaCount -- The number of fusionauth-app instances to run
replicaCount: 1

FusionAuth Version#

Each chart release sets image.tag to the FusionAuth version it was released with, so helm install installs that version. To run a different version, set image.tag in values.yaml to one of the FusionAuth releases. Use a specific version instead of latest, so every pod runs the same release.

Extra Containers#

You may want to inject a sidecar or ambassador container. This is useful if you want to, for instance, proxy requests to FusionAuth to put it at a different path or limit the IP addresses which have access to the administrative user interface. You can do this with the extraContainers value. Containers defined here will be added to the FusionAuth pod.

extraContainers with an nginx sidecar
extraContainers:
  - name: my-sidecar
    image: nginx:stable

You can find community contributed proxy configurations here.

Database Configuration#

Now, configure the database connection. Each of our FusionAuth pods will use this configuration when communicating with the database.

Note

FusionAuth requires an external database and, optionally an external Elasticsearch cluster. Setting these up is outside of the scope of this document.

Consult the setup guides for instructions for AWS, GCP, and more.

Store the database passwords in Kubernetes Secrets rather than in values.yaml, so they stay out of version control and your Helm release history. Create one Secret for the password FusionAuth uses in normal operation, and one for the database superuser password FusionAuth uses to create its database:

Create Secrets for the database passwords
kubectl create secret generic fusionauth-db-credentials --from-literal=password='<fusionauth-user-password>'
kubectl create secret generic fusionauth-db-root-credentials --from-literal=password='<superuser-password>'

Then add a database section to values.yaml with your database host and user names, and reference the Secrets:

Database configuration
database:
  host: "<database-host>"
  port: 5432
  protocol: postgresql
  tls: true
  tlsMode: require
  name: fusionauth

  dbUser:
    username: fusionauth
    existingSecret:
      enabled: true
      name: fusionauth-db-credentials
      passwordKey: password

  rootUser:
    username: "<superuser-name>"
    existingSecret:
      enabled: true
      name: fusionauth-db-root-credentials
      passwordKey: password

During initial setup, FusionAuth uses the rootUser credentials to create the fusionauth database and the dbUser user. If you'd rather set up the database yourself, follow the advanced database installation guide, then remove the rootUser section and its Secret.

The tlsMode: require setting encrypts the database connection, but doesn't verify the database server's certificate. To verify it, mount your database provider's CA certificate into the pod with extraVolumes and extraVolumeMounts, and set database.url to a full JDBC URL that uses sslmode=verify-full. See database.url in the configuration reference for an example. When database.url is set, the chart ignores tls and tlsMode.

Search Configuration#

If search.engine is set to elasticsearch, add a search section to values.yaml with your search host. Use https if your search service supports it. If your search service doesn't require a username and password, leave out basicAuth:

Search configuration
search:
  engine: elasticsearch
  protocol: https
  host: "<search-host>"
  port: 9200
  basicAuth:
    existingSecret:
      enabled: true
      name: fusionauth-search-credentials
      userKey: username
      passwordKey: password

Store the search credentials in a Secret, like the database passwords:

Create a Secret for the search credentials
kubectl create secret generic fusionauth-search-credentials --from-literal=username='<search-username>' --from-literal=password='<search-password>'

Runtime and Silent Mode#

Set the runtime mode to production and turn on silent mode. In silent mode, FusionAuth creates its database on first start and applies database migrations when you update it, without waiting for someone to complete Maintenance Mode in a browser:

Runtime and silent mode configuration
app:
  runtimeMode: production
  silentMode: true

Deploy FusionAuth To the Cluster#

Now that values.yaml is ready, it is time to install the chart on the cluster. The helm install command is used to install a chart by name and can be applied using the syntax:

Helm install format
helm install [CHART NAME] [CHART] [flags]

Here you will install a chart including the -f flag to override the default values.

Install the FusionAuth chart
helm install my-release fusionauth/fusionauth -f values.yaml
Note

You can override values in the values.yaml file that you pass when installing a chart, or with the --set flag on the command line. Using --set can be useful when developing scripts or command line tools. Either method is acceptable.

If the previous command was successful, you should see output similar to the following:

Example output
NAME: my-release
LAST DEPLOYED: Tue Nov  3 13:26:58 2026
NAMESPACE: default
STATUS: deployed
REVISION: 1
DESCRIPTION: Install complete
TEST SUITE: None
NOTES:
1. Get the application URL by running these commands:
  export SVC_NAME=$(kubectl get svc --namespace default -l "app.kubernetes.io/name=fusionauth,app.kubernetes.io/instance=my-release" -o jsonpath="{.items[0].metadata.name}")
  echo "Visit http://127.0.0.1:9011 to use your application"
  kubectl port-forward svc/$SVC_NAME 9011:9011

You can get the status of your deployment with kubectl.

Get a list of deployments running on the cluster
kubectl get deployments -o wide
Output
NAME                    READY   UP-TO-DATE   AVAILABLE   AGE   CONTAINERS   IMAGES                                       SELECTOR
my-release-fusionauth   1/1     1            1           21s   fusionauth   docker.io/fusionauth/fusionauth-app:1.69.4   app.kubernetes.io/instance=my-release,app.kubernetes.io/name=fusionauth

As instructed by the success message output above, you can create a proxy enabling you to connect to the cluster from localhost:

Set up a port-forwarding proxy
export SVC_NAME=$(kubectl get svc --namespace default -l "app.kubernetes.io/name=fusionauth,app.kubernetes.io/instance=my-release" -o jsonpath="{.items[0].metadata.name}")
kubectl port-forward svc/$SVC_NAME 9011:9011
Example output of kubectl proxying command
Forwarding from 127.0.0.1:9011 -> 9011

Navigate to http://localhost:9011 and you will land on the FusionAuth Setup Wizard. For a complete tutorial walking you through the initial FusionAuth configuration, see Setup Wizard & First Login.

FusionAuth bootstrap

FusionAuth bootstrap

Expose FusionAuth Through a Gateway#

To let users reach FusionAuth from outside the cluster, route traffic to it with the Gateway API, the Kubernetes API for bringing external traffic into a cluster. The FusionAuth chart can create the route for you.

A Gateway needs a controller that implements it. This guide uses Envoy Gateway. Most cloud providers also offer an implementation, such as the GKE Gateway controller. To use another implementation, install it instead of Envoy Gateway and use its GatewayClass in gateway.yaml.

Install Envoy Gateway#

Install Envoy Gateway, which also installs the Gateway API resource definitions:

Install Envoy Gateway
helm install eg oci://docker.io/envoyproxy/gateway-helm --version v1.9.3 -n envoy-gateway-system --create-namespace

Then wait until it is ready:

Wait for Envoy Gateway
kubectl wait --timeout=5m -n envoy-gateway-system deployment/envoy-gateway --for=condition=Available

Create the Gateway#

The Gateway terminates TLS, so it needs a certificate for your FusionAuth hostname. Store the certificate and private key in a TLS Secret, or use cert-manager to issue and renew one:

Create a TLS Secret
kubectl create secret tls fusionauth-tls --cert=tls.crt --key=tls.key

Next, create a file named gateway.yaml with the following resources:

  • A GatewayClass that assigns Gateways of the class eg to Envoy Gateway.
  • A Gateway with an HTTPS listener that uses your TLS Secret, and an HTTP listener.
  • An HTTPRoute that redirects every HTTP request to HTTPS.
Gateway resource configuration
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: eg
spec:
  controllerName: gateway.envoyproxy.io/gatewayclass-controller
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: fusionauth-gateway
spec:
  gatewayClassName: eg
  listeners:
    - name: http
      protocol: HTTP
      port: 80
      hostname: auth.example.com
    - name: https
      protocol: HTTPS
      port: 443
      hostname: auth.example.com
      tls:
        mode: Terminate
        certificateRefs:
          - kind: Secret
            name: fusionauth-tls
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: fusionauth-http-redirect
spec:
  parentRefs:
    - name: fusionauth-gateway
      sectionName: http
  hostnames:
    - auth.example.com
  rules:
    - filters:
        - type: RequestRedirect
          requestRedirect:
            scheme: https
            statusCode: 301

Replace auth.example.com with your FusionAuth hostname, then apply the file:

Apply the Gateway resources
kubectl apply -f ./gateway.yaml

Route Traffic to FusionAuth#

Add a gateway section to values.yaml, using the same hostname. The chart then creates an HTTPRoute that sends requests from the Gateway's HTTPS listener to FusionAuth:

Gateway route configuration
gateway:
  enabled: true
  parentRefs:
    - name: fusionauth-gateway
      sectionName: https
  hostnames:
    - auth.example.com

Apply the change by upgrading the release:

Upgrade the FusionAuth release
helm upgrade my-release fusionauth/fusionauth -f values.yaml

Finally, get the Gateway's external address:

Get the Gateway address
kubectl get gateway fusionauth-gateway
Example output
NAME                 CLASS   ADDRESS        PROGRAMMED   AGE
fusionauth-gateway   eg      203.0.113.10   True         2m

Point the DNS record for your FusionAuth hostname at the address in the ADDRESS column. On a cloud provider, this is the address of a load balancer that Envoy Gateway created for the Gateway.

Note

If you have completed this tutorial using minikube, you can give the Gateway an address by creating a network route to your cluster with the command minikube tunnel. For documentation on this, see Using minikube tunnel.

Congratulations! You now have a fully functional Kubernetes environment running FusionAuth.

Troubleshooting#

Runtime Mode Mismatch#

If you receive a message similar to this one in your system log:

ERROR io.fusionauth.api.service.system.NodeService - Node [c98263dc-7f12-4879-93c1-f1a634912a51] cannot be added. The runtime mode is [Development] and this node in [Production] mode.

This means that your nodes are in different runtime modes. This will not work and may cause undetermined behavior. All nodes of a FusionAuth cluster must be running in the same mode, either Production or Development.

To resolve this issue, take the following steps:

  • Stop all existing nodes, whether they are running as containers or processes.
  • Review configuration to confirm that all nodes are configured with the same runtime mode.
  • Restart the nodes.

Next Steps#

Before customers log in to this instance, see Harden a Production Instance for the FusionAuth settings to change, and the Kubernetes security documentation to secure the cluster itself.

To learn more about Kubernetes, here are a few recommended links to help you administer your cluster.