Install Clu on AWS EKS

This is the end-to-end install guide for Clu v0.0.1. Follow it top to bottom - you'll have Clu running in your EKS cluster in about 15 minutes.

Clu v0.0.1 supports AWS EKS only. Azure AKS, Google GKE, and on-prem / self-managed Kubernetes support is planned for later releases. See https://getclu.dev/roadmap.

Your data stays in your account

Clu runs inside your cluster and uses your own Amazon Bedrock in your AWS account for every inference call. Cluster telemetry, chat context, and generated artifacts never leave your account. There is no SaaS control plane and no outbound telemetry. Cloudology never sees your data.

The only outbound network calls the pod makes are to Bedrock and (for Cloud) the AWS APIs for inventory reads. Everything else is in-cluster.

Bedrock costs are billed to your AWS account at standard AWS rates. Cloudology does not proxy or mark up inference usage.

How Clu is priced

Clu uses hourly container pricing on AWS Marketplace. AWS meters pod uptime automatically and bills per EKS pod hour. The flat rate covers every module in the subscription; modules are capability gates, not price gates. One subscribed customer running Clu 24/7 for a month pays the hourly rate times 730 hours. No per-API, per-tool, or per-module metering.

Enterprise customers install from a separate Marketplace listing with negotiated Private Offer rates. Contact support@cloudology.cloud.

Prerequisites

  • An EKS cluster running Kubernetes 1.28 or newer.
  • kubectl 1.28+, helm 3.14+, aws CLI configured for the target account.
  • Cluster-admin access for the one-time setup (IAM role, trust policy, RBAC install).
  • Bedrock model access enabled in your chosen region for Claude Sonnet and Claude Haiku. New AWS accounts: open the Bedrock console, click through the model-access opt-in, wait a few minutes. Bedrock requests return 403 until the model is opted in.
  • A subscription to Clu on AWS Marketplace (or a JWT license from support@cloudology.cloud for non-Marketplace installs).

EKS worker nodes already have the permissions to pull Clu's images from AWS Marketplace ECR if they're running the default AmazonEKSWorkerNodePolicy and AmazonEC2ContainerRegistryReadOnly node roles. Marketplace gates image pulls by subscription status - no image pull secrets required.

Step 1: Choose IRSA or EKS Pod Identity

Clu authenticates pod-to-AWS via one of:

IRSAPod Identity
EKS version1.19 or newer1.27 or newer
Trust mechanismOIDC federationEKS service principal
One-time cluster setupCreate OIDC providerInstall eks-pod-identity-agent addon
RotationAutomatic (OIDC tokens)Automatic (EKS-managed)
RecommendedDefault for most installsUse if already adopted

The rest of this guide uses IRSA. If you already run Pod Identity, substitute Step 2's OIDC provider setup with "create a Pod Identity association on the clu-ops-agent service account after the Helm install (Step 6)."

Step 2: Register the cluster's OIDC provider (IRSA only)

eksctl utils associate-iam-oidc-provider \
  --cluster <your-cluster-name> \
  --approve

Only happens once per cluster. If you provisioned the cluster with terraform-aws-modules/eks/aws and set enable_irsa = true, this is already done.

Step 3: Create the IAM role for Clu

Name it something like clu-irsa-role. The role's trust policy must match the Clu service account in the clu-ops namespace.

Get your cluster's OIDC provider ID:

aws eks describe-cluster \
  --name <your-cluster> \
  --query "cluster.identity.oidc.issuer" \
  --output text

The ID is the suffix on that URL. Save the trust policy below as trust-policy.json, replacing the three placeholders:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::<AWS_ACCOUNT_ID>:oidc-provider/oidc.eks.<REGION>.amazonaws.com/id/<OIDC_ID>"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "oidc.eks.<REGION>.amazonaws.com/id/<OIDC_ID>:sub": "system:serviceaccount:clu-ops:clu-ops-agent",
          "oidc.eks.<REGION>.amazonaws.com/id/<OIDC_ID>:aud": "sts.amazonaws.com"
        }
      }
    }
  ]
}

Create the role:

aws iam create-role \
  --role-name clu-irsa-role \
  --assume-role-policy-document file://trust-policy.json

Step 4: Attach policies per module

Attach one or more of the policies below based on which modules you plan to enable. Core is always required. Cloud and Core Plus are additive.

Core policy (required)

Save as clu-module1.json:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "BedrockInvoke",
      "Effect": "Allow",
      "Action": [
        "bedrock:InvokeModel",
        "bedrock:InvokeModelWithResponseStream"
      ],
      "Resource": [
        "arn:aws:bedrock:*::foundation-model/anthropic.claude-3-*",
        "arn:aws:bedrock:*::foundation-model/anthropic.claude-haiku-*",
        "arn:aws:bedrock:*::foundation-model/anthropic.claude-sonnet-*"
      ]
    },
    {
      "Sid": "CloudWatchRead",
      "Effect": "Allow",
      "Action": [
        "cloudwatch:GetMetricData",
        "cloudwatch:GetMetricStatistics",
        "cloudwatch:ListMetrics",
        "cloudwatch:DescribeAlarms",
        "logs:StartQuery",
        "logs:GetQueryResults",
        "logs:DescribeLogGroups",
        "logs:DescribeLogStreams"
      ],
      "Resource": "*"
    },
    {
      "Sid": "MarketplaceMetering",
      "Effect": "Allow",
      "Action": [
        "aws-marketplace:RegisterUsage"
      ],
      "Resource": "*"
    }
  ]
}

The MarketplaceMetering block is only exercised for Marketplace installs. JWT-license installs with marketplace.enabled=false can omit it; leaving it in place is harmless.

Attach:

aws iam put-role-policy \
  --role-name clu-irsa-role \
  --policy-name clu-module1 \
  --policy-document file://clu-module1.json

Cloud policy (required for Cloud)

Save as clu-module2.json and attach if you plan to set modules.cloud.enabled=true:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "IamRead",
      "Effect": "Allow",
      "Action": [
        "iam:ListRoles",
        "iam:GetRole",
        "iam:ListAttachedRolePolicies",
        "iam:ListRolePolicies",
        "iam:GetRolePolicy",
        "iam:SimulatePrincipalPolicy"
      ],
      "Resource": "*"
    },
    {
      "Sid": "EksRead",
      "Effect": "Allow",
      "Action": [
        "eks:DescribeCluster",
        "eks:ListClusters",
        "eks:ListAddons",
        "eks:DescribeAddon",
        "eks:DescribeAddonVersions",
        "eks:ListNodegroups",
        "eks:DescribeNodegroup",
        "eks:ListPodIdentityAssociations",
        "eks:DescribePodIdentityAssociation",
        "eks:ListAccessEntries",
        "eks:DescribeAccessEntry",
        "eks:ListInsights",
        "eks:DescribeInsight",
        "eks:ListUpdates",
        "eks:DescribeUpdate"
      ],
      "Resource": "*"
    },
    {
      "Sid": "EksAddonManagement",
      "Effect": "Allow",
      "Action": [
        "eks:CreateAddon",
        "eks:UpdateAddon",
        "eks:UpdateAddonConfiguration",
        "eks:DeleteAddon"
      ],
      "Resource": "*"
    },
    {
      "Sid": "ManagedServicesRead",
      "Effect": "Allow",
      "Action": [
        "rds:DescribeDBInstances",
        "elasticache:DescribeCacheClusters"
      ],
      "Resource": "*"
    },
    {
      "Sid": "StorageRead",
      "Effect": "Allow",
      "Action": [
        "s3:ListAllMyBuckets",
        "s3:GetBucketLocation",
        "ecr:DescribeRepositories",
        "ecr:DescribeImages"
      ],
      "Resource": "*"
    },
    {
      "Sid": "SecretsListOnly",
      "Effect": "Allow",
      "Action": [
        "secretsmanager:ListSecrets"
      ],
      "Resource": "*"
    },
    {
      "Sid": "NetworkingRead",
      "Effect": "Allow",
      "Action": [
        "ec2:DescribeVpcs",
        "ec2:DescribeSubnets",
        "ec2:DescribeSecurityGroups",
        "ec2:DescribeAvailabilityZones"
      ],
      "Resource": "*"
    },
    {
      "Sid": "CostExplorer",
      "Effect": "Allow",
      "Action": [
        "ce:GetCostAndUsage"
      ],
      "Resource": "*"
    }
  ]
}

Cloud deliberately excludes secretsmanager:GetSecretValue. Clu never reads secret contents, only metadata (name, created date, rotation status).

Attach:

aws iam put-role-policy \
  --role-name clu-irsa-role \
  --policy-name clu-module2 \
  --policy-document file://clu-module2.json

Core Plus (Core Plus)

Core Plus writes are Kubernetes-side only, enforced through a least-privilege ClusterRole the chart creates. There is no AWS-specific policy to attach for Core Plus today. If you plan to generate AWS Terraform through Core Plus, the tool runs against the Cloud read permissions you already granted.

Step 5: Install Clu

Install Clu via Helm against the Marketplace ECR registry.

Install via Helm directly from Marketplace ECR

Still requires an active AWS Marketplace subscription - Helm pulls the chart + images from the Marketplace ECR registry which gates access by subscription status. Your AWS credentials must resolve to a subscribed account.

aws ecr get-login-password --region us-east-1 \
  | helm registry login --username AWS --password-stdin \
    709825985650.dkr.ecr.us-east-1.amazonaws.com

helm install clu-ops-agent \
  oci://709825985650.dkr.ecr.us-east-1.amazonaws.com/cloudology/clu-ops-agent \
  --version 0.1.4 \
  --namespace clu-ops \
  --create-namespace \
  --set serviceAccount.annotations."eks\.amazonaws\.com/role-arn"="arn:aws:iam::<ACCOUNT_ID>:role/clu-irsa-role" \
  --set llm.bedrock.region=<REGION>

The Marketplace product code is baked into the chart's default values; you don't configure it.

Pull container images directly

Same Marketplace-gated access pattern; use this when you want to deploy Clu through your own manifests rather than Helm. Images are at:

  • 709825985650.dkr.ecr.us-east-1.amazonaws.com/cloudology/clu-ops-agent-backend:0.1.4
  • 709825985650.dkr.ecr.us-east-1.amazonaws.com/cloudology/clu-ops-agent-frontend:0.1.4

Your EKS nodes need AmazonEKSWorkerNodePolicy + AmazonEC2ContainerRegistryReadOnly on the node IAM role - Marketplace authenticates pulls through standard EKS node credentials if the account is subscribed.

Step 6: Open the UI

kubectl port-forward -n clu-ops svc/clu-ops-agent 8080:8080

Open http://localhost:8080. The dashboard shows module status and the first knowledge-graph scan (takes 5-30 seconds depending on cluster size).

For production access (ALB, ingress, auth-aware proxy for per-operator accountability), see UI Access.

Step 7: First conversation

Try these to verify the install:

  • Health check - "What's the health of my cluster?" kicks off the reporter on demand and surfaces findings by severity.
  • Convention detection - "What conventions have you learned about this cluster?" lists the patterns the scanner picked up.
  • Cloud inventory (Cloud) - "What IAM roles does this account have?" lists them via the IRSA role.
  • Manifest generation (Core Plus) - "Create a web service manifest for a workload called api in namespace staging, image myorg/api:1.0, exposed on 8080."

Common install issues

  • 403 from Bedrock - the IRSA role is missing bedrock:InvokeModel, or Bedrock model access wasn't requested in the console. The chat surface prints the exact failing action with a paste-ready IAM fragment.
  • Pod fails to start with CustomerNotEntitledException - the AWS account isn't subscribed to the Clu Marketplace product yet, or the subscription hasn't propagated. Wait 2-3 minutes after subscribing and restart the pod.
  • Pod fails to start with PlatformNotSupportedException - running outside EKS or ECS. RegisterUsage is EKS/ECS-only. For local kind / docker-compose, use marketplace.enabled=false and license.key=dev.
  • Cloud shows active: false after enabling in Helm - most commonly a missing Cloud policy attachment or a trust-policy typo. Run aws_irsa_mapping from the chat UI for a one-shot diagnosis.
  • Empty Helm releases list in chat - the reader ClusterRole needs get/list/watch on Secrets (Helm 3 stores releases there). The chart includes this by default; a stripped-down custom install may have dropped it.
  • Prometheus tool errors with "not configured" - Clu auto-discovers Prometheus via Services whose name contains prometheus on port 9090. If your install uses a proxy or a non-standard name, set integrations.prometheus.url explicitly in Helm values.

Verifying the setup

Hit /api/status or the Knowledge view in the UI. You should see:

  • All expected modules showing active: true
  • No blockedReason on any module
  • Cloud integrations (Prometheus, metrics-server, external-secrets) detected as expected

If an AWS API call fails at runtime, Clu surfaces the exact missing action in chat with a paste-ready policy fragment:

The agent was denied 'ListRoles' on aws:iam
Fix: add iam:ListRoles to the policy attached to clu-irsa-role.

Add the action and reattach the policy. If multiple calls fail in quick succession, grant the superset rather than iterating one at a time.

Next