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.Architecture Overview

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.kubernetes.io/role/elb=1 tag:
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
ACM Certificate
Request a wildcard certificate, it covers bothconsole. and console-api. subdomains with a single cert:
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.Option A: EKS Pod Identity (recommended)
Pod Identity is the modern replacement for IRSA. Pods receive AWS credentials viahttp://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 beforehelm 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:
ECR Pull Access
Option A: Vijil’s ECR (cross-account pull)
Vijil’s images and Helm chart live in account266735823956 (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.
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.
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.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 viaCUSTOM_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 allowingbedrock-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:
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.
agentRuntimeArn and agentRuntimeId. Set commonEnv.CUSTOM_HARNESS_AGENT_RUNTIME_ARN in your values (e.g. my-values.yaml) to the returned agentRuntimeArn.
Diamond Artifacts
The Diamond evaluation engine downloads detector configs, harness definitions, and small model weights from S3 at startup. These artifacts are versioned and live under adiamond/{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:
- Vijil runs
aws s3 syncfrom their internal artifacts bucket to your S3 bucket. - You set
RESOURCE_BUCKET_NAMEto your own bucket (the same bucket asS3_BUCKET_NAME). No separate artifacts bucket or cross-account IAM is needed — your existing S3 policy already covers your own bucket. - If using the
installblock in your Helm values,install.aws.s3Bucketautomatically sets bothS3_BUCKET_NAMEandRESOURCE_BUCKET_NAMEto the same value. No extra configuration required.
Step 3: Helm Values and Secrets
Work from the chart directory:3.1 Secrets file
values/secrets/secrets.yaml:
Never commit
secrets.yaml — add it to .gitignore.3.2 Environment values file
Createmy-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
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.
Step 5: DNS
Create Route 53 records after the NLB is provisioned. Bothconsole.* and console-api.* point to the same nginx NLB hostname.
Step 6: Database Migrations
Migrations run automatically as Helmpre-install and pre-upgrade hooks — no manual action needed on a normal install. Verify they completed:
Running state:
Step 7: Bootstrap
Run from the repo root (not the chart directory):seed_agents.py to point seeded agents at in-cluster sample agent services:
--update to refresh existing agents’ URLs to http://<service>.<namespace>.svc.cluster.local/v1):
Step 8: Verify
https://console.yourdomain.com and log in with the credentials you set in bootstrap.