Deploying a Hedgerules site requires five stages. Stages 1 and 4 are initial setup (CloudFormation); stages 2, 3, and 5 run on every content update.

Stage Object Notes
1. CloudFormation stack S3 content bucket Static file storage; no website hosting needed
S3 bucket policy Grants CloudFront OAC read access
Origin Access Control Grants CloudFront access to S3
ACM Certificate DNS validation for custom domain
ACM validation DNS record CNAME proving domain ownership; can be Route53 or out-of-band
CloudFront KVS: redirects Empty store; entries managed in stage 3
CloudFront KVS: headers Empty store; entries managed in stage 3
IAM deployment group Permissions for S3, CloudFront, KVS, and Function APIs
2. hugo _hedge_redirects.txt Alias redirects; hedgeredirects output format
_hedge_headers.json Path–header map; hedgeheaders output format
Directory structure Scanned by hedgerules to produce /path/path/ redirects
Site files (HTML, CSS, JS, images) Used by stage 5
3. hedgerules deploy Viewer-request function Created on first deploy, code updated on each subsequent deploy
Viewer-response function Created on first deploy, code updated on each subsequent deploy
Redirects KVS entries Convergent sync against KVS from stage 1
Headers KVS entries Convergent sync against KVS from stage 1
4. CloudFormation stack CloudFront Distribution References S3 bucket from stage 1 and functions from stage 3
Route53 DNS record Alias to distribution
5. hugo deploy S3 objects Uploaded to bucket from stage 1
CloudFront cache invalidation Invalidates distribution from stage 4

We recommend CloudFormation for stages 1 and 4, but anything that can deploy resources to AWS works fine: the AWS web console, the aws commandline program, Terraform, etc. For the purposes of most of our docs, we assume CloudFormation.

CloudFormation cannot own the CloudFront Functions because hedgerules deploy updates their code on every deploy, which would cause stack drift. The same is true for any other declarative approach like Terraform. The KVS resources are safe in CloudFormation because CloudFormation does not track individual KVS entries.

The Distribution is in a separate stack (stage 4) because it references the function ARNs, which don’t exist until hedgerules deploy creates them in stage 3.

Out-of-band DNS#

The table above includes DNS records in stages 1 and 4, but you may want to manage DNS outside of your CloudFormation stacks. Reasons to do this include:

  • Credential separation. DNS changes can redirect all traffic for a domain. Keeping DNS credentials separate from deployment credentials limits the blast radius of a compromised deploy pipeline.
  • Different AWS account. The DNS zone may live in a shared infrastructure account while the site resources live in an application account.
  • External DNS provider. The domain may be hosted outside of Route53 entirely.

When DNS is out of band, the two DNS-related items become manual steps:

  1. Stage 1: After CloudFormation creates the ACM certificate, look up the validation CNAME (aws acm describe-certificate --certificate-arn <ARN>) and add it to your DNS provider. CloudFormation will pause until validation completes.
  2. Stage 4: After CloudFormation creates the distribution, CNAME your domain to the DistributionDomainName output.

The infra/prod/ templates use this approach: they create the ACM certificate but expect DNS records to be managed externally.

Initial setup#

# Stage 1: base infrastructure
aws cloudformation deploy \
  --template-file infra.cfn.yml \
  --stack-name mysite-infra \
  --capabilities CAPABILITY_NAMED_IAM

# Stage 2: build
hugo

# Stage 3: create functions + sync edge data
hedgerules deploy --site public/

# Stage 4: distribution (references functions from stage 3)
aws cloudformation deploy \
  --template-file distribution.cfn.yml \
  --stack-name mysite-distribution

# Stage 5: upload content
hugo deploy --target mysite

See examples/micahrlweb/ for a complete CloudFormation template.

Each content update#

hugo                              # Stage 2
hedgerules deploy --site public/  # Stage 3
hugo deploy --target mysite       # Stage 5

The order matters:

  1. hugo must run first because it generates _hedge_redirects.txt, _hedge_headers.json, and the directory structure that hedgerules deploy reads.
  2. hedgerules deploy should probably run before hugo deploy so that redirects and headers are in place before new content goes live. This avoids a window where new pages are served without their headers or where old redirects point to not-yet-uploaded content.
  3. hugo deploy runs last, uploading content to S3 and invalidating the CloudFront cache.

Re-running hedgerules deploy is always safe. The KVS sync is convergent: it computes a diff against the existing state and only applies changes.