Self-managed Cortex

For organizations with strict security and compliance requirements, Cortex can be deployed on-premises into your own Kubernetes cluster.

The Cortex installation is provided as a Helm chart.

Due to the frequency of our releases and rapid changes to our product, we only offer dedicated support for releases up to two months prior to the current release version. For versions older than this we will provide best-effort support. If you are currently using a release older than two months, we strongly recommend updating to the latest release. If you encounter an issue while using a version of Cortex older than two months, you may be asked to upgrade as part of our ongoing support efforts.

Additional self-managed resources

After following the instructions on this page to get started with your self-managed Cortex account, see these additional guides to enable other features and integrations:

Getting started with a Cortex self-managed account

To deploy Cortex on-premises, you will:

  • Make sure you have the required environment and infrastructure set up

  • Create a PostgreSQL database where Cortex data will be stored

  • Create Kubernetes secrets for pulling software images and connecting to the database

  • Create a YAML file containing your configuration values for the Cortex Helm chart

  • Preview the Kubernetes deployment

  • Deploy using Helm

  • Configure DNS records to allow users to access Cortex

Prerequisites

See the prerequisites below:

Prerequisites

Before getting started, you will need:

  • A running Kubernetes cluster, configured with permission to make HTTPS requests to the public internet (our Docker images are hosted on GitHub) - you can use AWS/EKS, Azure/AKS, GCP/GKE, or your own locally installed Kubernetes distribution.

  • Permission to add services, deployments, secrets and namespaces to the cluster

  • kubectl installed and connected to your Kubernetes cluster

  • A PostgresSQL database (15+) that is accessible from your k8s cluster, and has a dedicated database and user for Cortex.

  • A GitHub personal access token (PAT) and a JWT license key, both provided to you by your Cortex account team

Required resources in Kubernetes

Below is a list of the containers that make up a minimum Cortex deployment, with their resource requirements:

  • 2 instances of backend container, with 8GB memory per instance (2 cores recommended)

  • 1 instance of worker container, with 8GB memory (2 cores recommended)

  • 1 instance of frontend container

    • 500MB memory is generally sufficient, but you can adjust based on your available resources.

Ensure that your cluster has enough resources available to run the containers you plan to deploy.

DNS and TLS requirements

Before installing Cortex, you need to prepare:

  • DNS Names: Create two DNS records in your domain:

    • One for the frontend (web UI): e.g., cortex-app.yourdomain.dom

    • One for the backend (API): e.g., cortex-api.yourdomain.dom

  • TLS Certificate: Obtain a TLS certificate that covers both DNS names. You can use:

    • A wildcard certificate (e.g., *.yourdomain.dom)

    • A certificate with Subject Alternative Names (SANs) for both DNS names

    • For AWS: AWS Certificate Manager (ACM)

    • For Azure: Azure Key Vault certificates

  • DNS Configuration: After installation, you'll need to point DNS records for the frontend and backend donain names to the two load balancers created by Kubernetes. The exact configuration depends on your environment:

    • For AWS: Point to each NLB's DNS name

    • For Azure: Point to each Load Balancer's public IP

    • For self-managed load balancers: Point to your load balancers' IPs or hostnames

Verify Kubernetes connection

Check kubectl installation and cluster access:

Check Helm installation:

Step 1: Set up a persistent database

Cortex stores all persistent data in a PostgreSQL database. You will need to set up a database in order to install and use Cortex.

Step 1.1: Database prerequisites

Cortex requires PostgreSQL 15 or higher and does not support other datastores. You have several options, depending on your environment:

  • Self-managed PostgreSQL: Install PostgreSQL on your own infrastructure

  • AWS RDS: Use Amazon RDS for PostgreSQL if running on AWS

  • Azure Database for PostgreSQL: Use Azure's managed PostgreSQL service if running on Azure

You will need the following resources:

  • 15GB minimum available storage, 40 GB recommended

  • Maximum number of connections ≥100. The maximum number of connections should be ≥2x the backend connection pool size, which is 25 per instance, for a total of 50 when two backend containers are running.

Step 1.2: Create a Postgres database
  1. On your PostgreSQL server, create a database with UTF-8 encoding that will be accessible from your instance.

  2. Create a user with the following permissions on the public.schema within the database you created:

    • Add ALL on the public schema to create/modify tables

    • Add INSERT, SELECT, UPDATE, DELETE on all tables in the public schema

  3. Make a note of the following database connection details, as you will need them in the next steps:

    • The hostname and port number for connecting to the database server

    • The name of the database you created (this guide assumes the database is named cortex)

    • The username and password to access the database

Here is an example of SQL that you might run to create the database and the user with sufficient permissions:

Step 1.3: Verify database connection

Verify that containers in your Kubernetes cluster will be able to connect to your Postgres database using the connection details you noted above:

This command will:

  • Connect to your database from within the cluster

  • Create and drop a test table to verify write permissions

  • Exit with an error if the database doesn't exist or the user lacks permissions

If it is successful, you will see:

Step 2: Configure your Kubernetes cluster

Configure Kubernetes namespace and secrets
  1. Create a Kubernetes namespace. A namespace is optional, but it's recommended to install Cortex in its own namespace for better organization and isolation:

  1. Run the following command to add a Docker registry secret for pulling Cortex images from the GitHub Container Registry:

  • Replace <enter the GitHub PAT provided by Cortex> with the value of the GitHub PAT that was provided to you by your Cortex account team, and replace <[email protected]> with an email address.\

  1. Run the following command to create a secret for your Cortex license and your database connection details:

  • Substitute the placeholders in this command with the actual values of your port, username, password, database, and Cortex license JWT.

See the Kubernetes documentation for more information on managing secrets using kubectl.

Step 3: Add the Cortex repo to Helm

Add the Cortex Helm repo
  1. In command line, run the following command to add the Cortex repo to Helm:

  1. Install the Helm diff plugin to preview changes before applying them:

Step 4: Configure and install the helm chart

Chart defaults

Note the following:

  • The default values for resource allocation in the chart are provided as guidance, but you may need to adjust these depending on your resource consumption.

  • User authentication is disabled by default. After installing Cortex, visiting the frontend URL in a browser will immediately log you in as a demo user. You will then be able to configure authentication using an OIDC provider.

  • The chart does not ship with built in TLS or certificates. If you are deploying Cortex into a cluster behind your existing load balancer with TLS, change the protocol value in the chart.

Step 4.1: Configure the helm chart

It's not recommended to modify the Helm chart or values.yaml directly. Instead, we recommend that you create a separate overrides.yaml file for your configuration. This approach ensures:

  • Configurations are preserved across upgrades

  • Easy version control of your settings

  • Clear separation between defaults and customizations

  • Timely support from Cortex in the event of an issue

Cortex requires at minimum one frontend pod, one backend pod, and one worker pod. See the example overrides.yaml below demonstrating this.

In this step, you will create and configure the overrides.yaml file. In the next step, you will install the chart and include the overrides.yaml.

See a list of available settings

To see a complete list of available settings, you can review the values.yaml that is bundled in the Cortex Helm chart by running the following command:

Recommended configurations in overrides.yaml

In your overrides.yaml, adjust the replica counts and resource allocations based on your expected load and availability requirements.

At a minimum, the following values should be set:

  • image.secrets.name : The name of the Docker registry secret you set in step 2

  • app.service.type : Set this field based on your infrastructure. If you're using EKS, AKS, or GKE, set the type as LoadBalancer. If you're using your own Kubernetes distribution and plan to do your own load balancing, configure NodePort.

  • app.ingress.type: The type of ingress controller to use with your load balancing setup. For EKS and AKS, use nginx . For GKE, use gcp. If you are using your own Kubernetes distribution, this setting is not used.

  • app.secret : The name of the secret you set up in step 2 containing your Cortex license key and database connection details

  • app.hostnames.frontend : The DNS name where users will visit the Cortex Web UI in the browser. This should match the common name or a subject alternative name on the TLS certificate you use with your load balancer for the frontend service.

  • app.hostnames.backend : The DNS name where the Cortex REST API will be available. This should match the common name or a subject alternative name on the TLS certificate you use with your load balancer for the backend service.

  • app.hostnames.protocol : Set to https if you are configuring TLS termination at the load balancer (recommended), or http if you will use Cortex over cleartext. Note that this field does not set up TLS - TLS is configured at the load balancer; this setting is used only for generating URLs.

  • app.backend.enabled : Set to true to enable the backend containers to run

  • app.worker.enabled : Set to true to enable the worker containers to run

Additional container configuration:

There are additional settings available to configure the backend and worker for your environment. The following paths can be configured under app.backend and app.worker:

  • replicaCount (default: 2) This field determines how many containers to create. Depending on your resource constraints, you may need to change this to 1.

  • containerConfiguration.resources: Depending on your resource constraints, you may need to adjust requests.memory and limits.memory. By default, each container will consume a minimum of 8GB and a limit of 16GB.

  • containerConfiguration.livenessProbe.periodSeconds (default: 10) and containerConfiguration.livenessProbe.timeoutSeconds (default: 6): In environments with slow networking or database, probes may expire before the containers are fully started, causing them to continually be restarted. If this occurs, you may need to raise the values of these settings.

Example overrides.yaml

Below is an example overrides.yaml implementing one backend pod and one worker pod with minimum resources, and configuring TLS termination using network load balancers in AWS:

Step 4.3: Install the helm chart
  1. Identify the version of Cortex you want to install.

    • Consider consulting with your Cortex account team to identify the appropriate version. Alternatively, you can find the latest version by running helm search repo cortex/cortex. Cortex version numbers look like 0.0.411.

    • Make a note of the version you are installing, in case you want to change configuration values while remaining on the same version.

  2. Before installation, use the following command to preview what will be deployed. Replace DESIRED_VERSION with the version number you identified in the previous step:

  1. Use helm to install Cortex. Replace DESIRED_VERSION with the version number you identified in the first step:

  1. Monitor the deployment status:

The backend is ready when you see log lines containing:

It may take a few minutes, depending on your infrastructure.

Step 5: Configure DNS

After installation, Kubernetes will create two load balancers: one for the frontend and one for the backend. Configure your DNS records:

Configure and verify DNS
  1. Get the load balancer endpoints:

    You'll see two services of type LoadBalancer. Identify which is frontend and which is backend by their names.

  2. Update your DNS records:

    • AWS NLB: Create CNAME records pointing to the respective load balancer DNS names.

    • Azure: Create A records pointing to the respective load balancer IPs.

    • Point your frontend hostname (for example, cortex-app.yourdomain.dom) to the frontend load balancer.

    • Point your backend hostname (for example, cortex-api.yourdomain.dom) to the backend load balancer.

  3. Verify DNS resolution:

  4. Test access to Cortex:

    • Open your frontend URL (for example, https://cortex-app.yourdomain.dom) in your web browser.

      • You should be logged in automatically as "Demo User."

      • After this, you can configure SSO for proper authentication.

Additional configuration for a cloud cluster installation

If you're installing Cortex into a cloud cluster, for example EKS or GKE, you may need to install required plugins to your cluster. For example, kubectl apply the nginx plugin for AWS.

Backup and recovery

As a best practice, we recommend regularly backing up the following:

  • Your Kubernetes secrets

    • To get copies of your Kubernetes secrets as YAML files that can be restored using kubectl apply, use commands like the following:

  • Your overrides.yaml file

    • This contains all the configuration values for your Cortex installation. We recommend keeping this file in a revision control system.

  • Your PostgresSQL database

    • This contains all of your Cortex data. We recommend that you implement a regular backup schedule and periodically test restore procedures.

Recovery process

  1. Create namespace if it doesn't exist: kubectl create namespace cortex

  2. Restore PostgreSQL database from backup

  3. Apply Kubernetes secrets to the cortex namespace

  4. Install Cortex using your saved overrides.yaml and the appropriate version

Backup and recovery best practices

  • Don't fork or modify the Helm chart: If you fork or modify the Helm chart itself, it will be much more difficult to obtain timely support from Cortex. If it seems like the chart needs to be modified, please reach out to your Cortex team to see if there are value overrides available, or if Cortex can modify the official Helm chart to meet your needs.

  • Version Control: Keep your overrides.yaml in version control

  • Documentation: Document any custom configurations or integrations

  • Testing: Always test upgrades in non-production first

  • Monitoring: Set up monitoring for Cortex components

  • Regular Updates: Stay current with Cortex releases for security and feature updates

  • Backup Verification: Regularly test your backup and restore process

Additional configuration options

Modifying the configuration after installing

You can make changes to your configuration by editing overrides.yaml and applying them using helm upgrade. First, identify the Cortex version you are running - the Cortex version appears at the bottom of the left-hand nav in the settings page in the Cortex UI. Cortex version numbers look like 0.0.411.

Next, preview the changes:

Read the output carefully to be sure that no unexpected changes will be made. Finally, apply the updated configuration using helm upgrade:

Self-Managed SSL Certificates

This configuration is used when Cortex needs to act as a client connecting to other services in your infrastructure that use self-signed or private CA certificates. Common use cases include:

  • Connecting to a self-hosted GitLab instance with custom certificates

  • Accessing internal APIs that use private certificate authorities

  • Integrating with on-premises services using self-signed certificates

This is not for TLS termination of the Cortex API or UI; that should be handled by your load balancer or ingress controller.

Configuration

Before getting started, note the following prerequisites and considerations:

Certificate prerequisites

Prerequisites:

  • Certificates must be in PEM format (Base64 encoded).

    • If you have certificates in other formats, convert them to PEM:

      • DER to PEM: openssl x509 -inform der -in cert.der -out cert.pem

      • P7B to PEM: openssl pkcs7 -print_certs -in cert.p7b -out cert.pem

      • PFX to PEM: openssl pkcs12 -in cert.pfx -out cert.pem -nodes

  • Certificates must include the complete certificate chain if they are using intermediate CAs.

  • Each certificate should begin with -----BEGIN CERTIFICATE----- and end with -----END CERTIFICATE-----

  • Multiple certificates can be concatenated in a single file.

Considerations:

  • After following these instructions, the certificate will be added to the Java trust store in both backend and worker pods.

  • This configuration is only needed for outbound connections from Cortex

  • Be sure the full certificate chain is included, since importing only the leaf certificate can still result in PKIX path building errors when Java services attempt internal HTTPS calls.

  • Inbound TLS/SSL should be configured at the load balancer

Example of creating a Kubernetes secret with concatenated certificates:

Example of PEM formatted certificates:

Follow these steps to add a certificate to the trusted keystores:

  1. Create a kubernetes secret in the same namespace as your Cortex helm installation called tls.cert, containing the contents of the certificate file:

  1. In your overrides.yaml file, enable selfSignedCerts and ensure the secret name is the same as the one created in the last step. In the example below, the secret is called tls-secret:

  1. Repeat the previous step for each service in your overrides.yaml where you want to use the provided certificate.

  2. Preview and apply the updated configuration by following the steps in Modifying the configuration after installing above.

Troubleshoot certificate issues

Troubleshoot SSL/TLS certificate issues

Enabling Redis

Cortex provides optional Redis support by provisioning Redis in Kubernetes as part of the Helm chart. Enable Redis to gain support for outbound rate limiting of integration API calls. Without enabling Redis, Cortex will still function, but may not have rate limiting capabilities for external API calls.

Redis is enabled via a third-party Helm chart; a full reference to all of the possible settings can be found in this repository on GitHub.

Enable basic Redis
  1. Set redis.enabled to true in your overrides.yaml:

  1. Run helm to apply the changes as outlined in .

Note: It will only be accessible from within the Kubernetes cluster, and authentication will not be enabled by default.

Set authentication for Redis

In production environments, it is recommended that you enable authentication.

Configure the Redis subchart to use your secret

If you enable authentication for Redis:

  1. Create a secret for the Redis password:

  1. Open your overrides.yaml. Configure the Redis subchart to use that secret:

  1. Preview and apply the updated configuration by following the steps in Modifying the configuration after installing above.

Embedded Redis upgrades

Embedded Redis upgrade instructions

If you are using embedded Redis, service interruption may occur if you upgrade after August 28, 2025.

To avoid service interruption, you must follow these instructions when you upgrade:

  • If you have set redis.global.imagePullSecrets, ensure that the list of secrets includes the secret that enables you to pull the Cortex images, usually cortex-docker-registry-secret.

    • If you have not set redis.global.imagePullSecrets, no action is required.

  • New Redis images are hosted by Cortex in ghcr.io/cortexapps/helm-chart/. If you have set any image URI options, or mirrored the old official images locally, you will need to update your values to use the new images.

    • Image options include:

      • redis.global.imageRegistry

      • redis.image.registry, redis.image.repository, or redis.image.tag

      • redis.metrics.image.registry, redis.metrics.image.repository, or redis.metrics.image.tag

    • If none of the above are set, no action is required.

  • Ensure that all of the following settings are false:

    • redis.sentinel.enabled

    • redis.volumePermissions.enabled

    • redis.sysctl.enabled

    • redis.kubectl.enabled

These are all false by default, so if you have not set them, no action is required.

If you have questions about these steps or about the upgrade, please reach out to Cortex support.

Monitor Redis usage

You can monitor Redis usage to ensure it's properly sized:

Monitor Redis

Enabling metrics

Prometheus

Prometheus metrics provide visibility into Cortex's performance and health, enabling:

  • Performance monitoring: Track response times, request rates, and error rates

  • Resource utilization: Monitor memory usage, CPU consumption, and thread pools

  • Integration health: Track success or failure rates for external API calls

  • Alerting: Set up alerts for degraded performance or failures

  • Capacity planning: Understand usage patterns to plan scaling

Expand the tile below to learn how to publish Prometheus metrics and configure your Prometheus instance to scrape pods directly.

Publish metrics and configure Prometheus

To publish Prometheus metrics:

  1. Set app.shared.prometheusMetrics.enabled to true in your overrides.yaml:

  1. Preview and apply the updated configuration by following the steps in Modifying the configuration after installing above.

When enabled, Prometheus metrics will be published on port 8181 at path /manage/prometheus (http://localhost:8181/manage/prometheus) for both backend and worker pods.

Prometheus configuration

To scrape metrics from Cortex, configure your Prometheus instance to scrape pods directly, for example:

This configuration uses Kubernetes service discovery to find Cortex pods by their labels and scrapes metrics directly from port 8181.

Access Prometheus metrics

Expand the tile below to see some of the available metrics and how to access them.

Available Prometheus metrics

Application Metrics:

  • http_server_requests_seconds - HTTP request latencies

  • spring_data_repository_invocations_seconds - Database query durations

  • comparatron_operation_seconds - External API call durations

  • scorecard_latest_cache_access_total - Scorecard cache hit/miss rates

  • executor_active_threads - Background job thread activity

  • executor_queued_tasks - Queued background tasks

JVM Metrics:

  • jvm_memory_used_bytes - Memory usage by area

  • jvm_gc_pause_seconds - Garbage collection pause times

  • jvm_threads_live_threads - Active thread count

  • jvm_classes_loaded_classes - Loaded class count

Database Metrics:

  • hikaricp_connections_active - Active database connections

  • hikaricp_connections_pending - Pending connection requests

  • hikaricp_connections_idle - Idle connections in pool

  • hikaricp_connections_timeout_total - Connection timeout count

To check published Prometheus metrics:

Add Prometheus annotations

Enable automatic discovery by Prometheus
  1. Add annotations in overrides.yaml:

  1. Preview and apply the updated configuration by following the steps in Modifying the configuration after installing above.

Example alerting rules

See an example of Prometheus alerting rules for Cortex:

Prometheus alerting rules

Create a Cortex-specific Grafana dashboard

Grafana dashboard

You can create a Grafana dashboard to display the metrics from Prometheus.

Note: The dashboard below is a basic dashboard to get started. You can expand it by adding more panels for:

  • Background job executors (executor_active_threads)

  • Scorecard cache performance (scorecard_latest_cache_access_total)

  • Integration API calls (comparatron_operation_seconds_count)

  • Database query performance (spring_data_repository_invocations_seconds)

Import JSON into Grafana

Save the following as cortex-dashboard.json and import it into Grafana:

Troubleshoot Prometheus metrics issues

Troubleshooting Prometheus metrics

Enabling feature flags

Occasionally, Cortex may ask you to enable a feature flag. To do so, add settings to the app.shared.featureFlags map in your overrides.yaml:

Preview and apply the updated configuration by following the steps in Modifying the configuration after installing above.

Getting Help

When working with Cortex Customer Support, you will be asked to collect diagnostic data using a tool we distribute called brain-freeze. To install brain-freeze, follow this link to the latest release, and download the .tar.gz file that matches the operating system and architecture where you are running kubectl. Extract the file on the machine where you are running kubectl, and put the enclosed brain-freeze binary somewhere in your path. Then you can run:

This will create a data subdirectory in your current directory, containing the Kubernetes logs from your deployment from the last 24 hours (1440 minutes) and the details of your Kubernetes deployment configuration.

The brain-freeze CLI does not dump sensitive information. The values of secrets are masked.

Product analytics

Cortex collects basic, anonymized data from self-managed customers. If you would like to opt out, please reach out to your Cortex Customer Success Manager.

Last updated

Was this helpful?