AWS Applications Deployment
This guide covers deploying the core Kindo application stack on AWS using the kindo-applications Terraform module.
Overview
The applications module deploys these Kindo services:
| Service | Purpose |
|---|---|
| API | Node.js backend REST/tRPC API |
| Next.js | Server-rendered React frontend |
| LiteLLM | AI model routing proxy |
| Llama Indexer | Document indexing service |
| SSOReady | SSO authentication services |
| Cerbos | Authorization policy engine |
| Task Worker | Background job processing |
| External Poller | Integration data polling |
| External Sync | Integration synchronization |
| Credits | Usage tracking service |
| Audit Log Exporter | Compliance log export |
Quick Start
-
Set up the applications stack:
Terminal window cd kindo-modules/stacks/applicationscp terraform.tfvars.example terraform.tfvars -
Configure
terraform.tfvarswith outputs from infrastructure, secrets, and peripheries modules. -
Deploy:
Terminal window terraform initterraform planterraform apply
Configuration
Core Settings
project_name = "mycompany"environment = "production"aws_region = "us-west-2"
# Domaindomain_name = "kindo.mycompany.com"
# Kindo registrykindo_registry_username = "..."kindo_registry_password = "..."
# Application versionapp_version = "2025.08.2"From Previous Module Outputs
The applications module requires outputs from all three previous modules (infrastructure, secrets, peripheries), including:
- EKS cluster details
- Database endpoints and credentials
- Redis and RabbitMQ endpoints
- S3 bucket names
- Unleash endpoint
- Secret references
Post-Deployment
Verify All Services
for ns in api next litellm llama-indexer ssoready cerbos task-worker-ts \ external-poller external-sync credits audit-log-exporter; do echo "=== $ns ===" kubectl get pods -n $nsdoneConfigure DNS
Create DNS records pointing to the ALB endpoints:
# Get ALB addresseskubectl get ingress -A -o wideCreate A/CNAME records for:
app.kindo.mycompany.com— Next.js ingressapi.kindo.mycompany.com— API ingresssso.kindo.mycompany.com— SSOReady ingress
Initial Setup
- Create the first organization and admin user using the setup SQL (see SSO Setup)
- Configure AI models in Unleash (see Model Configuration)
- Set up integrations via Nango (see Integrations)
Test End-to-End
# Health checkcurl https://api.kindo.mycompany.com/health
# Access the UIopen https://app.kindo.mycompany.comIngress and DNS
The applications stack creates a shared Application Load Balancer (ALB) ingress group that fronts every Kindo service. Use the tables below to plan DNS, firewall rules, and health probes for self-managed installs. The values mirror the installer reference in infra/released-modules/kindo-helm-e2e-dist/docs/INGRESS.md.
Route Table
| Hostname | Namespace | Service | Port | Health Check | Paths | Access |
|---|---|---|---|---|---|---|
app.<domain> | next | next | 80 | /api/health | / | Whitelisted |
api.<domain> | api | api | 80 | /healthcheck | / | Whitelisted |
api.<domain> (webhooks) | api | api | 80 | /healthcheck | /merge-integration-changes, /merge-common-model-synced-webhook | Public/Webhook Provider |
integrations-api.<domain> | nango | nango | 80 | /health | / | Provider callbacks (see Nango docs) |
integrations-connect.<domain> | nango | nango-connect-ui | 3009 | / | / | Whitelisted |
sso-auth.<domain> | ssoready | ssoready-auth | 80 | /health | / | Whitelisted |
sso-api.<domain> | ssoready | ssoready-api | 80 | /health | / | Whitelisted |
sso.<domain> | ssoready | ssoready-admin | 80 | /health | / | Whitelisted |
sso-app.<domain> | ssoready | ssoready-app | 80 | /health | / | Whitelisted |
hatchet.<domain> | hatchet | hatchet-api | 8080 | /api/ready | /api | Whitelisted |
hatchet.<domain> (fallback) | hatchet | hatchet-frontend | 8080 | /api/ready | / | Whitelisted |
litellm.<domain> | litellm | litellm | 4000 | /health/liveliness | / | Whitelisted |
unleash.<domain> | unleash | unleash | 4242 | /health | / | Whitelisted |
unleash-edge.<domain> | unleash | unleash-edge | 3063 | /health | / | Whitelisted |
Webhooks and Streaming Requirements
- Merge.dev webhooks:
/merge-integration-changesand/merge-common-model-synced-webhookmust be reachable by Merge. In private (internal) ALB deployments these paths route over your corporate network; in internet-facing setups, allow-list Merge’s IP ranges or route the two paths via a dedicated ingress rule. - SSE streaming (
/express/chat): The API ingress must disable response buffering, gzip, and short timeouts. Configure your ingress/WAF with:- Response buffering: disabled (
proxy_buffering offin NGINX) - Read/send timeouts: ≥ 300s
- Response body limits: none
- No content transformation on SSE responses
- Response buffering: disabled (
Hatchet Path-Based Routing
hatchet.<domain> serves two backends: /api goes to hatchet-api (REST/gRPC) and the catch-all / route serves the admin UI. Ensure the /api rule has a higher priority than / so task-worker traffic doesn’t hit the UI service.
DNS Strategy
- Wildcard: Point
*.<domain>at the ALB for the quickest rollout. - Explicit records: Create individual
A/CNAMEentries for each hostname when you need per-host routing or firewall control. - Regardless of strategy, keep TLS certs aligned with the hostnames above. With cert-manager, add the full host list to your
Certificateresources.
Bring Your Own Ingress
The shipped kindo-ingress chart is optional. If you remove it (make ingress-destroy) and supply your own ingress controller:
- AWS ALB Controller: Use the
kindo-sharedIngressGroup order from the table above. Attach the AWSLoadBalancerControllerIAMPolicy to the controller role. - Non-VPC CNIs (Calico/Cilium): ALB
target-type=ipcannot reach pod IPs on those CNIs. Either switch services toNodePortand usetarget-type=instance, or adopt an in-cluster ingress (NGINX, Traefik, Istio, etc.). - NGINX example: set
proxy-buffering: "off",proxy-read-timeout: 300,proxy-send-timeout: 300, and disable gzip on the API ingress. Similar settings exist for Traefik (respondingTimeouts) and Envoy (stream_idle_timeout).
These requirements also apply to downstream proxies (CloudFront, F5, HAProxy, etc.). Always replicate the streaming and health-check behavior documented above.
Troubleshooting
Pods in CrashLoopBackOff
kubectl logs <pod-name> -n <namespace> --previouskubectl describe pod <pod-name> -n <namespace>Common causes: missing secrets, database connection failures, incorrect environment variables.
Ingress Not Working
# Check ALB controller logskubectl logs -n kube-system -l app.kubernetes.io/name=aws-load-balancer-controller
# Verify target group healthaws elbv2 describe-target-health --target-group-arn <arn>