ECS Fargate
ECS Fargate is the deploy target for a long-running webapi that wants to run as a container rather than a Lambda. Set deployTarget: "ecs-fargate" on the entry and kanject aws deploy builds the image, registers a task definition, and promotes it through CloudFormation. Deploy, rollback, and previews work the same way they do for Lambda — a different substrate, the same workflow.
- Turn an entry into an ECS Fargate service (
deployTarget,webapi,image) - Configure the service — cpu/memory, autoscaling, ALB, IAM — in the stage file
- Understand how a deploy promotes a task definition, and how rollback works
- Know what's managed for you vs referenced, and the MVP constraints
How it works: two planes
CloudFormation owns the durable infrastructure — the cluster, service, load balancer, IAM roles, and log groups. Each deploy produces an immutable artifact — a digest-pinned image and a registered task-definition revision — and promotes it by swapping a single stack parameter (TaskDefinitionArn). So a deploy is a CloudFormation change set, and a rollback is the same change set with the previous revision. The stack stays the source of truth; the task definition is the thing that changes.
Make an entry ECS Fargate
An ECS service is exactly one entry, running one primary application container. doctor enforces the shape: runtimeMode must be webapi and packageType must be image.
// manifest.json → service.entryProjects[]{ "id": "web", "projectPath": "src/Acme.Web/Acme.Web.csproj", "runtimeMode": "webapi", "packageType": "image", "deployTarget": "ecs-fargate", "build": { "kind": "container", "mode": "sdk-publish", "containerPort": 8080 }} deployTarget—"ecs-fargate"(omit, or"lambda", for the Lambda path).packageType—"image"is required; the service runs a container.build— how the image is produced:mode: "sdk-publish"runsdotnet publish -t:PublishContainer(no Docker daemon), or"dockerfile"builds aDockerfile.containerPortdefaults to8080.
Configure the service
The region-agnostic service shape lives in stages/<stage>.json → ecs:
// stages/<stage>.json"ecs": { "cpu": 512, "memory": 1024, "desiredCount": 2, "autoscaling": { "min": 2, "max": 10, "targetCpuPercent": 60 }, "circuitBreaker": { "enable": true, "rollback": true }, "alb": { "scheme": "public", "healthCheckPath": "/healthz", "port": 443 }, "assignPublicIp": false, "taskRolePolicies": [], "execRolePolicies": []} cpu/memory/desiredCount(required) — Fargate task size and the baseline task count.autoscaling—min,max,targetCpuPercentfor target-tracking; omit to holddesiredCount.circuitBreaker—enable+rollback; ECS rolls a failed deployment back to the last good task definition.alb—scheme(public/internal),healthCheckPath,port.assignPublicIp,taskRolePolicies(your app's AWS permissions),execRolePolicies(image pull, secret read, logs).
Per-region references
Region-scoped resources — VPC, subnets, certificate, hosted zone, and an optional existing cluster — attach per target under targets[].ecs. They are referenced (Kanject diagnoses them with KANCLI400–404 if missing, but never creates them).
// stages/<stage>.json — per-region references for an ECS target"targets": [ { "region": "eu-west-2", "ecs": { "cluster": { "ref": "arn:aws:ecs:eu-west-2:111122223333:cluster/shared" }, "vpc": "vpc-0a1b2c3d", "subnets": ["subnet-1111", "subnet-2222"], "certificate": "arn:aws:acm:eu-west-2:111122223333:certificate/abc", "hostedZone": "Z0123456789" } }] Deploy, roll back, preview
A deploy builds the image → pushes to ECR (pinned by digest, never a mutable tag) → registers a task definition → creates and executes a CloudFormation change set that points the service at the new revision → waits for steady state. The first deploy runs a two-phase bootstrap (foundation resources, then the service); later deploys just swap the parameter.
# Deploy: build the image, register a task definition, promote via CloudFormationkanject aws deploy --env prod# Roll back to the previous task definition (a stack update — minutes, not seconds)kanject aws rollback --env prod Managed vs referenced
- Managed (CloudFormation creates): the ECS service, ALB + target group + listener, task & execution IAM roles, log groups, security groups, autoscaling. The cluster is managed by default, or you reference an existing one.
- Referenced (must pre-exist): VPC + subnets, the ACM certificate, and the Route 53 hosted zone. Missing references are diagnosed before any change set runs.
Constraints (MVP)
- One entry — one primary application container — per ECS service. User-declared sidecars are a follow-up.
- Rolling deploys with the ECS circuit breaker. Blue/green and canary traffic shifting are a separate proposal.
- Migrating an existing Lambda to ECS creates a new, target-qualified stack and leaves the Lambda stack standing — opt in with
--replace-target(refused when the stage carries anapi.domain, to protect DNS).
Guidelines
- Set a real
healthCheckPath. The ALB and the steady-state wait both depend on it — a service that never reports healthy times out (KANCLI408). - Set
autoscalingfor anything production.desiredCountalone is a fixed floor; target-tracking handles load. - Keep secrets as references. Kanject resolves them describe-only and emits
valueFrom— secret material never enters the CLI process or the CloudFormation template. - Expect a Fargate preview to cost more than a Lambda preview. It's an always-on task, not a scale-to-zero function.
- Use
--replace-targetdeliberately. It acknowledges the old Lambda stack is now orphaned; the diagnostic names it for you to remove.
- Set
deployTarget: "ecs-fargate"(withwebapi+image) to run an entry as a container service. - CloudFormation owns the durable infra; each deploy registers a digest-pinned task definition and promotes it by a change-set parameter swap.
- Configure the service in
stages/<stage>.json → ecs; reference VPC / cert / zone per region undertargets[].ecs. - Rollback re-promotes the previous task definition (minutes, preflighted). One entry per service, rolling deploys, in MVP.