Appearance
Configuration (keel.yml)
Everything Keel manages is described by one file at the root of your project. This page covers the anatomy; see the keel.yml Reference for every field, type, default, and validation rule.
A complete example
yaml
# keel.yml
name: my-api
region: us-east-1
mode: advanced # basic | advanced | custom
services:
web:
type: web # web | worker | scheduled
port: 8080
health_check: /health # or a block, see below
command: ["bundle", "exec", "puma"] # overrides the image CMD
cpu: 256 # 256 | 512 | 1024 | 2048 | 4096
memory: 512
desired_count: 2
autoscaling:
min: 2
max: 10
target_cpu: 60
environment: # merged into every service
RAILS_ENV: production
secrets: # added to every service, read from SSM
- SECRET_KEY_BASE
release: # runs once per deploy, before any service takes traffic
command: bundle exec rails db:migrate
logs:
retention_days: 365 # default 14; -1 never expires
database:
engine: postgres # postgres | mysql | aurora-postgres | aurora-mysql
version: "16"
instance: db.t4g.micro
storage: 20
multi_az: true
retain: true # deletion protection + final snapshot (default)
backup_retention_days: 7
name: appdb # default keeldb
username: appuser # default keeluser
cache:
engine: valkey # valkey | redis
node_type: cache.t4g.micro
domain:
name: api.example.com
zone: example.com
waf:
enabled: true
pipeline:
source: github # github | codecommit
repo: myorg/my-api # required for github; optional for codecommit
branch: main
# connection_arn: # override the org's CodeConnections connection
tags: # applied to every managed AWS resource
team: platform
cost-center: eng
settings: # project-level CLI behavior
verbose: false
cautious: falseOptional sections (database, cache, domain, waf, resources, environments) can be omitted entirely.
Services
Each entry under services: becomes an ECS Fargate service. Three types exist:
web— receives traffic from the load balancer. Requiresportand a health check. Only onewebservice can share a load balancer today; path-based routing is not implemented, so a second one is rejected at validation rather than provisioned with a target group nothing routes to.worker— no load balancer, no port; runs continuously.scheduled— runs on a schedule instead of continuously.
command
command overrides the image's CMD, so one image can run a web process and a worker process. Without it every service runs the same default command — and a worker silently boots a second copy of the web server.
It is a list of arguments, not a shell line. The scalar form is split on whitespace, but shell syntax (&&, |, $, redirection) is rejected: the container command is PID 1, and a shell there does not forward SIGTERM, so every task would be hard-killed on every deploy. Use the list form when you need a shell: ["sh", "-c", "first && second"].
Health checks
health_check accepts a path, or a block when the defaults do not fit:
yaml
services:
web:
health_check:
path: /up
grace_period: 90 # seconds before ECS may replace a new task (default 60)
interval: 30
timeout: 5
healthy_threshold: 3
unhealthy_threshold: 3
matcher: "200-299"
deregistration_delay: 30grace_period is the one worth knowing about
ECS defaults the health-check grace period to 0, which makes a task eligible for replacement the moment it registers — so an app that takes 20–40 seconds to boot can be killed before it ever serves a request, and then loops. Keel defaults it to 60 seconds, and every service gets a deployment circuit breaker, so a bad deploy rolls back instead of restarting forever.
Shared environment and secrets
Anything shared by every service belongs at the top level rather than repeated:
yaml
environment: # merged into every service
RAILS_ENV: production
APP_HOST: app.example.com
secrets: # added to every service
- SECRET_KEY_BASE
services:
web:
environment:
LOG_LEVEL: debug # a service-level value wins over the app-level onePrecedence, lowest to highest: app base → app environment override → service base → service environment override.
Settings
Two project-level behaviors change how commands communicate, persisted under settings: and shared across every environment. Command-line flags override them for a single invocation.
- verbose — narrate every action a command takes as it happens (
→ Triggering CodeBuild — my-api-prod-web-build). - cautious — preview the bulleted list of actions a command will take and ask for confirmation before it changes any AWS state.
bash
keel settings # list current values
keel settings set cautious true
keel deploy --verbose # override for one runWhere to go next
- Deployments — pipeline sources, the deploy flow, rollback
- Environments — staging/production overrides and how they merge
- AWS Resources & Runtime Access — S3/SNS/SQS bindings, databases, caches
- keel.yml Reference — every field with types and defaults