Table of Contents

Prerequisites

Business clusters images

In order to run your Neos cluster in the Kubernetes environment, you will need to provide for each cluster a Docker image of the backend and frontend parts. If your cluster calls background server methods, you will also need to provider a Docker image of the task-runner.

These images should be based on the same Neos version as the application version of the Helm chart.

Important

Docker image tags used in the examples below are illustrative. Some examples use explicit pinned tags for reproducibility, while others keep a convenience tag for readability. Choosing, updating, and patching base images (for example mcr.microsoft.com/dotnet/aspnet or nginx) is the responsibility of the team operating the deployment. In production, prefer explicit and traceable tags. From the framework perspective, the technical requirement is compatibility with the expected .NET minor version (for example .NET 10.x), not a specific patch tag or Linux distribution tag.

Warning

Keep the base image updated over time. Security fixes are delivered through image updates, so stale image tags can expose known vulnerabilities.

Backend example

# syntax=docker/dockerfile:1.28

# Use this dockerfile to generate technicaldemos-backend image.
#
# Build arguments :
# - NEOS_VERSION : neos-fmk image version to use for cluster generation (default: latest)
#
# /!\ The build command has to be run from the cluster root directory context :
#   docker build -t technicaldemos-backend:{version} -f ./docker/backend.Dockerfile --build-arg NEOS_VERSION={version} . #from technicaldemos root directory

ARG NEOS_VERSION=latest
# To build the application, we use a neos-fmk image as base so we can directly run the neos commands
FROM "harbor.hexanet.fr:8443/neos/neos-fmk:${NEOS_VERSION}" AS builder

COPY ./modules /usr/src/technicaldemos/modules
COPY ./*.yml /usr/src/technicaldemos/

# The persistence configuration is mandatory for the build but it can be overridden in the appsettings.json file
RUN echo 'PersistenceType: PostgreSQL' > /usr/src/technicaldemos/technicaldemos.persistence.yml
# /!\ DONT PUT YOUR REAL CONNECTIONSTRING HERE /!\
# We just want to generate the application so we need to set a value but it will not be used by the process.
# In production the connectionstring should be overridden either by :
# - mounting a specific appsettings.json file
# - setting the PersistenceSettings__ConnectionString environment variable
RUN echo 'ConnectionString: None' >> /usr/src/technicaldemos/technicaldemos.persistence.yml
WORKDIR /usr/src/technicaldemos

# We only need to build the server part of the cluster
RUN neos generate --build-configuration Release Server

# The backend will run inside a container having dotnet installed
# Use the most recent available tag of the image for the .NET version your cluster targets
FROM mcr.microsoft.com/dotnet/aspnet:10.0.14-alpine3.24

# Install globalization functionality
RUN apk add --upgrade --no-cache \
    icu-data-full \
    icu-libs \
    tzdata

ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false

# Copy the result of the AspNetCore application build into the dotnet image
COPY --from=builder /usr/src/technicaldemos/server/bin/TechnicalDemos.AspNetCore/ /app

# Create an unprivileged user to run the application in an isolated directory (/app)
RUN addgroup -g 1001 -S neos && adduser -u 1001 -S -G neos neos && chown -R neos /app
USER 1001

WORKDIR /app

# Indicates the port on with the backend will be listening
ENV ASPNETCORE_URLS=http://+:7000

# When the container is started, it will run the application backend server
ENTRYPOINT ["dotnet", "TechnicalDemos.AspNetCore.dll"]

Task-runner example

# syntax=docker/dockerfile:1.28

# Use this dockerfile to generate technicaldemos-task-runner image.
#
# Build arguments :
# - NEOS_VERSION : neos-fmk image version to use for cluster generation (default: latest)
#
# /!\ The build command has to be run from the cluster root directory context :
#   docker build -t technicaldemos-task-runner:{version} -f ./docker/task-runner.Dockerfile --build-arg NEOS_VERSION={version} . #from technicaldemos root directory

ARG NEOS_VERSION=latest
# To build the application, we use a neos-fmk image as base so we can directly run the neos commands
FROM "harbor.hexanet.fr:8443/neos/neos-fmk:${NEOS_VERSION}" AS builder

COPY ./modules /usr/src/technicaldemos/modules
COPY ./*.yml /usr/src/technicaldemos/

# The persistence configuration is mandatory for the build but it can be overridden in the appsettings.json file
RUN echo 'PersistenceType: PostgreSQL' > /usr/src/technicaldemos/technicaldemos.persistence.yml
# /!\ DONT PUT YOUR REAL CONNECTIONSTRING HERE /!\
# We just want to generate the application so we need to set a value but it will not be used by the process.
# In production the connectionstring should be overridden either by :
# - mounting a specific appsettings.json file
# - setting the PersistenceSettings__ConnectionString environment variable
RUN echo 'ConnectionString: None' >> /usr/src/technicaldemos/technicaldemos.persistence.yml
WORKDIR /usr/src/technicaldemos

# We only need to build the server part of the cluster
RUN neos generate --build-configuration Release Server

# The task-runner will run inside a container having dotnet installed
# Use the most recent available tag of the image for the .NET version your cluster targets
FROM mcr.microsoft.com/dotnet/aspnet:10.0.14-alpine3.24

# Install globalization functionality
RUN apk add --upgrade --no-cache \
    icu-data-full \
    icu-libs \
    tzdata

ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false

# Copy the result of the task-runner application build into the dotnet image
COPY --from=builder /usr/src/technicaldemos/server/bin/TechnicalDemos.TaskRunner/ /app

# Create an unprivileged user to run the application in an isolated directory (/app)
RUN addgroup -g 1001 -S neos && adduser -u 1001 -S -G neos neos && chown -R neos /app
USER 1001

WORKDIR /app

# Indicates the port on with the task-runner will be listening
ENV ASPNETCORE_URLS=http://+:7000

# When the container is started, it will run the application task-runner server
ENTRYPOINT ["dotnet", "TechnicalDemos.TaskRunner.dll"]

Frontend example

Important

Neos generates a static frontend (client/dist). It can be served by any static hosting solution (Nginx, Apache, Caddy, object storage + CDN, Kubernetes ingress static hosting, etc.). The Nginx-based Dockerfile below is only one possible implementation.

Warning

Keep the base image updated over time. Security fixes are delivered through image updates, so stale image tags can expose known vulnerabilities.

# syntax=docker/dockerfile:1.28

# Use this dockerfile to generate technicaldemos-frontend image.
#
# Build arguments :
# - NEOS_VERSION : neos-fmk image version to use for cluster generation (default: latest)
#
# Use this dockerfile to generate technicaldemos-frontend image.
# /!\ The build command has to be run from the cluster root directory context :
#   docker build -t technicaldemos-frontend:{version} -f ./docker/frontend.Dockerfile --build-arg NEOS_VERSION={version} . #from technicaldemos root directory

ARG NEOS_VERSION=latest
# To build the application, we use a neos-fmk image as base so we can directly run the neos commands
FROM "harbor.hexanet.fr:8443/neos/neos-fmk:${NEOS_VERSION}" AS builder

COPY ./modules /usr/src/technicaldemos/modules
COPY ./*.yml /usr/src/technicaldemos/

# The persistence configuration is mandatory for the build but it can be overridden in the appsettings.json file
RUN echo 'PersistenceType: PostgreSQL' > /usr/src/technicaldemos/technicaldemos.persistence.yml
# /!\ DONT PUT YOUR REAL CONNECTIONSTRING HERE /!\
# We just want to generate the application so we need to set a value but it will not be used by the process.
# In production the connectionstring should be overridden either by :
# - mounting a specific appsettings.json file
# - setting the PersistenceSettings__ConnectionString environment variable
RUN echo 'ConnectionString: None' >> /usr/src/technicaldemos/technicaldemos.persistence.yml

WORKDIR /usr/src/technicaldemos
# Build the cluster
RUN neos generate --build-configuration Release

WORKDIR /usr/src/technicaldemos/client
# Build the client website
RUN npm install
RUN npm run build

# Example only: using Nginx to serve the generated static files
# Always pin the base image to an explicit, fixed tag (never a floating tag such as
# "stable-alpine-slim") so every build is reproducible and doesn't silently pick up a
# newer, heavier image over time.
FROM nginx:1.31.6-alpine-slim
# Provides some basic nginx configuration
COPY ./docker/frontend/nginx/nginx.conf /etc/nginx/conf.d/default.conf
# Copy the client website files into the nginx serving directory
COPY --from=builder /usr/src/technicaldemos/client/dist /usr/share/nginx/html

With nginx.conf file :

server_tokens off;

server {
  listen 80;
  server_name frontend;
  underscores_in_headers on;

  root /usr/share/nginx/html;

  location / {
    rewrite ^/$ /index.html break;
    try_files $uri =404;
    add_header Cache-Control "no-cache";
  }

  location /index.html {
    add_header Cache-Control "no-cache";
  }
}

Base image security and maintenance

Keeping container base images up to date is part of deployment operations.

  • Regularly update base image tags to include security fixes.
  • Scan produced images for vulnerabilities in your CI/CD pipeline.
  • Rebuild and redeploy images when upstream security advisories are published.
  • Prefer explicit and traceable tags in production (avoid relying on long-lived floating tags).

Secrets

In a deployed environnement, all sensitive data should be stored in Kubernetes secrets. Neos backend services (cluster, Tenant Management, reports, ...) use Dapr secret stores to read values from Kubernetes secrets and mount them as dotnet configuration.

Note

To create a simple key value secret in Kubernetes which will be usable in Neos you can use the following command (example for authentication secret in Powershell) :

kubectl create secret generic neos-auth-env `
  --from-literal=Authentication__Preset="AzureAdB2C" `
  --from-literal=Authentication__Authority="https://SomeApplication.b2clogin.com/SomeApplication.onmicrosoft.com/B2C_1A_SIGNUP_SIGNIN/v2.0" `
  --from-literal=Authentication__ClientId="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" `
  --from-literal=Authentication__ClientSecret="somesecretvalue" `
  --from-literal=Authentication__Scopes__0="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"

Authentication

Because authentication configuration needs to be known by gateway you must set the authenticationSecret property in your Helm values files so it contains all the necessary variables for authentication.

Note

The environment variables are dotnet override of the appsettings.json file. Please see this article for more information.

Example (Neos development authentication server) :

Authentication__Preset=NeosDevAuth

Example (Microsoft Entra External ID) :

Authentication__Preset=MicrosoftEntraExternalID
Authentication__Authority=https://SomeApplication.ciamlogin.com/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/v2.0
Authentication__ClientId=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Authentication__ClientSecret=somesecretvalue

Example (Azure AD B2C) :

Authentication__Preset=AzureAdB2C
Authentication__Authority=https://SomeApplication.b2clogin.com/SomeApplication.onmicrosoft.com/B2C_1A_SIGNUP_SIGNIN/v2.0
Authentication__ClientId=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Authentication__ClientSecret=somesecretvalue
Authentication__Scopes__0=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

Example (Azure Active Directory) :

Authentication__Preset=AzureAd
Authentication__Authority=https://login.microsoftonline.com/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/v2.0
Authentication__ClientId=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Authentication__ClientSecret=somesecretvalue

Example (Auth0) :

Authentication__Preset=Auth0
Authentication__Authority=https://someapplication.eu.auth0.com
Authentication__ClientId=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Authentication__ClientSecret=somesecretvalue

Example (Okta, with a manual configuration) :

Authentication__Authority=https://someapplication.okta.com
Authentication__ClientId=xxxxxxxxxxxxxxxxxxxx
Authentication__ClientSecret=somesecretvalue
Authentication__Scopes__0=email
Authentication__UserPropertiesMap__Identifier=email
Authentication__UserPropertiesMap__Email=email
Authentication__UserPropertiesMap__FirstName=given_name
Authentication__UserPropertiesMap__LastName=family_name
Note

If one or several cluster in your deployment is not subject to authentication you need to add their host to the AnonymousHosts configuration of the gateway :

AnonymousHosts__0=first.unauthenticated.cluster.com
AnonymousHosts__1=second.unauthenticated.cluster.com

Gateway

The gateway provides authentication and routing to the right cluster version. You can override dotnet configuration using environment variables. These environment variables should be set in a secret which will then be mounted inside the gateway container.

Note

For gateway routing and unavailable-tenants synchronization, see Gateway Routing, CORS, and Unavailable Tenants Settings. For CORS parameters, see Gateway CORS configuration.

You can set the secret name as the value of gateway.envSecret property in your Helm values file.

Note

Status code pages can be configured for the gateway, including dedicated pages for disabled or unavailable tenants. See Custom status code pages for the available settings, image layout, and Dockerfile example.

Note

If you configured a secret for authenticationSecret, its variables will be mounted inside the gateway container.

Note

If your application has server methods which are not subject to authentication (eg Authentication required set to false in the server method configuration), you need to add them to the AnonymousRoutes configuration of the gateway, either with the gateway.anonymousRoutes list of your Helm values file:

gateway:
  anonymousRoutes:
    - /api/v1/firstanonymousmethod
    - /api/v1/secondanonymousmethod

or with environment variables in the gateway secret:

AnonymousRoutes__0=/api/v1/firstanonymousmethod
AnonymousRoutes__1=/api/v1/secondanonymousmethod

Both sources add up: the chart numbers the Helm values from 1000, so keep the indexes of the environment variables below 1000. The path is matched exactly and whatever the host, so the route is anonymous for every cluster behind the gateway: give the server method a route of its own.

A route can contain * segments when a part of the path varies, such as the tenant of a multitenant cluster. Each * stands for exactly one non-empty segment, and the path must have the same number of segments as the route:

gateway:
  anonymousRoutes:
    - /*/api/v1/webhooks/public/*

This route matches /acme/api/v1/webhooks/public/samsara, but neither /api/v1/webhooks/public/samsara nor /acme/api/v1/webhooks/public/samsara/events. On a route such as /{tenant}/api/v1/..., the gateway always takes the tenant from the path: a Neos-Tenant header sent by the caller is replaced.

Reporting

The Stimulsoft license has to be configured inside the report container so you will need to provide the license key in a secret for the Reporting__StimulsoftLicenceKey environment variable.

You can set the secret name as the value of report.envSecret property in your Helm values file.

Tenant Management

The Tenant Management needs to run with a database so you'll need to provide the connection string in a secret for the PersistenceSettings__ConnectionString environment variable.

Note

Connection string needs to follow the DotNet syntax.

Tenant Management also needs an encryption key in the same secret, for the EncryptionKey environment variable. See Encryption key.

Warning

For Oracle database, you'll also need to disable concurrency access mode. To do so, you should add PersistenceSettings__DefaultConcurrencyAccessMode key with its value set to Disabled.

You can set the secret name as the value of tenantManagement.envSecret property in your Helm values file.

If you need to expose the Tenant Management cluster outside of Kubernetes, you need to provide a TLS certificate as a Kubernetes secret.

You can set the secret name as the value of tenantManagement.tlsSecret property in your Helm values file.

Encryption key

Tenant Management stores the connection strings of the database servers encrypted in its database. It encrypts and decrypts them with the key read from the EncryptionKey configuration. This key must be a random value of 32 bytes, encoded in base64.

Important

Create the encryption key at the first deployment of Tenant Management, before its first start. The Helm chart replaces the appsettings.json of the Tenant Management image with its own, which has no EncryptionKey: without the key in the secret, Tenant Management fails as soon as it accesses its database, with the error Secret encryption key configuration 'EncryptionKey' is required.

Caution

Outside the Helm chart, a Tenant Management started with the appsettings.json of its image uses the default key of this file. This key is the same for every Neos installation and is not a secret: never use it outside a development environment.

  1. Generate the key with a cryptographically secure random generator, directly into a new file that only you can read:

    • On Windows, with PowerShell 7, in your profile folder, which only you, the system and the administrators of the machine can read:

      $keyFile = Join-Path $HOME 'tenant-management-encryption-key.txt'
      [Convert]::ToBase64String([System.Security.Cryptography.RandomNumberGenerator]::GetBytes(32)) | Out-File -FilePath $keyFile -NoNewline -NoClobber
      
    • On Linux or macOS, with OpenSSL. umask 077 creates the file readable by you only:

      keyFile=~/tenant-management-encryption-key.txt
      (umask 077 && set -o noclobber && key=$(openssl rand -base64 32) && printf '%s' "$key" > "$keyFile")
      

    The file contains a string of 44 characters ending with =. Both commands fail when the file already exists, so the key never lands in an older file with wider permissions. Writing the key to a file keeps it out of the command lines, which the shell history and the process list would otherwise keep in clear.

  2. Keep a copy of the key in a secure vault (for example your password manager or your cloud provider's key vault), apart from the Kubernetes cluster. Restoring a backup of the Tenant Management database on another environment requires the same key.

  3. Add the key to the Tenant Management secret, with the connection string. kubectl create secret creates the whole secret, so give all its keys in the same command. Create the secret in the namespace of the Helm release, the one given to -n in helm upgrade --install: the chart looks for the secret in this namespace only and fails with Tenant Management secret ... not found otherwise.

    kubectl create secret generic neos-tenant-management-env -n <your-namespace> `
      --from-literal=PersistenceSettings__ConnectionString="Host=...;Database=...;Username=...;Password=..." `
      --from-file=EncryptionKey=$keyFile
    

    or with bash:

    kubectl create secret generic neos-tenant-management-env -n <your-namespace> \
      --from-literal=PersistenceSettings__ConnectionString="Host=...;Database=...;Username=...;Password=..." \
      "--from-file=EncryptionKey=$keyFile"
    

    Then delete the key file (Remove-Item $keyFile in PowerShell, rm "$keyFile" in bash). To recreate the secret later, write the key from your vault to a file again rather than typing it in the command.

    Tip

    If your Kubernetes cluster synchronizes its secrets from a vault (for example with the External Secrets Operator), store the key in the vault only and map it to the EncryptionKey entry of the synchronized secret.

  4. Set the secret name as the value of tenantManagement.envSecret in your Helm values file:

    tenantManagement:
      envSecret: neos-tenant-management-env
    
Warning

The encryption key is the only way to read the stored connection strings. Once Tenant Management has stored connection strings, never change or lose the key: with another key, Tenant Management can no longer decrypt them and no longer reaches the tenant databases. Tenant Management has no key rotation mechanism.

License Management

The License Management needs to run with a database so you'll need to provide the connection string in a secret for the PersistenceSettings__ConnectionString environment variable.

Note

Connection string needs to follow the DotNet syntax.

You can set the secret name as the value of licenseManagement.envSecret property in your Helm values file.

If you need to expose the License Management cluster outside of Kubernetes, you need to provide a TLS certificate as a Kubernetes secret.

You can set the secret name as the value of licenseManagement.tlsSecret property in your Helm values file.

Task runner actor state store (PostgreSQL)

Neos ecosystem relies on Dapr workflow components for its background server methods to run.

In the Kubernetes cluster, a specific actor state store component needs to be configured. See this article for supported state stores.

Neos Helm chart generates the component specs for Redis and PostgreSQL (v1).

By default the chart will generate the Redis actor state store component spec.

Warning

Redis is not recommended by Dapr as actor state store in production due to its limitations regarding transactions. Thus, we recommend to use postgresql preset instead.

In case you use the PostgreSQL (v1) component spec (actorStateStore.preset property of the Helm values file set to postgresql) you'll need to provide the name of a secret with the PostgreSQL connection string to use in the actorStateStore.postgresqlConnectionStringSecret property of your Helm values file.

The secret should have a connectionString entry like the following example :

kubectl create secret generic neos-actor-state-store-postgresql-secret `
  --from-literal=connectionString="host=localhost user=postgres password=example port=5432 connect_timeout=10 database=my_db"

Neos AI

The Neos AI cluster needs to run with a database so you'll need to provide the connection string in a secret for the PersistenceSettings__ConnectionString environment variable.

Note

Connection string needs to follow the DotNet syntax.

You can set the secret name as the value of neosAI.envSecret property in your Helm values file.

If you need to expose the Neos AI cluster outside the Kubernetes cluster, you need to provide a TLS certificate as a Kubernetes secret.

You can set the secret name as the value of neosAI.tlsSecret property in your Helm values file.

Task Scheduler

The Task Scheduler cluster needs to run with a database so you'll need to provide the connection string in a secret for the PersistenceSettings__ConnectionString environment variable.

Note

Connection string needs to follow the DotNet syntax.

You can set the secret name as the value of taskScheduler.envSecret property in your Helm values file.

If you need to expose the Task Scheduler cluster outside the Kubernetes cluster, you need to provide a TLS certificate as a Kubernetes secret.

You can set the secret name as the value of taskScheduler.tlsSecret property in your Helm values file.

Support Center

The Support Center cluster needs to run with a database so you'll need to provide the connection string in a secret for the PersistenceSettings__ConnectionString environment variable.

Note

Connection string needs to follow the DotNet syntax.

You can set the secret name as the value of supportCenter.envSecret property in your Helm values file.

If you need to expose the Support Center cluster outside the Kubernetes cluster, you need to provide a TLS certificate as a Kubernetes secret.

You can set the secret name as the value of supportCenter.tlsSecret property in your Helm values file.

Clusters

If you need to expose the cluster application outside the Kubernetes cluster, you need to provide a TLS certificate as a Kubernetes secret.

You can set the secret name as the value of clusters[n].tlsSecret property in your Helm values file.

If the cluster's backend needs to run with a database so you'll need to provide the connection string in a secret for the PersistenceSettings__ConnectionString environment variable.

Note

Connection string needs to follow the DotNet syntax.

You can set the secret name as the value of clusters[n].backend.envSecret property in your Helm values file.

Encryption key of a business cluster

A cluster that stores encrypted data in its database, such as the secrets of the receivers of the NeosWebhooks module, encrypts and decrypts it with the key read from the EncryptionKey configuration. This key must be a random value of 32 bytes, encoded in base64.

Caution

Without EncryptionKey in the secret, the cluster uses the key of the appsettings.json of its image. Unless the cluster configuration sets one, this is the default key of Neos, the same for every installation and not a secret: never deploy a cluster with it.

Generate a key for each cluster as for Tenant Management, keep a copy in a secure vault, and add it as the EncryptionKey entry of the secret of the cluster: clusters[n].backend.envSecret, or clusters[n].versions[m].backend.envSecret for each version of a multitenant cluster. The task runner of the cluster reads the same secret.

kubectl create secret generic neos-my-cluster-env -n <your-namespace> `
  --from-literal=PersistenceSettings__ConnectionString="Host=...;Database=...;Username=...;Password=..." `
  --from-file=EncryptionKey=$keyFile

For a multitenant cluster, leave out PersistenceSettings__ConnectionString: the connection strings of the tenants come from Tenant Management.

Warning

Create the key before the first start of the cluster, and never change or lose it afterwards: the data encrypted with a key can only be read with this key. With another key, the receivers of the NeosWebhooks module can no longer read their secrets and refuse every signed request, until an administrator adds their secrets again.

To allow the Kubernetes cluster to pull the images of your cluster, you may need to create a secret containing your registry credentials.

You can set the secret names as the values of clusters[n].backend.imagePullSecret and clusters[n].frontend.imagePullSecret property in your Helm values file.

Configuration of the user synchronization

When the UserPermissions module synchronizes the users with the authentication server, you'll need to provide its configuration to Tenant Management (tenantManagement.envSecret) and to your clusters using UserPermissions (clusters[n].backend.envSecret, or clusters[n].versions[m].backend.envSecret for each version of a multi-tenant cluster). This configuration allows UserPermissions to create and read the users of the authentication server.

You can set the environment variables of your authentication server in these secrets, as in the following examples.

NeosAuth

UserPermissions__AuthProvider=NeosAuth
UserPermissions__NeosAuth__Authority=https://auth.neos.groupeisagri.com

Microsoft Entra External ID

UserPermissions__AuthProvider=MicrosoftEntraExternalID
UserPermissions__MicrosoftEntraExternalID__ClientId=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
UserPermissions__MicrosoftEntraExternalID__TenantId=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
UserPermissions__MicrosoftEntraExternalID__TenantName=xxxx.onmicrosoft.com
UserPermissions__MicrosoftEntraExternalID__ClientSecretKey=yoursecret

The cluster sends an email with a temporary password to the created users: see Temporary password email.

Okta

The Okta synchronization is supplied by the UserPermissionsOkta module, which the cluster must reference in addition to UserPermissions.

UserPermissions__AuthProvider=Okta
UserPermissions__Okta__Domain=https://someapplication.okta.com
UserPermissions__Okta__ClientId=xxxxxxxxxxxxxxxxxxxx
UserPermissions__Okta__KeyId=yourkeyid
UserPermissions__Okta__PrivateKey=-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----
UserPermissions__Okta__GroupIds__0=00gxxxxxxxxxxxxxxxxx
UserPermissions__Okta__ApplicationId=0oaxxxxxxxxxxxxxxxxx
UserPermissions__Okta__UseOktaActivationEmail=true

GroupIds, ApplicationId and UseOktaActivationEmail are optional. When UseOktaActivationEmail is false, the cluster sends an email with a temporary password to the created users: see Temporary password email.

Tip

The private key is in PEM format, on several lines. The simplest way to put it in the secret is to read it from its file with --from-file, along with the other variables of the backend, since kubectl create secret creates the whole secret :

kubectl create secret generic northwind-env-secret `
  --from-literal=PersistenceSettings__ConnectionString="Host=...;Database=...;Username=...;Password=..." `
  --from-literal=UserPermissions__AuthProvider="Okta" `
  --from-literal=UserPermissions__Okta__Domain="https://someapplication.okta.com" `
  --from-literal=UserPermissions__Okta__ClientId="xxxxxxxxxxxxxxxxxxxx" `
  --from-literal=UserPermissions__Okta__KeyId="yourkeyid" `
  --from-file=UserPermissions__Okta__PrivateKey=./okta-private-key.pem

Azure AD B2C

UserPermissions__AuthProvider=AzureAdB2C
UserPermissions__AzureAdB2C__ClientId=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
UserPermissions__AzureAdB2C__TenantId=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
UserPermissions__AzureAdB2C__TenantName=xxxx.onmicrosoft.com
UserPermissions__AzureAdB2C__ClientSecretKey=yoursecret
UserPermissions__AzureAdB2C__B2cExtensionAppClientId=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

The cluster sends an email with a temporary password to the created users: see Temporary password email.

Note

The former AuthenticationMode=AzureB2C variable and AzureB2COption__* variables are still read, but are replaced by the UserPermissions__* variables above.

Temporary password email

With Microsoft Entra External ID, Azure AD B2C, and Okta when UseOktaActivationEmail is false, the cluster sends the temporary password of the created users by email, through SendGrid:

UserPermissions__EmailContent__RedirectUrl=https://yourapp/
UserPermissions__EmailContent__Title=Your title
UserPermissions__EmailContent__Content1=Log in with your email address ({Email}) and the temporary password below.
UserPermissions__EmailContent__Content2=Your temporary password: {Password}
UserPermissions__EmailContent__LinkText=Text
SendGrid__Key=yoursecret
SendGrid__From__Name=yourappname
[email protected]

You can disable this email by setting UserPermissions__SendTemporaryPasswordEmail to false. See Email content for the placeholders of the email content: the temporary password must appear in it through the {Password} placeholder.

Configure the first administrator user with UserPermissions module

If your deployed application is not multi-tenant, when the database is created, there are no users yet. To create the first administrator user, you can configure a new environment variable UserPermissions__DefaultUserAccountLogin or see Configuration in ASP.NET Core.

When the database is migrated, this user will be automatically created with all permissions.

Sending logs to Application Insights

Each Neos application can be configured to send logs to Application Insights, please see this article for more information.

Warning

Backend .NET SDK-based Application Insights settings in this chapter are deprecated and will be removed in a future version. Prefer OpenTelemetry Collector routing for backend telemetry and exporters. See OpenTelemetry collector migration.

Environnement variables for .Net processes

You can configure Application Insights for Neos .Net processes by setting options as environnement variables in the secret associated to the process.

The following processes are supported :

Example (cluster backend)

ApplicationInsights__ConnectionString=1a11a111-1a1a-1111-a111-1aaa1a1a1a11;IngestionEndpoint=https://westeurope-5.in.  applicationinsights.azure.com/;LiveEndpoint=https://westeurope.livediagnostics.monitor.azure.com/;  ApplicationId=2b22b222-2b2b-2222-b222-2bbb2b2b2b22
ApplicationInsights__EnableHeartbeat=false
ApplicationInsights__RoleName=NorthwindServer
ApplicationInsights__RequestCollectionOptions__TrackExceptions=true
Note

For more information on available options, see this article.

Log level

The default setting for Application Insights is to only capture Warning and more severe logs (see this article).

It is possible to modify this configuration with the Logging_ApplicationInsights__LogLevel__Default environment variable.

In the following example, Information and more severe logs will be sent to Application Insights even if Debug logs are emitted in the console output :

Logging__LogLevel__Default=Debug
Logging__ApplicationInsights__LogLevel__Default=Information
Exclude specific successful endpoints

By default, all request and traces will be sent to Application Insights except healthcheck (/hc & /healthz) and metrics (/metrics) endpoints if their status code is unset or less than 400.

If you want to exclude traces and requests associated to specific endpoints when the status code is unset or less than 400, you can set the ApplicationInsights__ExcludedSuccessfulEndpoints value (string array).

Warning

Don't forget to include /hc, /healthz and /metrics in the array in addition to your specific endpoints otherwise it could be overridden.

Example to exclude /api/v1/methods/some-specific-method and /api/v1/SomeEntityView request and traces when the response status code is unset or less than 400 :

ApplicationInsights__ExcludedSuccessfulEndpoints__0=/hc
ApplicationInsights__ExcludedSuccessfulEndpoints__1=/healthz
ApplicationInsights__ExcludedSuccessfulEndpoints__2=/metrics
ApplicationInsights__ExcludedSuccessfulEndpoints__3=/api/v1/methods/some-specific-method
ApplicationInsights__ExcludedSuccessfulEndpoints__4=/api/v1/SomeEntityView

Open Telemetry

You can configure Open telemetry for Neos .Net processes by setting options as environnement variables in the secret associated to the process.

The following processes are supported :

Tracing

Exclude specific endpoints

By default, /hc, /healthz and /metrics endpoints are not sent by open telemetry exporter.

You can customize the list of excluded endpoints by setting the Tracing__ExcludedEndpoints value (string array).

Warning

Don't forget to include /hc, /healthz and /metrics in the array in addition to your specific endpoints otherwise it could be overridden.

Example to exclude /api/v1/methods/some-specific-method and /api/v1/SomeEntityView traces :

Tracing__ExcludedEndpoints__0=/hc
Tracing__ExcludedEndpoints__1=/healthz
Tracing__ExcludedEndpoints__2=/metrics
Tracing__ExcludedEndpoints__3=/api/v1/methods/some-specific-method
Tracing__ExcludedEndpoints__4=/api/v1/SomeEntityView

JSON configuration for frontend

To configure Application Insights for frontend, you can create a secret containing an applicationInsights.json file content.

Example

JSON file content :

{
  "clusterVersion": "0.5.1",
  "connectionString": "1a11a111-1a1a-1111-a111-1aaa1a1a1a11;IngestionEndpoint=https://westeurope-5.in.  applicationinsights.azure.com/;LiveEndpoint=https://westeurope.livediagnostics.monitor.azure.com/;  ApplicationId=2b22b222-2b2b-2222-b222-2bbb2b2b2b22",
  "correlationHeaderExcludedDomains": ["domain1.com", "domain2.com"],
  "disableAjaxTracking": true,
  "enableAutoRouteTracking": true,
  "enableCorsCorrelation": true,
  "enableUnhandledPromiseRejectionTracking": true,
  "neosVersion": "1.21.0",
  "roleName": "NorthwindClient"
}

Associated secret :

apiVersion: v1
kind: Secret
data:
  applicationInsights.json: <base64encodedJSONfilecontent>
metadata:
  name: northwind-frontend-conf
Note

To create the secret directly from an existing applicationInsights.json file on your computer you can use the following command :

kubectl create secret generic <your-secret-name> --from-file=path/to/the/file/applicationInsights.json

See this article for more information on how to create secrets in Kubernetes.

Note

For more information on available options, see this article.

Ingress controller

This charts is configured to work with a Nginx ingress controller.