Skip to main content
Already have a cluster and AWS resources? Jump to Step 2: IAM and EKS Add-ons and then Step 3: Helm Values and Secrets.

Prerequisites

Before you start, make sure you have the following tools installed and configured:

Resource Checklist

Before starting, review the full list of AWS resources and external services this guide will walk you through creating. Items marked Vijil + Customer or Vijil require Vijil to update access policies on their side — request this early.
Two steps require Vijil’s involvement before you can proceed:
  1. ECR Pull Access (Step 2) — Vijil must grant your AWS account pull access to their container registry
  2. Diamond Artifacts (Step 2) — Vijil must copy Diamond evaluation artifacts (~4.5 MB) to your S3 bucket

Architecture Overview

Vijil Console Architecture showing VPC, subnets, NLB, EKS, and RDS All application workloads and the database live in private subnets. Only a single NGINX Network Load Balancer (NLB) sits in a public subnet , both console.* and console-api.* resolve to this one NLB, and the unified nginx router handles splitting frontend vs API traffic internally. EKS nodes reach S3 and ECR via NAT Gateway.

Step 1: AWS Infrastructure

VPC

Create a VPC with two public and two private subnets across two Availability Zones. The subnet tags are required. EKS uses them to discover subnets, and the AWS Load Balancer Controller uses them to place NLBs correctly.
Public subnets: the NLB goes here. It must have the kubernetes.io/role/elb=1 tag:
Private subnets: EKS nodes and RDS go here. It must have the kubernetes.io/role/internal-elb=1 tag:

EKS Cluster

--with-oidc is required. It enables the OIDC provider used by the EBS CSI driver’s IRSA configuration. Do not omit it.

Cluster Sizing Guidance

Vijil Console deploys 8 service pods plus telemetry (Grafana, Loki, Mimir, Tempo), a database migration job, and creates on-demand job pods for evaluations (Diamond), red-teaming, and scanning in separate namespaces. Why memory-optimized (r5)? Evaluation and red-team jobs are memory-intensive. r5 instances provide a better $/GiB ratio than general-purpose m5 for this workload. Disk: 200 GiB gp3 per node (default 80 GiB is tight when telemetry PVCs and container images accumulate). Set via --node-volume-size 200 in eksctl. Autoscaling: Enable Cluster Autoscaler or Karpenter. Set --nodes-min to your baseline and --nodes-max high enough to absorb burst evaluation jobs. Each Diamond/red-team job runs as a separate pod — 5 concurrent evaluations means 5 extra pods.

RDS PostgreSQL

S3 Bucket

Add a CORS configuration so the bucket can accept signed-URL file uploads from the browser:

ACM Certificate

Request a wildcard certificate, it covers both console. and console-api. subdomains with a single cert:
Wait for ISSUED status before proceeding:

Step 2: IAM and EKS Add-ons

IAM Policy for S3

Create a scoped S3 policy for your app data bucket. If you are deploying to multiple environments, use distinct policy names (e.g. VijilConsoleS3Access for prod, VijilConsoleS3AccessStaging for staging) to avoid conflicts.

S3 Access for Pods

There are two supported approaches. EKS Pod Identity (Option A) and Node IAM Role (Option B). EKS Pod Identity is recommended, it is what the current dev environment uses and scopes credentials to specific service accounts rather than all pods on a node. Pod Identity is the modern replacement for IRSA. Pods receive AWS credentials via http://169.254.170.23/v1/credentials, injected automatically by the Pod Identity Agent. The AWS SDK picks this up with no code changes.

Option B: Node IAM Role (simpler, broader scope)

Attach the S3 policy directly to the EKS node group role. All pods on every node in the cluster will inherit these permissions, simpler to set up but less isolated.

EBS CSI Driver

The telemetry stack (Grafana, Loki, Mimir, Tempo) uses PersistentVolumeClaims backed by EBS. The driver must be installed before helm install. Install the driver via Helm first (this creates the ebs-csi-controller-sa service account that the IRSA script needs), then run the IRSA setup script:
Alternatively, use the EKS managed add-on: aws eks create-addon --cluster-name $CLUSTER_NAME --addon-name aws-ebs-csi-driverThe EBS CSI driver deploys two controller replicas across AZs. Verify they show 5/5 Running before proceeding.

ECR Pull Access

Vijil action required. Before you can pull container images or the Helm chart, Vijil must run put-registry-policy in their account to grant your AWS account pull access. Request this before starting Step 2. The registry-level policy covers all repositories, so a single setup grants access to both container images and the Helm chart (OCI artifact).

Option A: Vijil’s ECR (cross-account pull)

Vijil’s images and Helm chart live in account 266735823956 (region us-west-2). Two steps are required: one run by Vijil’s side, one by yours. Step 1 - Vijil side (run in account 266735823956): Grant your account pull access on the ECR registry.
To allow multiple customer accounts, add additional ARNs to Principal.AWS as an array and re-run put-registry-policy once. Step 2 - Customer side: Give the EKS node role permission to pull from Vijil’s ECR.
After a minute, run a test pod that pulls from Vijil’s ECR:

Option B: Your own registry

Build and push images to your own registry, then override *.image.repository in your values file (see Step 3).

Bedrock AgentCore (Diamond / custom Harness)

The Diamond evaluation page and custom Harness workflows call the Bedrock AgentCore API. Attach a scoped policy to the same IAM principal your Console pods use for AWS access. If you used Pod Identity, attach it to the Pod Identity role. If you used the node IAM role, attach it to that.
The policy above includes bedrock-agentcore:GetAgentCard so the Console can fetch the agent card when creating custom Harnesses. If you created the policy before this was added, add a new policy version that includes bedrock-agentcore:GetAgentCard in the BedrockAgentCoreData statement and run aws iam create-policy-version --policy-arn <policy-arn> --policy-document file://policy.json --set-as-default.

Staging / Non-Dev: Custom Harness AgentCore Runtime

In dev, a custom Harness AgentCore runtime already exists in the account and is looked up by name. In any non-dev account (staging, customer), that runtime does not exist — you need to create one and point the Console at it via CUSTOM_HARNESS_AGENT_RUNTIME_ARN. Prerequisite: Vijil’s dev ECR (266735823956) must allow your account to pull images — see 2.4 Step 1.

Step 1: Create the execution role

The runtime runs the Harness container under an IAM role that Bedrock AgentCore assumes. Create a role with (a) a trust policy allowing bedrock-agentcore.amazonaws.com, and (b) permissions for ECR pull (your account + Vijil’s 266735823956), S3 for your app bucket, and CloudWatch Logs. Replace STAGING_ACCOUNT_ID with your account ID (e.g. 565393042914 for staging). Run in the staging account:
Then attach permissions for ECR (your account + Vijil’s 266735823956), S3, CloudWatch Logs, X-Ray, and Bedrock model invocation. Replace vijil-console-data-staging with your app bucket name:

Step 2: Create the AgentCore runtime

Use the same VPC subnets and security group as your EKS. Container image: Vijil’s Harness agent in dev ECR (vijil-harness-agent-agentcore). Environment variables should match dev except the bucket; the following snippet uses the same set as dev with the staging bucket.
The command returns agentRuntimeArn and agentRuntimeId. Set commonEnv.CUSTOM_HARNESS_AGENT_RUNTIME_ARN in your values (e.g. my-values.yaml) to the returned agentRuntimeArn.

Diamond Artifacts

Vijil action required. Vijil must copy Diamond evaluation artifacts (~4.5 MB, 38 files) to your S3 bucket before evaluations can run.
The Diamond evaluation engine downloads detector configs, harness definitions, and small model weights from S3 at startup. These artifacts are versioned and live under a diamond/{RESOURCE_VERSION}/ prefix, where RESOURCE_VERSION is a version string managed by Vijil (e.g. v1.2.0) that matches the deployed Diamond release. You do not need to set this value — the application reads it automatically. What gets copied:
How it works:
  1. Vijil runs aws s3 sync from their internal artifacts bucket to your S3 bucket.
  2. You set RESOURCE_BUCKET_NAME to your own bucket (the same bucket as S3_BUCKET_NAME). No separate artifacts bucket or cross-account IAM is needed — your existing S3 policy already covers your own bucket.
  3. If using the install block in your Helm values, install.aws.s3Bucket automatically sets both S3_BUCKET_NAME and RESOURCE_BUCKET_NAME to the same value. No extra configuration required.

Step 3: Helm Values and Secrets

Work from the chart directory:

3.1 Secrets file

Edit values/secrets/secrets.yaml:
Never commit secrets.yaml — add it to .gitignore.

3.2 Environment values file

Create my-values.yaml. The install block is the simplest way to configure the chart — set values once and the chart derives the redundant env vars for you:
Why the nginx NLB must be internal: false for public deployments: The React frontend is a single-page application that runs in the user’s browser.VITE_API_PREFIX is baked into the frontend at build time, and the browser makes API calls directly to that URL. If the single nginx NLB is internal-only, browser requests will fail even if DNS resolves.

Step 4: Helm Install

Wait for all pods to reach Running. Then get the nginx NLB hostname:
If EXTERNAL-IP shows <pending>, wait a moment and re-run.

Step 4.5: Deploy Darwin (Evolution Engine)

Darwin is the evolution engine. It runs in its own namespace (vijil-darwin) and is deployed separately from the vijil-console Helm chart. The Console chart routes /evolution/... API traffic to service-darwin.vijil-darwin.svc.cluster.local. Console starts fine without Darwin, but any /evolution calls will return 502 or 504 until Darwin is running.
Verify Darwin is healthy:

Step 5: DNS

Create Route 53 records after the NLB is provisioned. Both console.* and console-api.* point to the same nginx NLB hostname.
If you prefer CNAME records instead of alias records, replace AliasTarget with "Type": "CNAME" and "ResourceRecords": [{"Value": "$NGINX_NLB"}] — no NLB_HOSTED_ZONE_ID needed.The NLB alias hosted zone ID differs by region — see the full list in AWS docs.

Step 6: Database Migrations

Migrations run automatically as Helm pre-install and pre-upgrade hooks — no manual action needed on a normal install. Verify they completed:
If a hook failed, run the migration manually after pods are in Running state:

Step 7: Bootstrap

Run from the repo root (not the chart directory):
Optionally seed default content:
For staging environments with in-cluster sample agents: Set these env vars before running seed_agents.py to point seeded agents at in-cluster sample agent services:
Then run the seed script (or use --update to refresh existing agents’ URLs to http://<service>.<namespace>.svc.cluster.local/v1):

Step 8: Verify

Access the UI at https://console.yourdomain.com and log in with the credentials you set in bootstrap.
Last modified on July 1, 2026