Skip to content
Studio previewSelf-host Studio locally.Run it

Deploy Kortyx Studio on AWS with CDK

Updated 10 days ago • October 4, 2026

@kortyx/aws-cdk deploys a private Kortyx Studio installation in an existing VPC:

new KortyxStudio(this, "Studio", { vpc, domainName: "studio.example.com", version: "v0.4.0", });

The construct creates ECS Fargate, PostgreSQL on RDS, generated credentials in Secrets Manager, an internal HTTPS load balancer, and Route 53 DNS. Studio and telemetry share one hostname.

Private by default: The load balancer accepts traffic from the VPC CIDR, and neither the ECS task nor RDS receives a public IP. Reach it from the VPC, a peered network, or a VPN.

Good to know: Wire topology publication into your application's CI/CD. Deploying this construct does not publish your application's workflow catalog. Run kortyx topology push for each application release from a workload that can reach the private telemetry API. A public GitHub-hosted runner cannot reach the internal ALB without additional network connectivity. See Publish topology from the application release.

Choose a deployment path

This construct is the maintained, batteries-included AWS path, not a requirement for running Kortyx on AWS. Use it when your infrastructure is managed with CDK and its defaults fit your environment.

Kortyx also publishes ordinary container images and a stable deployment contract. You can implement that contract with:

  • your own ECS service using Terraform, Pulumi, CloudFormation, or lower-level CDK;
  • EKS or another Kubernetes platform;
  • Docker Compose on EC2; or
  • an existing database, cluster, certificate, and DNS design.

The CDK package is an infrastructure dependency; the deployed Kortyx workload does not require it at runtime. If your organization already standardizes on another infrastructure tool, keep that tool and use the same containers and environment variables. See Deploy on a Server for the portable container path.

Architecture

VPC or VPN client | | HTTPS :443 v studio.example.com (internal ALB) |-- /v1/telemetry/*, /health -> API :6400 `-- everything else -> Studio :6300 | v private RDS PostgreSQL

The Fargate task starts in this order:

database migration + bootstrap -> healthy telemetry API -> Studio

A failed migration or bootstrap prevents that task from serving traffic.

Before you start

You need:

  • an AWS account and AWS CLI credentials with permission to deploy CDK stacks;
  • Node.js 22 or newer;
  • an existing VPC with private subnets in at least two Availability Zones;
  • outbound HTTPS from those subnets, through NAT or equivalent egress, so Fargate can pull the public GHCR images; and
  • a public Route 53 hosted zone for the parent domain.

The minimal example infers example.com from studio.example.com and creates a DNS-validated ACM certificate. Pass an explicit hostedZone or certificate when your DNS layout differs.

This deployment creates billable ECS, RDS, Application Load Balancer, Secrets Manager, CloudWatch Logs, and networking resources. Review cdk diff and your expected AWS cost before deployment.

1. Create a CDK application

mkdir kortyx-studio-infra cd kortyx-studio-infra npx aws-cdk@latest init app --language typescript npm install @kortyx/aws-cdk aws-cdk-lib constructs

AWS requires each account and Region to be bootstrapped for CDK once.

2. Add Kortyx Studio

Replace lib/kortyx-studio-infra-stack.ts with:

lib/kortyx-studio-infra-stack.ts
import * as cdk from "aws-cdk-lib"; import * as ec2 from "aws-cdk-lib/aws-ec2"; import { Construct } from "constructs"; import { KortyxStudio } from "@kortyx/aws-cdk"; export class KortyxStudioInfraStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); const vpc = ec2.Vpc.fromLookup(this, "Vpc", { vpcId: "vpc-0123456789abcdef0", }); new KortyxStudio(this, "Studio", { vpc, domainName: "studio.example.com", version: "v0.4.0", }); } }

Use an immutable published version. latest and incomplete versions are rejected so a deployment can be reproduced and rolled back intentionally.

If studio.example.com is not directly beneath the hosted zone, pass it explicitly:

import * as route53 from "aws-cdk-lib/aws-route53"; const hostedZone = route53.HostedZone.fromLookup(this, "HostedZone", { domainName: "internal.example.com", }); new KortyxStudio(this, "Studio", { vpc, hostedZone, domainName: "studio.internal.example.com", version: "v0.4.0", });

3. Review the defaults

ResourceDefault
ECSOne Fargate task with database-init, API, and Studio containers
RDSPostgreSQL 17, db.t4g.micro, 20 GiB with storage autoscaling
SecretsGenerated database password, key pepper, Project keys, and Basic Auth password
Load balancerInternal ALB; one hostname with path-based routing
NetworkingALB ingress from the VPC CIDR; no task or database public IP
RecoverySeven-day database backups, retained secrets and logs, database deletion protection

Common overrides remain concise:

new KortyxStudio(this, "Studio", { vpc, domainName: "studio.example.com", version: "v0.4.0", allowedCidrs: [vpc.vpcCidrBlock, "10.50.0.0/16"], databaseMultiAz: true, databaseInstanceType: ec2.InstanceType.of( ec2.InstanceClass.T4G, ec2.InstanceSize.SMALL, ), });

You can also supply an existing cluster, hostedZone, certificate, or database. The TypeScript API documents the required shape for each value.

Availability: what one application task means

The @kortyx/aws-cdk convenience construct runs one active ECS task. This is an availability boundary, not a data-durability boundary:

  • telemetry, projects, and Studio state live in PostgreSQL, not in the task;
  • if the task crashes, ECS starts a replacement against the same database;
  • RDS backups, deletion protection, and an optional Multi-AZ standby protect the database; but
  • Studio and telemetry are briefly unavailable while the replacement initializes, and planned version updates also have a short interruption.

The Kortyx runtime supports multiple replicas, but the convenience construct intentionally keeps its cohesive task topology and one-step API. It does not model an existing load balancer, independently scaled services, or a separate deployment job. Do not deploy two copies of the construct against one database to imitate high availability.

When an AWS environment needs continuous application availability, implement the multi-replica deployment contract with lower-level CDK, Terraform, CloudFormation, ECS, or EKS: run migrations once, keep at least two replicas healthy across Availability Zones, and follow each release's rolling or recreate marker.

For stronger database availability, enable databaseMultiAz; it does not remove the convenience construct's single application task restart window.

4. Bootstrap, inspect, and deploy

Confirm the AWS identity and Region:

aws sts get-caller-identity aws configure get region

Then bootstrap, compile, inspect, and deploy:

npx cdk bootstrap npm run build npx cdk synth npx cdk diff npx cdk deploy

Commit the CDK source to your infrastructure repository. For shared environments, deploy through your reviewed CI/CD workflow.

The deployment is ready when the ECS service has one running task and both target groups report it healthy.

5. Retrieve the initial credentials

The construct outputs the Studio URL and generated secret names. Retrieve the browser password without placing it in source control:

studio_password_secret="$(aws cloudformation describe-stacks \ --stack-name KortyxStudioInfraStack \ --query "Stacks[0].Outputs[?contains(OutputKey, 'StudioPasswordSecretName')].OutputValue" \ --output text)" aws secretsmanager get-secret-value \ --secret-id "$studio_password_secret" \ --query SecretString \ --output text

Open the StudioUrl output and sign in as admin with that password.

Build the telemetry write key from its generated secret suffix:

telemetry_secret_name="$(aws cloudformation describe-stacks \ --stack-name KortyxStudioInfraStack \ --query "Stacks[0].Outputs[?contains(OutputKey, 'TelemetryKeySecretName')].OutputValue" \ --output text)" telemetry_secret="$(aws secretsmanager get-secret-value \ --secret-id "$telemetry_secret_name" \ --query SecretString \ --output text)" printf 'ktyx_live_default-telemetry_%s\n' "$telemetry_secret" unset telemetry_secret

Treat both outputs as secrets. Do not paste them into build logs, tickets, or screenshots.

6. Connect and verify

Give each application's server runtime:

server environment
KORTYX_TELEMETRY_API_URL=https://studio.example.com KORTYX_TELEMETRY_API_KEY=ktyx_live_default-telemetry_... KORTYX_TELEMETRY_ENVIRONMENT=production KORTYX_TELEMETRY_SERVICE_NAME=my-agent

The application must have a network route to the internal load balancer. Never put the telemetry key in a browser bundle. Continue with Connect Your Project to publish workflow topology.

Publish topology from the application release

Keep the catalog entrypoint with your agent application's source, not in the Studio infrastructure repository. It should export the same workflows the agent uses without starting an HTTP server or connecting to application persistence. For example:

src/catalog.ts
export { workflows } from "./workflows.js";

Include that module and the installed Kortyx CLI in your application's release image. If your build emits dist/catalog.js, run this command from the application directory inside that image:

./node_modules/.bin/kortyx topology push --entry dist/catalog.js

For ECS applications, the release workflow can launch a separate one-off ECS task using the same immutable application image, with its command overridden to run the CLI. This is not an exec into a running application container, and it does not run in the Studio container. Configure the task's network access and inject the application's four telemetry environment variables above through the workload definition and secret manager.

The workflow runner calls the AWS ECS API to start the task; the task itself sends topology to the telemetry API behind the internal ALB. It needs working DNS, a route to that ALB, and allowed security-group/CIDR access. Network access alone is not authentication: the CLI also needs the Project-scoped telemetry write key.

Wire this as an explicit application-release step, ideally before enabling traffic to the new application version:

  1. Wait for the Kortyx telemetry API to be ready.
  2. Launch the catalog task for the application version being released.
  3. Wait for it to finish and fail the release step if its exit code is nonzero.
  4. Open Workflows in Studio to confirm the catalog, then execute a real request to verify Runs.

A runner already connected to the allowed private network can run the CLI directly instead. Keep the same service name and environment as the application runtime, and never print the write key in CI logs. This job belongs to the application's release workflow; it is separate from the Studio database migration/bootstrap job.

From an allowed VPC or VPN client:

curl --fail https://studio.example.com/health curl --head https://studio.example.com

The API health request should return 200. Studio may return 401 before browser credentials are provided; that confirms Basic Auth is active.

If a target remains unhealthy, inspect the task's database-init, api, and studio CloudWatch log streams in that order. Common causes are missing subnet egress to GHCR, DNS or certificate validation, a CIDR that does not match the caller's observed address, or database connectivity blocked by a security group.

Upgrade with CDK

ECS deployments are controlled by CDK, not by the local Docker updater shown in Studio.

  1. Back up the RDS database and deployment secrets.
  2. Change version to the next immutable Studio release.
  3. Run npm run build, npx cdk synth, and npx cdk diff.
  4. Deploy through the same reviewed path.
  5. Wait for database initialization and both target groups to become healthy.

The ECS deployment circuit breaker can roll back a task definition when a replacement fails; it cannot roll back a database schema. Database downgrade is unsupported, so restore the matching pre-upgrade snapshot if a newer migration is incompatible with the previous release.

Expected Studio message: "This installation does not have an in-product updater. Manage updates through your deployment workflow, or use the Kortyx installer for local Docker update management." This is normal for an orchestrated installation: the workflow might be CDK/ECS on AWS, Terraform or Cloud Deploy on Google Cloud, or Helm/GitOps on Kubernetes.

The High Availability guide includes a scheduled GitHub Actions pattern that proposes image updates in the infrastructure repository and keeps rollout ownership in the platform.

For backups, credential handling, and failure recovery, continue with Operations and Troubleshooting and the Configuration Reference.