Anjana Deployment
Kit architecture
The anjana-k8s kit is a root Helm chart (anjana-platform) with three subcharts:
-
anjana-core: 12 microservices + 2 frontends (horus, portuno, zeus, kerno, minerva, viator, hermes, tot, drittesta, anjana-ui, portuno-ui, marketplace, adp-mcp)
-
anjana-persistences: in-cluster persistences (PostgreSQL, MongoDB, RabbitMQ, Valkey, Solr, SeaweedFS), each independently enabled/disabled. Disabled by default in cloud (managed services are used instead: RDS, DocumentDB, ElastiCache, Amazon MQ).
-
anjana-plugins: 20 connectors, all disabled by default
Horus acts as a centralized Config Server: all microservices query it at startup to obtain their configuration (connection strings to MongoDB, RabbitMQ, Solr, S3, license, etc.).
Requirements
Required connectivity
The kit will download all necessary resources from the Anjana Data central repository over HTTPS protocol and port 443 against the server dr-releases.anjanadata.com, so connectivity from the servers/cluster to be provisioned to that service will be required.
Access and credentials must be requested from the Anjana Data team.
Prerequisites on the deployment machine
-
kubectl: connected to the target cluster (kubeconfig configured)
-
helm >= 3.12: Kubernetes chart manager
-
anjana CLI: credential management tool (
anjanain PATH) -
cert-manager (>= 1.14): required only for TLS
internalmode. Not included in the kit: the client must have it previously installed in the cluster. If cert-manager is not available, use TLSmanualmode.
Cluster resource requirements (sizing)
The kit sizes the microservices according to the active environment profile (global.environment.profile in the values file): DEV (lab/local), PRE (pre-production/UAT, default value), or PRO (production). The profile sets the CPU and memory requests and limits for each service; the per-service detail can be checked in charts/anjana-core/values.yaml and adjusted individually if needed (see k8s Advanced, "Environment profiles").
Minimum guideline capacity that the cluster must reserve for the Anjana microservices (includes the 2 horus replicas):
|
Profile |
CPU (requests) |
RAM (requests) |
RAM (limits) |
|---|---|---|---|
|
DEV |
~2 vCPU |
~8 Gi |
~20 Gi |
|
PRE |
~2.5 vCPU |
~12.5 Gi |
~22 Gi |
|
PRO |
~4 vCPU |
~16.5 Gi |
~25.5 Gi |
If the in-cluster persistences are enabled (anjana-persistences.enabled: true), additionally add:
|
Persistence |
RAM (requests / limits) |
Default storage |
|---|---|---|
|
PostgreSQL |
512Mi / 1Gi |
10Gi |
|
MongoDB |
512Mi / 1Gi |
10Gi |
|
RabbitMQ |
512Mi / 1Gi |
5Gi |
|
Valkey |
256Mi / 512Mi |
5Gi |
|
Solr |
4Gi / 5Gi |
15Gi (10Gi data + 5Gi configuration) |
|
SeaweedFS |
512Mi / 1Gi |
20Gi |
With all persistences enabled: approximately 6.5 Gi of additional RAM in requests and 65 Gi of storage.
NOTE: these figures cover only the Anjana platform. The cluster must also have capacity available for Kubernetes' own components and any other deployed workload.
Required adjustments
IMPORTANT: Since 26.k1 a TLS certificate is required for secure communication between microservices and access to the frontends. In internal mode (default), cert-manager generates and renews the certificate automatically. In manual mode, it is necessary to export the certificate paths before running anjana setup (see advanced guide).
The kit comes preconfigured with recommended settings. You will need to prepare the credentials bundle with the connection strings and license for your environment by following the three steps below.
Installation guardrails
The chart validates at render time that the prerequisites for the chosen configuration exist (for example the ClusterIssuer and the Secrets for TLS internal mode, or that exactly one Config Server backend is enabled). The behavior is controlled with global.guardrails.mode in the values file:
-
strict(default value):helm install/helm upgradefails with a descriptive message if any prerequisite is missing. ✅RECOMMENDED in production. -
permissive: only emits warnings and continues. Useful in a lab or when validating values locally withmake test.
IMPORTANT: if helm install fails with a guardrails error, the message indicates exactly which prerequisite is missing. Fix it before retrying; do not switch to permissive to bypass it in production environments.
Infrastructure Secrets
The kit requires three Kubernetes Secrets created before the helm install:
|
Secret |
Content |
Created by |
|---|---|---|
|
|
Docker Registry credentials |
|
|
|
TLS: PKCS12 keystore, cacerts, PEM certs, passwords |
|
|
|
All platform credentials: DB, MongoDB, RabbitMQ, Valkey, S3, license, domain |
|
Anjana Installation - 3 steps
Step 1 - Prepare the credentials bundle
The kit uses an encrypted credentials bundle (secrets/anjana.env.aes) as the single source of truth for all platform secrets: connection strings, licenses, persistence credentials, and service configuration.
Copy the template included in the kit, fill in all the values, and generate the encrypted bundle:
cp anjana.env.example horus.env
vi horus.env # fill in with the environment's values
anjana horus update --from-env horus.env # generates secrets/anjana.env.aes
Or alternatively, from a JSON payload (CI/automation):
anjana horus update --from-json payload.json
NOTE: The secrets/anjana.env.aes file is encrypted with AES and can be safely versioned in Git. The plain text (horus.env) must never be uploaded to the repository.
Step 2 - Create namespace and Kubernetes Secrets
The anjana setup command creates the namespace, the pull secrets, and decrypts the AES bundle to create the necessary Secrets in the cluster:
anjana setup [namespace] # default: anjana-data
This step creates the following resources in Kubernetes:
-
anjanadr: docker-registry pull secret to download Anjana Data images -
anjana-keystore: TLS material: PKCS12 keystore, Java truststore, passwords -
anjana-secrets: platform credentials decrypted from the AES bundle
To provide your own certificates (bring-your-own):
export ANJANA_KEYSTORE_P12=/path/to/anjana-ssl.keystore.p12
export ANJANA_CACERTS=/path/to/cacerts
export ANJANA_FULLCHAIN_PEM=/path/to/fullchain.pem
export ANJANA_PRIVKEY_PEM=/path/to/privkey.pem
anjana setup [namespace]
Step 3 - Install the Helm chart
Once the Secrets have been created, install the complete platform with Helm:
helm dependency build
helm install anjana-platform . -f values-<env>.yaml -n anjana-data
The post-install hook config-init-job automatically fills portuno.app_configuration from anjana-secrets, making all the service configuration available to Horus Config Server.
RECOMMENDED: Once the installation is complete, the Anjana Data frontends can be accessed through the configured domain:
-
https://<dominio>/: Anjana UI Frontend -
https://<dominio>/configpanel: Portuno UI administration panel
Both frontends listen on the same port 443: anjana-ui and portuno-ui are two independent nginx frontends, separated by path (/ vs /configpanel), not by port. If you need to expose them behind your own on-prem Ingress/LoadBalancer, the separation is done the same way: two path rules pointing to the same port 443 on each Service. See examples/ingress-onprem-traefik/ in the kit repo for a working example with Traefik.
Horus configuration modes
|
Mode |
When to use |
Requirement |
|---|---|---|
|
AWS Secrets Manager |
Deployments on AWS EKS |
IRSA: ServiceAccount with the |
|
Git |
Any environment with access to a Git repository |
Private SSH key in a K8s Secret |
|
JDBC (no cloud backend) |
On-premises, air-gapped environments, no cloud |
|
In all modes, JDBC is always active: horus reads from
portuno.app_configurationin addition to the selected cloud backend.
AWS Secrets Manager mode (AWS production)
This is the preconfigured mode in values.yaml for AWS EKS environments.
Prerequisites:
-
ServiceAccount with IRSA created in the namespace before the
helm install:
kubectl -n <namespace> create serviceaccount horus-dev-sm-sa
kubectl -n <namespace> annotate serviceaccount horus-dev-sm-sa \
eks.amazonaws.com/role-arn=arn:aws:iam::<AccountID>:role/<RoleName>
-
Platform secrets loaded into AWS Secrets Manager under the prefix configured in
global.horus.aws.prefix(default/secret/dev).
Configuration in values.yaml:
global:
horus:
aws:
region: "eu-central-1"
prefix: "/secret/dev"
serviceAccountName: "horus-dev-sm-sa"
iamRoleArn: "arn:aws:iam::<AccountID>:role/<RoleName>"
config:
cloud: true
secrets_backend:
aws_secrets_manager:
enabled: true
config_server:
aws_secrets_manager:
enabled: true
In this mode anjana-secrets is not necessary (Horus reads from AWS SM via IRSA) and anjana-persistences must be disabled (use managed services: RDS, DocumentDB, ElastiCache, Amazon MQ).
JDBC mode (no cloud backend)
In environments without access to AWS / Azure / GCP, Horus operates in purely JDBC mode: it reads all the configuration from the portuno.app_configuration table in PostgreSQL, which the anjana-config-init Job automatically populates during the helm install.
Step 1 - Prepare credentials
Follow Step 1 of the Anjana Installation - 3 steps section. The keys expected in horus.env are documented in anjana.env.example included in the kit.
Group 1 - persistences.* (used by the config-init job and the init containers of the persistence StatefulSets; not inserted into portuno.app_configuration):
persistences.bbdd.url host:port/db (e.g.: postgresql:5432/anjana)
persistences.bbdd.user PostgreSQL user
persistences.bbdd.pass PostgreSQL password
persistences.mongodb.user MongoDB user
persistences.mongodb.pass MongoDB password
persistences.rabbitmq.user RabbitMQ user
persistences.rabbitmq.pass RabbitMQ password
persistences.valkey.pass Valkey password
persistences.s3.access_key S3/SeaweedFS access key
persistences.s3.secret_key S3/SeaweedFS secret key
Group 2 - UPPERCASE keys (inserted into portuno.app_configuration and served by Horus to all microservices):
DB_URL PostgreSQL host:port (e.g.: postgresql.<namespace>.svc.cluster.local:5432)
DB_USER / DB_PASS PostgreSQL credentials
MONGO_URI mongodb://user:pass@host:27017/db?authSource=admin
RABBITMQ_HOST / RABBITMQ_PORT / RABBITMQ_USER / RABBITMQ_PASS / RABBITMQ_SSL
VALKEY_HOST Valkey host (e.g.: valkey.<namespace>.svc.cluster.local)
VALKEY_PORT Valkey port (default: 6379)
VALKEY_PASS Valkey password
VALKEY_SSL true (TLS mandatory in in-cluster deployment)
INDEX_URL host:port/solr (e.g.: solr:8983/solr)
S3_TYPE / S3_URL / S3_REGION / S3_ACCESS_KEY / S3_SECRET_KEY / S3_BUCKET_*
LICENSE_INSTALLATION_CODE / LICENSE_PRIVATE_KEY / LICENSE_PUBLIC_KEY
anjana.domain / anjana.horusAdmin.user / anjana.horusAdmin.pass
The
persistences.*keys are consumed directly by the config-init job and the persistence init containers. They are not inserted intoportuno.app_configuration.
Step 2 - Run anjana setup
anjana setup <namespace>
Creates anjana-secrets from the encrypted secrets/anjana.env.aes bundle.
Step 3 - Configure values.yaml
global:
horus:
config:
cloud: false
envSecretName: "anjana-secrets"
secrets_backend:
aws_secrets_manager:
enabled: false
config_server:
aws_secrets_manager:
enabled: false
git:
enabled: false
Step 4 - Install the chart
helm install anjana-platform . -f values.yaml -n <namespace> --wait
Behavior of the anjana-config-init Job
The Job is only generated when:
-
global.horus.config.envSecretNameis defined (not empty) -
global.horus.config_server.aws_secrets_manager.enabled: false -
global.horus.config.secrets_backend.aws_secrets_manager.enabled: false
The Job performs the following operations:
-
Waits until PostgreSQL is available (
pg_isready) -
Creates the application schemas (
anjana,hermes,minerva,portuno,tot,zeus) if they do not exist; idempotent operation -
Creates the
portuno.app_configurationtable and its sequence if they do not exist -
For each key in the Secret:
-
Skips keys with the
persistences.prefix and internal Job variables (PGHOST,PGUSER,PGPASSWORD,PGDATABASE) -
Runs
INSERT ... WHERE NOT EXISTS(idempotent: does not overwrite existing values)
-
This behavior ensures that successive upgrades do not destroy configuration manually customized in the database.
⚠️ Order of operations with backup restoration or sample data
The microservices use optional:configserver: when connecting to Horus. If Horus has no configuration available at startup time (empty table), the service starts with Spring Boot default values and does not automatically reload the configuration once it becomes available.
Recommended order:
-
Restore the backup or load the sample data into PostgreSQL
-
Run
helm install/helm upgrade
If the deployment was carried out before loading the data:
kubectl rollout restart deployment -n <namespace>
kubectl rollout restart statefulset/horus -n <namespace>
Git mode
Allows serving configuration from a Git repository.
global:
horus:
config_server:
aws_secrets_manager:
enabled: false
git:
enabled: true
uri: "git@github.com:<org>/<repo>.git"
defaultLabel: "main"
searchPaths: "{application}"
privateKeySecret:
name: "horus-git-key"
key: "privateKey"
Create the Secret with the SSH key:
kubectl -n <namespace> create secret generic horus-git-key \
--from-file=privateKey=~/.ssh/id_rsa \
--dry-run=client -o yaml | kubectl apply -f -
In-cluster persistences (anjana-persistences)
For on-premise or development/test environments, the kit includes the anjana-persistences subchart with the following persistences, all independently enabled/disabled:
|
Persistence |
Port |
Default image |
|---|---|---|
|
PostgreSQL |
5432 |
postgres:16 |
|
MongoDB |
27017 |
mongo:7 |
|
RabbitMQ |
5672 / 15672 |
rabbitmq:3.13-management-alpine |
|
Valkey (Redis-compat.) |
6379 |
valkey/valkey:8-alpine |
|
Solr |
8983 |
solr:9.8.1 |
|
SeaweedFS (S3-compat.) |
8333 |
chrislusf/seaweedfs:3.79 |
All persistences read their credentials from the anjana-secrets Secret.
Enable in-cluster persistences in values.yaml:
anjana-persistences:
enabled: true # enables the complete subchart
postgresql:
enabled: true
storage:
type: hostPath # hostPath | pvc | emptyDir
hostPath: /opt/data/postgresql
mongodb:
enabled: true
storage:
type: hostPath
hostPath: /opt/data/mongodb
rabbitmq:
enabled: true
storage:
type: hostPath
hostPath: /opt/data/rabbitmq
valkey:
enabled: true
storage:
type: hostPath
hostPath: /opt/data/valkey
solr:
enabled: true
storage:
data:
type: hostPath
hostPath: /opt/data/solrdata # Solr index
config:
type: hostPath
hostPath: /opt/data/solrconfig # ZooKeeper configuration
seaweedfs:
enabled: false # enable if using CDN proxy with in-cluster SeaweedFS
storage:
type: pvc
claimName: seaweedfs-data
For PVC with dynamic provisioning (not hostPath):
anjana-persistences:
enabled: true
postgresql:
storage:
type: pvc
storageClass: "gp3"
size: 10Gi
Cloud production: keep
anjana-persistences.enabled: falseand use managed services (RDS, DocumentDB, ElastiCache, Amazon MQ, S3).
CDN Proxy (SeaweedFS / AWS S3)
The anjana-ui frontend includes an nginx proxy to the object storage. It is configured in global.cdnProxy:
global:
cdnProxy:
enabled: true
mode: aws_s3 # aws_s3 | seaweedfs
bucket: "anjana-cdn"
aws_s3:
region: "eu-central-1"
seaweedfs:
endpoint: "https://seaweedfs.<namespace>.svc.cluster.local:8333"
If cdnProxy.enabled: false, the /cdn/ block is omitted from nginx (environments without CDN).
CA Bundle (TLS internal mode only)
kubectl -n <namespace> create secret generic anjana-internal-ca-bundle \
--from-file=ca.crt=./ca.crt \
--dry-run=client -o yaml | kubectl apply -f -
Kit update and maintenance
K8s kit update
To update the kit to a new version:
-
Download and unzip the new kit into the working directory.
-
Update the credentials bundle if necessary:
anjana horus update --from-json payload.json -
Update the Secrets in the cluster:
anjana setup [namespace] -
Update the chart dependencies and apply the upgrade:
helm dependency build
helm upgrade anjana-platform . -f values-<env>.yaml -n anjana-data
In case of failure, revert to the previous release:
helm rollback anjana-platform -n anjana-data
Anjana update and maintenance
Anjana update
The version of each service is controlled through the chart values. To update a specific service, modify its version in the values file and apply the upgrade:
global:
version:
core:
horus: 6.1.0
portuno: 5.3.0
From here, there are two strategies for applying the upgrade, depending on the type of change between versions:
Strategy A - with a service window (recommended)
Recommended for cross-minor upgrades (for example 25.x → 26.x) and for any upgrade that includes schema changes, Horus configuration changes, or connection string changes. It guarantees that different versions do not coexist communicating with each other during the transition.
Procedure:
# 1. Stop core and frontends before the upgrade
kubectl -n anjana-data scale deployment --all --replicas=0
kubectl -n anjana-data scale statefulset/horus --replicas=0
# 2. Wait for the pods to stop
kubectl -n anjana-data get pods -w
# 3. Apply the upgrade
helm upgrade anjana-platform . -f values-<env>.yaml -n anjana-data --wait
RECOMMENDED: announce the maintenance window to users before starting the procedure. After the helm upgrade --wait, the chart automatically brings the services back up in the correct order (horus → core → frontends) thanks to the wait-for-dependencies init container.
Strategy B - rolling (no window, higher risk)
Only recommended for patch upgrades (X.Y.Z → X.Y.Z+1) without structural changes. During the rolling update, different versions may temporarily coexist communicating with each other: check the compatibility matrix in the kit's release notes before proceeding.
helm upgrade anjana-platform . -f values-<env>.yaml -n anjana-data --wait
IMPORTANT: If the upgrade introduces changes to portuno.app_configuration or to the behavior of Horus Config Server, the services already running will not automatically reload the configuration. In that case it is necessary to force a restart:
kubectl rollout restart statefulset/horus -n anjana-data
kubectl rollout restart deployment -n anjana-data
Anjana maintenance
Status check
At the pod level:
kubectl get pods -n anjana-data
kubectl get pods -n anjana-data -w # real-time watch
RECOMMENDED: use watch mode to quickly identify pods that fail to start or that restart.
During platform startup (initial installation or update), the anjana-ui frontend shows a loading page with the readiness status of the services for the user, and automatically switches to the application when the platform is operational. This makes it possible to know when the environment is ready without needing kubectl access.
For a more detailed check of the service status, the kit exposes three dashboards/endpoints served through Horus and proxied by anjana-ui:
-
Spring Boot Admin: consolidated dashboard of all registered microservices, with health status, logs, JVM metrics, and active configuration:
https://<dominio>/horus/admin -
Eureka Dashboard: service registry and Service Discovery status:
https://<dominio>/horus/balancer -
Endpoint
/actuator/health- granular check per service (requires internal cluster access):
# Overall health of a service
kubectl exec -n anjana-data <pod> -- curl -k https://localhost:<port>/actuator/health
# Readiness / liveness sub-probes (the same ones Kubernetes uses)
kubectl exec -n anjana-data <pod> -- curl -k https://localhost:<port>/actuator/health/readiness
kubectl exec -n anjana-data <pod> -- curl -k https://localhost:<port>/actuator/health/liveness
RECOMMENDED: Spring Boot Admin is the fastest way to locate the exact point of failure when a service fails to start or goes to DOWN. Eureka Dashboard is used to verify that all services register correctly in the discovery.
Exporting microservice logs
kubectl logs -n anjana-data <pod> -f
kubectl logs -n anjana-data <pod> --previous # logs from the previous pod (if it crashed)
kubectl logs -n anjana-data <pod> --tail=200 --since=2h
RECOMMENDED: to provide logs to the Anjana Data support team.
Uninstallation
Uninstallation is performed with Helm:
# Uninstall the platform (keeps PVCs and Secrets)
helm uninstall anjana-platform -n anjana-data
# Complete namespace deletion (removes ALL resources and data)
kubectl delete namespace anjana-data
IMPORTANT: Deleting the namespace removes all resources. Any data that has not been persisted to external storage will be lost.