Deploying on AWS (EKS)
Run Orbtrace on Amazon EKS the recommended way — RDS for Postgres+pgvector, a throughput-tuned gp3 StorageClass, Doris via the operator or EC2, an ALB ingress with ACM TLS, and the AWS-specific gotchas (EBS AZ-locking, IRSA, security groups).
This is the Kubernetes (Helm) path on Amazon EKS. The generic Helm mechanics — pull secret, my-values.yaml, helm install — are on Install with Helm; this page is the AWS-specific decisions layered on top, and it follows the same recommendation as everywhere else: run the stateful stores as managed services, let Helm deploy only the stateless app.
In EKS — Helm deploys
AWS-managed / EC2 — you run these
Apps send OTLP to your Collector, which writes to Doris. The stateless Orbtrace app in EKS reads telemetry back from Doris and keeps settings + cache in RDS Postgres + the bundled Valkey. Only the app and the ephemeral cache live in EKS; the data you can't lose (RDS, Doris) stays on managed/EC2 services outside the pod lifecycle.
What runs where on AWS
| Component | AWS choice | Notes |
|---|---|---|
| Orbtrace app | EKS (this chart) | Stateless — replicas + HPA, spread across AZs |
| Postgres + pgvector | RDS for PostgreSQL | Multi-AZ in production; pgvector is a one-line CREATE EXTENSION |
| Valkey (cache) | bundled in-cluster | Ephemeral — leave it in EKS. ElastiCache only if you want it managed |
| Apache Doris | doris-operator on EKS, or EC2 VMs | Memory-heavy, IOPS-heavy — give it its own node group / instances |
| Collector | a container / your existing pipeline | Never in the chart — see Integration patterns |
| Ingress + TLS | AWS Load Balancer Controller (ALB) + ACM | NLB via a LoadBalancer Service is the alternative |
| Block storage | gp3 (EBS CSI) | Tune IOPS/throughput — Doris BE is IOPS-bound (step 2) |
Size the cluster from your workload first: run the Capacity planning calculator and it returns the Doris FE/BE counts, per-node vCPU/memory, and disk — the node-group shapes below follow from it.
- 1
Create the EKS cluster — two node groups
Give Doris BE its own node group: it wants memory (16–32 GB+ each) and pinned, IOPS-heavy EBS, which you don't want competing with the stateless app. Spread the app group across AZs for HA. A
eksctlconfig with both:cluster.yaml apiVersion: eksctl.io/v1alpha5 kind: ClusterConfig metadata: { name: orbtrace, region: <region>, version: "1.30" } iam: { withOIDC: true } # required for IRSA (step 2) managedNodeGroups: - name: app instanceType: m6i.xlarge desiredCapacity: 3 availabilityZones: ["<az-a>", "<az-b>", "<az-c>"] - name: doris-be instanceType: r6i.2xlarge # memory-optimized; size per capacity planning desiredCapacity: 3 availabilityZones: ["<az-a>"] # keep a BE group in ONE AZ — see the EBS note below labels: { "orbtrace.io/doris-be": "true" }Applysave as
cluster.yaml, then runeksctl create cluster -f cluster.yamlEBS volumes are AZ-locked — pin Doris BE to one AZ
An EBS volume lives in a single Availability Zone; a pod bound to that PVC can only run on a node in the same AZ. If a Doris BE pod is rescheduled to another AZ it cannot mount its data and stays
Pending. Keep each BE node group in one AZ (as above), or run one BE node group per AZ with the operator's anti-affinity — never let a single BE's replicas float across AZs. The stateless app has no such constraint; spread it freely. - 2
EBS CSI driver (via IRSA) + a throughput-tuned gp3 StorageClass
The EBS CSI driver needs its own IAM role through IRSA — without it, every PVC hangs
Pending. Create the role, then install the add-on wired to that role (the--service-account-role-arnis the step people miss —--role-onlycreates the role but does not attach it to the driver):eksctl create iamserviceaccount --cluster orbtrace --region <region> \ --namespace kube-system --name ebs-csi-controller-sa \ --attach-policy-arn arn:aws:iam::aws:policy/service-role/AmazonEBSCSIDriverPolicy \ --role-only --role-name orbtrace-ebs-csi --approve eksctl create addon --cluster orbtrace --region <region> --name aws-ebs-csi-driver \ --service-account-role-arn arn:aws:iam::<acct-id>:role/orbtrace-ebs-csi --forceThen a
gp3StorageClass — and turn the IOPS and throughput up. gp3 defaults to 3000 IOPS / 125 MB/s, which starves Doris BE compaction; 6000+ IOPS and 250+ MB/s is a sane floor for a real workload:gp3-orbtrace.yaml apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: gp3-orbtrace annotations: { storageclass.kubernetes.io/is-default-class: "true" } provisioner: ebs.csi.aws.com parameters: type: gp3 iops: "6000" # gp3 default 3000 is too low for BE compaction throughput: "250" # MB/s — gp3 default 125 is too low volumeBindingMode: WaitForFirstConsumer # binds the volume in the pod's AZApplysave as
gp3-orbtrace.yaml, then runkubectl apply -f gp3-orbtrace.yamlWaitForFirstConsumeris important: it delays provisioning until the pod is scheduled, so the volume is created in the same AZ as the pod (the flip side of the AZ-lock above). - 3
Provide Postgres — RDS for PostgreSQL + pgvector
Create an RDS for PostgreSQL 16+ instance in the cluster's VPC (Multi-AZ for production — automated failover and backups), in private subnets. Allow the EKS node security group to reach it on 5432:
aws ec2 authorize-security-group-ingress \ --group-id <rds-sg-id> --protocol tcp --port 5432 \ --source-group <eks-node-sg-id>Then, connected to the instance, create the database and enable pgvector (RDS ships it — no parameter-group change needed):
CREATE DATABASE orbtrace; CREATE USER orbtrace WITH PASSWORD 'a-strong-password'; GRANT ALL PRIVILEGES ON DATABASE orbtrace TO orbtrace; \c orbtrace CREATE EXTENSION IF NOT EXISTS vector;The
orbtracenames are a convention — any database/user works; you'll setpostgres.database/postgres.usernameto match in step 6. (Cache: leave Valkey bundled — it's ephemeral; reach for ElastiCache only if you want it managed.) - 4
Set the Doris node kernel prerequisite
Doris BE needs
vm.max_map_count ≥ 2000000on its nodes. Apply it with a small privileged DaemonSet targeted at the BE node group you labeled in step 1 (or bake it into a custom AMI'ssysctl):doris-sysctl.yaml apiVersion: apps/v1 kind: DaemonSet metadata: { name: doris-sysctl, namespace: kube-system } spec: selector: { matchLabels: { app: doris-sysctl } } template: metadata: { labels: { app: doris-sysctl } } spec: nodeSelector: { "orbtrace.io/doris-be": "true" } initContainers: - name: sysctl image: busybox:1.37 securityContext: { privileged: true } command: ["sh", "-c", "sysctl -w vm.max_map_count=2000000"] containers: - name: pause image: registry.k8s.io/pause:3.9Applysave as
doris-sysctl.yaml, then runkubectl apply -f doris-sysctl.yaml - 5
Stand up Doris
Run Doris with the doris-operator in the cluster — its
DorisClusteruses thegp3-orbtraceStorageClass and anodeSelectorfor the BE node group — or on EC2 VMs and point the chart at it. Full walkthrough (operator install, the referenceDorisCluster, BE PVC sizing) is on Setting up Doris. Note the FE Service address fordoris.host. - 6
Install Orbtrace with Helm
Follow Install with Helm — the pull secret and
helm installare identical on AWS. In yourmy-values.yaml, point Postgres at RDS and Doris at the address from step 5:my-values.yaml global: imagePullSecrets: [{ name: ghcr }] # the secret from the Helm page postgres: mode: external host: orbtrace.abc123.<region>.rds.amazonaws.com # RDS endpoint port: 5432 database: orbtrace username: orbtrace password: <your-postgres-password> # or existingSecret — see the Helm page doris: host: <your-doris-fe-host> # FE Service / EC2 address from step 5 - 7
Expose it — ALB ingress with ACM TLS
Install the AWS Load Balancer Controller (its own Helm chart + an IRSA role), then drive the ALB entirely from the chart's ingress annotations (chart ≥ 2.0.13):
my-values.yaml (add) orbtrace: 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: '[{"HTTP": 80}, {"HTTPS": 443}]' alb.ingress.kubernetes.io/ssl-redirect: "443" # needs the HTTP:80 listener above to redirect from alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:<region>:<acct-id>:certificate/<id> alb.ingress.kubernetes.io/healthcheck-path: /actuator/health/readiness hosts: - host: orbtrace.example.com paths: [{ path: /, pathType: Prefix }]Point DNS (a Route 53 alias record) at the ALB, then upgrade the release. Alternative — NLB: if you'd rather front the app with a Network Load Balancer, set
orbtrace.ingress.enabled: falseand apply a small standaloneServiceoftype: LoadBalancercarryingservice.beta.kubernetes.io/aws-load-balancer-type: nlb, its selector pointing atapp.kubernetes.io/name: orbtrace— TLS then terminates at an upstream proxy, not the LB. The ALB path above is the simpler, recommended one.On a chart older than 2.0.13 the
ingress.annotationskey isn't rendered — either upgrade, or apply a hand-writtenIngresswith these annotations pointing at theorbtrace-appService. - 8
Verify
kubectl -n orbtrace get pods # orbtrace-app ×N, orbtrace-valkey-0 Running; migration line: kubectl -n orbtrace logs deploy/orbtrace-app -c orbtrace | grep doris-migration # ready when you see: [doris-migration] complete kubectl -n orbtrace get ingress # ADDRESS column shows the ALB DNS name once provisioned curl -fsSI https://orbtrace.example.com | head -1 # HTTP/2 200Then continue to First login.
Production hardening on AWS
- RDS: Multi-AZ, automated backups + PITR, and a parameter group with a sane
max_connections. Store the DB password in Secrets Manager and reference it viapostgres.existingSecret. - Doris backups: enable the chart's
backup.*CronJobs to push Postgres dumps and Doris snapshots to S3 (the referenceDorisClustersupports an S3 cold tier too — see Capacity planning). - Nodes: run the app node group across ≥ 3 AZs; add the Cluster Autoscaler or Karpenter. The chart already ships PodDisruptionBudgets so a node drain never takes the last replica.
- Least privilege: IRSA per controller (EBS CSI, Load Balancer Controller), private RDS subnets, security groups scoped to the node SG — no
0.0.0.0/0on 5432.
Common AWS first-install snags
- PVCs stuck
Pending→ the EBS CSI driver has no IAM role. Re-check the IRSA service account in step 2. - A Doris BE pod stuck
Pendingafter a reschedule → it landed in a different AZ from its EBS volume. Pin the BE node group to one AZ (step 1). - Ingress has no
ADDRESS→ the AWS Load Balancer Controller isn't installed or its IRSA role is missing. - App un-Ready,
STORAGE_UNAVAILABLE→ node security group can't reach RDS on 5432, ordoris.hostis wrong. See Troubleshooting. - BE disk slow / compaction lagging → gp3 at default 3000 IOPS. Raise
iops/throughputon the StorageClass (step 2).
Wire your telemetry in — stand up an OTel Collector, or add the doris exporter to your existing OTel pipeline (Integration patterns) — then finish with the post-install checklist.