The most recent patch for this version is 26.1.3.  Learn more  

Skip to main content

Deployment

Before deploying the Authorization Hub, ensure the following prerequisites are met:

  • KubernetesOpens in a new tab

    A running K8s cluster (version 1.25 recommended) is required for deployment. This can be a local Minikube cluster for testing or a cloud cluster for production.

  • HelmOpens in a new tab

    Helm version 3.8 must be installed locally to manage the charts.

  • kubectlOpens in a new tab

    Installed and configured to access your K8s cluster.

  • PostgreSQLOpens in a new tab (optional)

    An external Postgres instance. If you don't have one, a local Postgres pod can be provisioned, as described in the Deployment section.

  • Sufficient resources

    A minimum of 4 CPUs and 8GB of RAM is required for local clusters running the full microservices stack. The ADM pod defaults to a 2GB memory limit, which supports ALFA policies up to approximately 8MB. If your policy files exceed this size, increase the ADM pod memory limit and ensure the cluster has sufficient memory.

  • Authorization Hub license

    Contact the Axiomatics Customer SupportOpens in a new tab to obtain a license file (axiomatics_HUB.license) for your deployment.

Preparation

The Authorization Hub is downloaded using the AWS CLI. If you have not already done so, install AWS CLI following the instructions in AWS documentation.

  1. Configure the Axiomatics AWS CLI account using the aws configure command, as explained in the AWS documentation. This procedure requires an Access key ID and a Secret access key, which will have been provided to you by Axiomatics.

  2. Download the Authorization Hub distribution.

    aws s3api get-object --bucket axiomatics-customer-artifacts --key releases/com/axiomatics/hub/26.1.3/hub-26.1.3.zip hub-26.1.3.zip
  3. Log in to the Amazon ECR registry provided by Axiomatics.

    aws ecr get-login-password --region eu-central-1 | docker login --username AWS --password-stdin 748131003707.dkr.ecr.eu-central-1.amazonaws.com

Deployment

warning

Replace all $CHANGE_ME placeholders in the commands below with your actual values before running them.

  1. Copy axiomatics_HUB.license into the /configurations directory.

  2. Create a secret to pull the images from the ECR registry.

    kubectl create secret docker-registry regcred \
    --docker-server=748131003707.dkr.ecr.eu-central-1.amazonaws.com \
    --docker-username=AWS --docker-password=$(aws ecr get-login-password --region eu-central-1)
  3. Create a secret containing values for the Keycloak pod.

    kubectl create secret generic keycloak \
    --from-literal=hub_client_secret=$CHANGE_ME \
    --from-literal=principal_client_secret=$CHANGE_ME
  4. (Optional) Configure SMTP to enable email delivery.

    1. Enable SMTP in values.yaml by setting the following keys to true:

      services:
      keycloak:
      smtp:
      enabled: true
      smtpAuth: true
      smtpStartTlsEnabled: true
      smtpStartTlsRequired: true

      services:
      hubService:
      smtp:
      enabled: true
    2. Create a secret containing the SMTP credentials:

      kubectl create secret generic smtp \
      --from-literal=host=$CHANGE_ME \
      --from-literal=port=587 \
      --from-literal=user=$CHANGE_ME \
      --from-literal=password=$CHANGE_ME \
      --from-literal=from=$CHANGE_ME
    3. Optionally, for local testing, enable emails using a test Gmail SMTP (smtp.gmail.com) app through https://myaccount.google.com/apppasswords.Opens in a new tab

      note

      We recommend using port 587 but you may configure an alternative port based on your requirements.

  5. Create a secret with the PostgreSQL database credentials.

    kubectl create secret generic db-connection \
    --from-literal=dbusername=$CHANGE_ME \
    --from-literal=dbpassword=$CHANGE_ME \
    --from-literal=dbhost=postgres \
    --from-literal=dbport=5432
  6. Create a secret with the Tenant admin credentials. Authorization Hub bootstraps this user on first startup.

    warning

    The password must comply with the Password policy.

    kubectl create secret generic principal \
    --from-literal=tenant_admin_email=$CHANGE_ME \
    --from-literal=tenant_admin_password=$CHANGE_ME
  7. Navigate to kubernetes/charts and install the Helm chart.

    helm install hub \
    -f hub/values.yaml hub \
    --set registry=748131003707.dkr.ecr.eu-central-1.amazonaws.com/axiomatics/ \
    --set hubHostname=$CHANGE_ME \
    --set 'imagePullSecrets[0].name=regcred'

    If you don't have an external database, add -f hub/values-local-database.yaml after the second line to deploy a PostgreSQL pod automatically.

Local testing deployment

warning

Replace all $CHANGE_ME placeholders in the commands below with your actual values before running them.

For testing purposes only, you can run a deployment locally using minikube. First step is to start minikube.

minikube start --memory=8000 --cpus=4

Follow the steps in the Deployment section above, but skip the Helm chart installation. Then, proceed with the following steps.

Enable TLS (optional)

note

This section is optional. TLS is required when running Authorization Hub on a hostname other than localhost. If you are using localhost, skip this section as the HTTP configuration is sufficient.

To enable TLS, an Ingress controller is needed to terminate HTTPS before traffic reaches the Authorization Hub services. The following steps use the NGINX Ingress controller provided by Minikube.

  1. While Minikube is running, enable the NGINX Ingress controller.

    minikube addons enable ingress
  2. Generate a self-signed TLS certificate for your hostname.

    openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:P-256 \
    -days 3650 -nodes \
    -keyout hub.key -out hub.crt \
    -subj "/CN=$CHANGE_ME" \
    -addext "subjectAltName=DNS:$CHANGE_ME"
  3. Create a Kubernetes secret containing the TLS certificate.

    kubectl create secret tls hub-ingress-tls \
    --key hub.key \
    --cert hub.crt
  4. Make your hostname resolvable from the browser.

    *:first-child]:mt-0>

    Add the hostname to /etc/hosts pointing to the Minikube node IP.

    echo "$(minikube ip) $CHANGE_ME" | sudo tee -a /etc/hosts
    *:first-child]:mt-0 hidden>

    Add your hostname to the Windows hosts file pointing to localhost.

    1. Open C:\Windows\System32\drivers\etc\hosts as Administrator and add:

      127.0.0.1 $CHANGE_ME
    2. Patch CoreDNS so that Authorization Hub services can resolve the hostname inside the cluster.

      1. Get the NGINX ingress ClusterIP.

        kubectl get svc ingress-nginx-controller -n ingress-nginx
      2. Edit the CoreDNS ConfigMap.

        kubectl edit configmap coredns -n kube-system
      3. Find the existing hosts block and add your entry:

        hosts {
        192.168.49.1 host.minikube.internal
        <CLUSTER-IP> $CHANGE_ME
        fallthrough
        }

        Replace <CLUSTER-IP> with the value from the previous command.

      4. Save and restart CoreDNS.

        kubectl rollout restart deployment coredns -n kube-system

Helm chart installation adjustments

Select the option that matches your setup: accessing Authorization Hub through localhost without TLS, or accessing it through a non-localhost hostname with TLS.

*:first-child]:mt-0>
  1. Navigate to kubernetes/charts and install the Helm chart.

    helm install hub \
    -f hub/values.yaml hub \
    --set registry=748131003707.dkr.ecr.eu-central-1.amazonaws.com/axiomatics/ \
    --set hubHostname=localhost \
    --set hubProtocol=http \
    --set services.apiGateway.service.type=LoadBalancer \
    --set 'imagePullSecrets[0].name=regcred'
  2. Start the minikube tunnel in a separate terminal window and keep it running:

    minikube tunnel

    Authorization Hub is now available at http://localhost.

*:first-child]:mt-0 hidden>

Navigate to kubernetes/charts and install the Helm chart.

helm install hub \
-f hub/values.yaml \
-f hub/values-local-tls.yaml hub \
--set registry=748131003707.dkr.ecr.eu-central-1.amazonaws.com/axiomatics/ \
--set hubHostname=$CHANGE_ME \
--set hubProtocol=https \
--set 'imagePullSecrets[0].name=regcred'
note

values-local-tls.yaml enables the NGINX ingress and scopes trustedProxies to the minikube pod CIDR (10.244.0.0/16 by default). This is required because the NGINX Ingress addon's hostNetwork mode can cause iptables SNAT to inject pod network IPs into forwarding headers, triggering false session-binding mismatches. If you configured a custom --pod-network-cidr, update trustedProxies in values-local-tls.yaml accordingly. You can verify your pod CIDR with:

kubectl get nodes -o jsonpath='{.items[*].spec.podCIDR}'
*:first-child]:mt-0>

The NGINX ingress controller is directly accessible at the Minikube node IP — no tunnel is required. Authorization Hub is available at the hostname you configured in the previous steps.

*:first-child]:mt-0 hidden>

The Minikube node IP is not directly accessible from the host. Run the following command in a separate Administrator terminal and keep it running:

kubectl port-forward -n ingress-nginx svc/ingress-nginx-controller 443:443

Authorization Hub is available at the hostname you configured in the previous steps.

info

Your browser will show a warning for the self-signed certificate. This is expected. Accept the warning to continue.

Cloud deployment

This guide covers deploying Authorization Hub on any managed Kubernetes platform, such as EKS, GKE, AKS, OpenShift, and others. Each step describes what is required, followed by an AWS CLI example you can adapt to your provider.

note

The AWS examples are CLI-based reference commands. In your environment, you can use Infrastructure as Code (Terraform, CloudFormation), manual steps in the cloud console, or any equivalent method.

Requirements

RequirementDetails
Managed Kubernetesversion ≥ 1.25
Helm≥ 3.10
kubectlconfigured against the target cluster
Managed PostgreSQLversion ≥ 17, SSL/TLS enforced, encrypted storage, automated backups
DNS zoneyou control
TLS certificatematching the Hub hostname
Container registry accesscredentials for the Authorization Hub image registry
License fileaxiomatics_HUB.license provided by Axiomatics

Variables

Export the following variables to your shell before starting as they are referenced throughout this guide.

export CLUSTER_NAME=hub-test
export DB_PASSWORD="$(openssl rand -base64 24 | tr -d '=+/')"
export TENANT_ADMIN_EMAIL="<your-email@example.com>"
export LICENSE_PATH="<path-to>/axiomatics_HUB.license"

# Filled in before Steps 5 and 8
export HUB_HOST=hub.yourdomain.com
export HOSTED_ZONE_ID=<Zxxxxxxxxxxxxx>

Step 1: Managed Kubernetes cluster

Set up a managed Kubernetes cluster with the following minimum requirements:

  • Version: Kubernetes ≥ 1.25
  • Node capacity: ≥ 2 worker nodes totaling ~16 GiB RAM / 4 vCPU
  • Networking: nodes in private subnets with outbound internet access via NAT for image pulls
  • RBAC: enabled (default on all managed offerings)
  • OIDC provider: enabled (required for workload identity (IRSA, GKE Workload Identity, AKS Managed Identity))
  • Autoscaling: node-group autoscaling recommended (min 2, max ≥ 4)
  • CSI driver: a dynamic block-storage CSI driver installed (EBS, PD, or Azure Disk)

Managed Kubernetes is available from AWS (EKS), GCP (GKE), and Azure (AKS).

AWS example

cat > /tmp/eks-hub.yaml <<EOF
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata:
name: ${CLUSTER_NAME}
region: ${AWS_REGION}
version: "1.30"
iam:
withOIDC: true
managedNodeGroups:
- name: ng-default
instanceType: t3.large
desiredCapacity: 2
minSize: 2
maxSize: 4
volumeSize: 30
privateNetworking: true
addons:
- name: vpc-cni
- name: coredns
- name: kube-proxy
- name: aws-ebs-csi-driver
EOF

eksctl create cluster -f /tmp/eks-hub.yaml
aws eks update-kubeconfig --name "$CLUSTER_NAME" --region "$AWS_REGION"

EKS cluster creation using eksctl (takes approximately 15 minutes).

Step 2: Managed PostgreSQL database

Set up a managed PostgreSQL instance with the following requirements:

  • Version: PostgreSQL ≥ 17
  • Network: reachable only from the cluster's private network - no public endpoint
  • Encryption: at rest (storage encryption) and in transit (SSL/TLS enforced)
  • Sizing: ≥ 2 vCPU / 4 GiB RAM for initial production; tune based on load
  • Backups: automated daily backups, ≥ 7-day retention, point-in-time recovery enabled
  • High availability: multi-AZ or equivalent recommended for production
  • User: a single DB user with ownership of all three Authorization Hub databases (hub, keycloak, domain_manager)

Managed PostgreSQL is available from AWS (Amazon RDS), GCP (Cloud SQL), and Azure (Azure Database for PostgreSQL – Flexible Server).

warning

Do not use the in-cluster Postgres pod from values-local-database.yaml for anything beyond local development or PoCs.

AWS example

VPC_ID=$(aws eks describe-cluster --name "$CLUSTER_NAME" \
--query "cluster.resourcesVpcConfig.vpcId" --output text)

NODE_SG=$(aws eks describe-cluster --name "$CLUSTER_NAME" \
--query "cluster.resourcesVpcConfig.clusterSecurityGroupId" --output text)

# Private subnets tagged by eksctl for internal load balancers
SUBNET_ARR=( $(aws ec2 describe-subnets \
--filters "Name=vpc-id,Values=$VPC_ID" \
"Name=tag:kubernetes.io/role/internal-elb,Values=1" \
--query "Subnets[].SubnetId" --output text) )
echo "count=${#SUBNET_ARR[@]} subnets=${SUBNET_ARR[@]}"

aws rds create-db-subnet-group \
--db-subnet-group-name hub-subnets \
--db-subnet-group-description "hub private subnets" \
--subnet-ids "${SUBNET_ARR[@]}"

DB_SG=$(aws ec2 create-security-group --group-name hub-rds \
--description "RDS for hub" --vpc-id "$VPC_ID" \
--query GroupId --output text)

aws ec2 authorize-security-group-ingress --group-id "$DB_SG" \
--protocol tcp --port 5432 --source-group "$NODE_SG"

aws rds create-db-instance \
--db-instance-identifier hub \
--db-instance-class db.t3.small \
--engine postgres --engine-version 17.6 \
--master-username hubadmin \
--master-user-password "$DB_PASSWORD" \
--allocated-storage 20 \
--db-subnet-group-name hub-subnets \
--vpc-security-group-ids "$DB_SG" \
--backup-retention-period 7 \
--storage-encrypted \
--no-publicly-accessible

aws rds wait db-instance-available --db-instance-identifier hub

DB_HOST=$(aws rds describe-db-instances --db-instance-identifier hub \
--query "DBInstances[0].Endpoint.Address" --output text)
echo "DB_HOST=$DB_HOST"

RDS PostgreSQL instance creation in private subnets, accessible only from the cluster's security group.

Step 3: Create the databases

Create the following three empty databases on the PostgreSQL instance from step 2 above. All three must be owned by the DB user referenced in the db-connection secret that will be created in step 7 below.

  • hub: application state for the Authorization Hub service
  • keycloak: identity-provider state (realms, users, clients, sessions)
  • domain_manager: ABAC policy storage for ADM
CREATE DATABASE hub;
CREATE DATABASE keycloak;
CREATE DATABASE domain_manager;

AWS example

kubectl run pgclient --rm -it --restart=Never --image=postgres:17 -- \
bash -c "PGPASSWORD='$DB_PASSWORD' psql -h $DB_HOST -U hubadmin -d postgres \
-c 'CREATE DATABASE hub;' \
-c 'CREATE DATABASE keycloak;' \
-c 'CREATE DATABASE domain_manager;'"

Temporary psql pod to create the databases. No bastion host or public database endpoint is required.

Step 4: Ingress controller

Deploy an ingress controller that meets the following requirements:

  • Provisions a cloud L7 load balancer (public-facing or internal, based on your exposure model)
  • Terminates TLS using the certificate from step 5 below
  • Routes all external traffic to the api-gateway Kubernetes service on port 80
  • Forwards the X-Forwarded-For, X-Forwarded-Proto, and X-Forwarded-Host headers (Keycloak relies on these to construct issuer URLs)
  • Supports HTTP health checks against /actuator/health
  • Has permissions to create and modify load balancer resources in your cloud

Common options are the AWS Load Balancer Controller for EKS, GKE built-in Ingress or NGINX for GKE, and AGIC or NGINX Ingress Controller for AKS. For on-premises or portable deployments, use the NGINX Ingress Controller with cert-manager. When available, prefer the cloud-native controller as it integrates directly with the provider's certificate and DNS services.

AWS example

curl -fsSL -o /tmp/lbc-iam-policy.json \
"https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/v2.13.4/docs/install/iam_policy.json"

ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
POLICY_NAME=AWSLoadBalancerControllerIAMPolicy-${CLUSTER_NAME}
POLICY_ARN="arn:aws:iam::${ACCOUNT_ID}:policy/${POLICY_NAME}"

aws iam create-policy \
--policy-name "$POLICY_NAME" \
--policy-document file:///tmp/lbc-iam-policy.json
echo "POLICY_ARN=$POLICY_ARN"

eksctl create iamserviceaccount \
--cluster "$CLUSTER_NAME" --region "$AWS_REGION" \
--namespace kube-system --name aws-load-balancer-controller \
--attach-policy-arn "$POLICY_ARN" \
--override-existing-serviceaccounts --approve

helm repo add eks https://aws.github.io/eks-charts
helm repo update
helm install aws-load-balancer-controller eks/aws-load-balancer-controller \
-n kube-system --version 1.13.4 \
--set clusterName="$CLUSTER_NAME" \
--set serviceAccount.create=false \
--set serviceAccount.name=aws-load-balancer-controller \
--set region="$AWS_REGION" \
--set vpcId="$VPC_ID"

kubectl -n kube-system rollout status deploy/aws-load-balancer-controller

# Ensure public subnets are tagged so the ALB controller can place the LB
for s in $(aws ec2 describe-subnets --filters "Name=vpc-id,Values=$VPC_ID" \
--query "Subnets[?MapPublicIpOnLaunch==\`true\`].SubnetId" --output text); do
aws ec2 create-tags --resources "$s" --tags Key=kubernetes.io/role/elb,Value=1
done

AWS Load Balancer Controller installation using IRSA.

Step 5: TLS certificate

Obtain a TLS certificate for $HUB_HOST before proceeding. The certificate must:

  • match the Hub hostname exactly (CN or SAN)
  • be issued by a trusted public or internal CA
  • support automated renewal as manual renewal is an operational risk for a long-lived service like Authorization Hub

Options for obtaining a certificate:

  • Cloud-provider managed certificate: Renewal is automatic and no in-cluster management is required (AWS ACM, GCP Managed Certificates, Azure App Gateway certificates).
  • cert-manager + Let's Encrypt: Portable across any cluster with HTTP-01 or DNS-01 validation. Use this when a cloud-managed option is not available.

AWS example

CERT_ARN=$(aws acm request-certificate \
--domain-name "$HUB_HOST" \
--validation-method DNS \
--region "$AWS_REGION" \
--query CertificateArn --output text)
echo "CERT_ARN=$CERT_ARN"

read -r NAME TYPE VALUE <<< $(aws acm describe-certificate --certificate-arn "$CERT_ARN" \
--query "Certificate.DomainValidationOptions[0].ResourceRecord.[Name,Type,Value]" --output text)

aws route53 change-resource-record-sets --hosted-zone-id "$HOSTED_ZONE_ID" \
--change-batch "{\"Changes\":[{\"Action\":\"UPSERT\",\"ResourceRecordSet\":{\"Name\":\"$NAME\",\"Type\":\"$TYPE\",\"TTL\":60,\"ResourceRecords\":[{\"Value\":\"$VALUE\"}]}}]}"

aws acm wait certificate-validated --certificate-arn "$CERT_ARN" --region "$AWS_REGION"

ACM certificate request and validation using a Route 53 DNS record.

note

ACM cannot issue certificates for AWS-owned hostnames (*.elb.amazonaws.com). You must own the domain.

Step 6: SMTP service (optional)

Configure an SMTP relay reachable from the cluster for Keycloak to use for password resets, user invitations, and email verification. This step is optional and you can deploy without SMTP, but email-driven flows will not work until it is configured.

Collect the following values before proceeding. They will be stored in the smtp secret in step 7 below.

  • Host and port (commonly 587 for STARTTLS)
  • Username and password (or API key equivalent)
  • Sender address (typically no-reply@yourdomain.com - must be a verified identity on the relay)

STARTTLS on port 587 is recommended. Plain port 25 is not accepted by most managed cluster outbound policies.

Common relay options are Amazon SES (AWS), SendGrid or Mailgun (GCP), and Azure Communication Services (Azure).

Step 7: Kubernetes secrets and license

Create the Kubernetes secrets in the install namespace and copy the license file into the chart's configurations/ directory.

tip

For production environments, consider managing secrets through an external secret store (AWS Secrets Manager, GCP Secret Manager, Azure Key Vault) using the External Secrets Operator rather than raw kubectl create secret commands.

For the exact field names and commands, see the Deployment section above.

Step 8: Helm install

Install the Authorization Hub Helm chart, overriding the following values for your deployment:

  • registry: your container registry hostname and path, with a trailing slash
  • hubHostname: the DNS name users will access (must match the TLS certificate)
  • imagePullSecrets: list referencing regcred by name
  • ingress.enabled: true and ingress.className: matching the ingress controller from step 4
  • ingress.annotations: load balancer annotations (certificate reference, health-check path, SSL policy, and so on)
  • ingress.hosts[0].host: same value as hubHostname

Use -f - to pass values through stdin to keep deployment-specific configuration out of the chart directory and avoid conflicts during future upgrades.

AWS example

cd src/main/helm

helm install hub \
-f hub/values.yaml \
-f - \
hub/ <<EOF
hubHostname: "${HUB_HOST}"
registry: "748131003707.dkr.ecr.eu-central-1.amazonaws.com/axiomatics/"
imagePullSecrets:
- name: regcred
ingress:
enabled: true
className: "alb"
annotations:
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: ip
alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}]'
alb.ingress.kubernetes.io/ssl-redirect: "443"
alb.ingress.kubernetes.io/certificate-arn: "${CERT_ARN}"
alb.ingress.kubernetes.io/target-group-attributes: stickiness.enabled=true
alb.ingress.kubernetes.io/ssl-policy: "ELBSecurityPolicy-TLS13-1-2-2021-06"
alb.ingress.kubernetes.io/healthcheck-path: /actuator/health
alb.ingress.kubernetes.io/healthcheck-protocol: HTTP
alb.ingress.kubernetes.io/backend-protocol: HTTP
alb.ingress.kubernetes.io/group.name: hub
hosts:
- host: "${HUB_HOST}"
paths:
- path: /
pathType: Prefix
tls: []
EOF

Chart installation with an ALB ingress, ACM certificate, and HTTPS-only listener with sticky sessions.

Step 9: DNS to load balancer

Create a DNS record for $HUB_HOST pointing to the load balancer provisioned in step 8. Retrieve the load balancer hostname from the Ingress status.

kubectl get ingress hub -o jsonpath='{.status.loadBalancer.ingress[0].hostname}'

AWS example

ALB=$(kubectl get ingress hub \
-o jsonpath='{.status.loadBalancer.ingress[0].hostname}')
echo "ALB=$ALB"

ALB_HZID=$(aws elbv2 describe-load-balancers \
--query "LoadBalancers[?DNSName=='$ALB'].CanonicalHostedZoneId" --output text)

aws route53 change-resource-record-sets --hosted-zone-id "$HOSTED_ZONE_ID" \
--change-batch "{\"Changes\":[{\"Action\":\"UPSERT\",\"ResourceRecordSet\":{\"Name\":\"$HUB_HOST\",\"Type\":\"A\",\"AliasTarget\":{\"HostedZoneId\":\"$ALB_HZID\",\"DNSName\":\"$ALB\",\"EvaluateTargetHealth\":false}}}]}"

A-ALIAS record creation in Route 53 pointing to the ALB.

Next step

Once the Authorization Hub is deployed, you should configure user authentication before you begin inviting users. Read the User authentication section for details.