Two paths are provided:
deploy/terraform/ — Infrastructure-as-Code (ECS Fargate + ALB + Secrets Manager +
IAM + CloudWatch). Costs nothing until terraform apply — ideal to showcase the
cloud architecture in a portfolio without any billing. See
deploy/terraform/README.md.Zero-cost portfolio tip: keep the Terraform module as reviewable IaC (don’t apply), or apply it briefly, screenshot the running app, then
terraform destroy(a few cents). Either way the code demonstrates the AWS competency.
AWS App Runner is closed to new customers (announced 2026). Only accounts that already used App Runner can create services. AWS’s recommended, App Runner-equivalent replacement is Amazon ECS Express Mode: one command provisions a full stack (Fargate + Application Load Balancer + auto-scaling + networking) and gives you an HTTPS URL. There is no extra charge for Express Mode — you pay only for the underlying resources (Fargate task + ALB).
If your AWS account is an existing App Runner customer and you prefer App Runner, the only difference is the final step: replace
create-express-gateway-servicewith anapprunner create-servicecall pointing at the same ECR image and secret. Ask and we’ll swap it.
# macOS
brew install awscli
aws configure # access key, secret, default region eu-west-3
The IAM user/role needs permissions for ECR, ECS, IAM, and Secrets Manager.
AQ... or AIza...).ecsTaskExecutionRole and ecsInfrastructureRoleForExpressServices.
(The script below creates the execution role if missing, but the infrastructure role
must exist first.)Region used throughout: eu-west-3 (Paris) — App Runner and ECS Express Mode are available there, keeping data and latency in France.
export GOOGLE_API_KEY=AQ...your_real_key # never commit this
AWS_REGION=eu-west-3 ./deploy/deploy-ecs-express.sh
The script:
medical-imaging-agent/google-api-key).--platform linux/amd64, pushes it./_stcore/health,
1 vCPU / 2 GB, autoscale 1→2 tasks).Provisioning takes ~3–5 min. Get the URL:
aws ecs describe-express-gateway-service --service-name medical-imaging-agent \
--region eu-west-3 --query 'service.url' --output text
Tunable via env vars: APP_NAME, CPU, MEMORY, MIN_TASKS, MAX_TASKS,
MODEL_ID, ENABLE_WEB_SEARCH, DEFAULT_LANGUAGE.
After the first deploy exists, .github/workflows/deploy-ecs.yml
rebuilds and redeploys the image. It is workflow_dispatch (manual) by default;
uncomment the push trigger for auto-deploy on every push to main.
One-time setup:
github-actions-ecs-role
trusting your repo, with ECR + ECS Express permissions
(guide).AWS_REGION=eu-west-3, AWS_ACCOUNT_ID, ECR_REPOSITORY=medical-imaging-agent,
ECS_SERVICE=medical-imaging-agent.GOOGLE_API_KEY from Secrets Manager into the task
(secrets → valueFrom), so the key never appears in the task definition env or the repo.environment-variables (less secure — visible
in the task def).Rotate the key anytime:
aws secretsmanager put-secret-value --secret-id medical-imaging-agent/google-api-key \
--secret-string AQ...new_key --region eu-west-3
MIN_TASKS=0 is not supported for an always-on web app; to avoid idle cost for a
demo, tear down when unused (below).Gemini API is billed separately by Google (free tier: ~20 requests/day on gemini-flash-latest;
enable billing in Google AI Studio for production).
aws ecs delete-express-gateway-service --service-name medical-imaging-agent --region eu-west-3
aws ecr delete-repository --repository-name medical-imaging-agent --force --region eu-west-3
aws secretsmanager delete-secret --secret-id medical-imaging-agent/google-api-key \
--recovery-window-in-days 7 --region eu-west-3