Appearance
Static Sites
A Keel static site uses a private S3 bucket behind CloudFront. Declare it in keel.yml, provision it with keel up, and publish it with keel sites deploy. CloudFront accesses the bucket through an origin access control.
sites sits alongside services. An app can contain services, sites, or both, allowing a frontend and its API to share one config and deployment workflow.
yaml
sites:
frontend:
root: ./dist # required — where the built site lives
build: npm run build # optional — a site of committed files needs none
# build_in: codebuild # default: codebuild when pipeline.source is set, else local
spa: true # single-page app: client-side routing works on refresh
cdn:
domain: www.example.com # must sit inside the app's domain zone
# price_class: PriceClass_100
# default_ttl: 86400bash
keel sites list # configured and live state (--json)
keel sites deploy # build, publish, invalidate the CDN (--local, --skip-build, --no-delete, --dry-run)
keel sites verify frontend # check the certificate in ACM and record that it is issuedThe essentials
rootis required and identifies the directory to publish.build_incontrols whether the build runs in CodeBuild or locally. It defaults to CodeBuild whenpipeline.sourceis set and to local otherwise.spa: truemaps S3 403 and 404 responses toindex.html, allowing client-side routes such as/orders/42to load directly.cdnis required because Keel does not make the bucket public. Withoutcdn.domain, the site uses its assigned*.cloudfront.netdomain.- Certificates are created in us-east-1 as required by CloudFront.
default_ttldefaults to 86400 seconds. Keel invalidates CloudFront on deployment, but other caches may continue to honor this TTL.price_classdefaults toPriceClass_100for North America and Europe.PriceClass_Alladds more regions at a higher per-GB rate.
Where the build runs
build_in is codebuild or local.
When omitted, build_in defaults to codebuild if pipeline.source names a repository and to local otherwise. keel up, keel deploy, and keel sites deploy print the selected location before building:
frontend: building in codebuild — pipeline.source is githubIn CodeBuild, the build runs from a clone of the selected commit and uploads the output from AWS. The local machine does not need the build toolchain or upload bandwidth, and the deployment can run in CI. root must point to a directory inside the repository.
Locally, the build runs in your project root and the files are uploaded from your disk, comparing each one's content hash so only what changed is sent.
You can set build_in: local even when a source is configured, or pass keel sites deploy --local for one deployment. A site configured for local builds has no CodeBuild project, so there is no flag to force the reverse override.
What a CodeBuild site build is
Each site has its own CodeBuild project, separate from service image builds. It uses a site-specific buildspec without a privileged Docker daemon, and IAM access is scoped to the site's buckets. Keel retains responsibility for CloudFront invalidation and distribution changes.
Local and CodeBuild uploads apply the same Content-Type and Cache-Control rules. Assets are uploaded before HTML, and old files are deleted only after the new files are present. This keeps the previous build readable until the new entry points are published.
CodeBuild deployments always invalidate CloudFront because the remote build does not compare output hashes with the previous deployment.
Preflight
Git preflight checks apply only to sites built in CodeBuild, which clones a specific commit. Local-only site deployments do not require a repository or run these checks.
Sites and services together
An API on ECS and a frontend on CloudFront can share one keel.yml, hosted zone, and deployment:
yaml
name: shopfront
region: eu-west-1
services:
api:
type: web
port: 3000
health_check: /healthz
sites:
frontend:
root: ./dist
build: npm run build
spa: true
cdn:
domain: www.example.com
domain:
name: app.example.com # the load balancer
zone: example.com
dns: route53
pipeline:
source: github
repo: myorg/shopfrontkeel deploy rolls out services before publishing sites, inside the same deploy lock. CodeBuild uses the same commit for both. Use keel deploy frontend or keel deploy api to deploy one target.
The load balancer and site require separate hostnames, such as app.example.com and www.example.com. Keel rejects a site that reuses the application's hostname because the DNS alias would conflict. Routing both through one CloudFront distribution is not yet supported.
Sites-only infrastructure
When an app declares no services, Keel omits ECS, the load balancer, VPC, NAT gateway, and ECR resources. keel cost excludes those resources from the estimate.
Keel rejects a database, cache, or waf in a sites-only app because those features require service networking and, for WAF, a load balancer.
CodeBuild runs in an AWS-managed network and does not require a VPC or NAT gateway. A sites-only app can therefore build in AWS without provisioning service infrastructure:
yaml
name: docsite
region: us-east-1
sites:
www:
root: ./dist
build: npm run build
spa: true
cdn: {}
pipeline:
source: github
repo: myorg/docsitekeel cost shows a site build as its own CodeBuild (sites) line — separate from the image builds, which may run on a larger (and twice as expensive) instance.
Custom domains for sites
cdn.domain must sit inside the zone the top-level domain: block declares — Keel creates no second zone and derives none by stripping labels. With domain.dns: external, run keel domains records for the DNS entries to create by hand, and keel sites verify once the certificate validates. See Custom Domains & WAF.
Limits
- No build cache in CodeBuild. Every build reinstalls its dependencies, which for a Node front end is most of the wall clock.
- No build-time environment variables for a CodeBuild site build, so a build-time API URL has to come from the repository.
- The site build runs on
BUILD_GENERAL1_SMALL(3 GB), and there's no key to raise it. A very large front end can exhaust it — the failure reads asJavaScript heap out of memory. - No per-pattern
Cache-Control. Hashed asset filenames deserveimmutable, but guessing which files are hashed would cache a non-hashed one for a year.