Skip to content

Feat/elastic beanstalk heroku#106

Open
azpaulp wants to merge 12 commits into
awslabs:mainfrom
azpaulp:feat/elastic-beanstalk-heroku
Open

Feat/elastic beanstalk heroku#106
azpaulp wants to merge 12 commits into
awslabs:mainfrom
azpaulp:feat/elastic-beanstalk-heroku

Conversation

@azpaulp

@azpaulp azpaulp commented Jul 2, 2026

Copy link
Copy Markdown

Summary

Add AWS Elastic Beanstalk (Docker platform, AL2023) as the default compute target for Heroku dyno migrations. Fargate and EKS remain as user-selectable overrides via Q12c.

This preserves the PaaS-to-PaaS model: Heroku users push code, EB handles builds/deploys/scaling/patching — the same operational experience they had on Heroku.

  • Elastic Beanstalk (default) — managed platform, git push to deploy via CodePipeline
  • Fargate (override) — direct container control, pay-per-second
  • EKS (override) — full Kubernetes orchestration

Key design decisions

  1. Field naming: Kept design_constraints.kubernetes with "elastic_beanstalk" as a new valid value (alongside existing "ecs-fargate", "eks-managed", "eks-or-ecs"). Avoids a breaking schema rename across 10+ files and in-progress migration runs. A rename to design_constraints.compute_target is planned as a follow-up PR with a migration path.

  2. Worker mapping: Heroku workers are persistent processes (Sidekiq, Celery), NOT HTTP-triggered SQS consumers. They map to EB SingleInstance WebServer tier (Docker CMD runs continuously), not EB Worker tier. Horizontal worker scaling requires the Fargate override path.

  3. Terraform generation: beanstalk.tf (EB app/environments/IAM) + pipeline.tf (CodePipeline from GitHub for auto-deploy). CodePipeline mirrors Heroku's git push deploy model.

  4. Security model: App SG uses VPC CIDR ingress (EB manages its own ALB with its own SG internally).

  5. Pricing: EB cost = EC2 instances + ALB (for LoadBalanced). EC2 instance rates added to aws-infra-pricing.json. Decomposed in estimate via existing EC2/ALB formulas.

  6. Sizing data: Extracted to knowledge/design/dyno-eb-sizing.json following the established pattern (dyno-fargate-sizing.json, eks-pod-sizing.json). Declared in _knowledge frontmatter with appropriate _when condition.

Changes by phase

Phase Files What changed
Clarify clarify-interview.md Q12c renamed to "Compute target preference", EB as default option A
Clarify clarify-assemble.md "elastic_beanstalk" added to valid values
Design design.md _knowledge entry for EB sizing, lookup table updated
Design design-mapping.md EB branch (default), Fargate branch (override), fir_intent path fix
Design design-assemble.md Route gate accepts EB entries
Design dyno-eb-sizing.json New structured sizing data file
Estimate estimate-cost-engine.md EB formula, EB cost calculation section, savings plans for EB
Estimate estimate-defaults.json eb_environment log volume, EB in savings plan targets
Generate generate-terraform.md Step 5.5 (beanstalk.tf + pipeline.tf), SG rename to app
Generate generate-docs.md has_beanstalk definition, Phase 3 EB deploy, artifact table
Generate generate.md Gate check includes beanstalk.tf
Shared aws-infra-pricing.json EC2 instance pricing data
Skill SKILL.md Philosophy, triggers, error handling, file tree

Test plan

  • mise run build passes locally (frontmatter validation, tsc, fmt:check, security)

Known limitations

  • solution_stack_name is hardcoded (v4.4.0) — should use a data source in production; documented as a template customization point
  • CodePipeline requires manual GitHub connection setup in AWS Console (documented in MIGRATION_GUIDE)
  • Workers are limited to 1 instance (SingleInstance); horizontal worker scaling requires Fargate override
  • t3.micro (eco/basic dynos) is marginal for Docker — documented with a warning note in sizing JSON

azpaulp added 10 commits July 1, 2026 13:48
Change Heroku dyno migration default from Fargate to Elastic Beanstalk
(Docker platform, AL2023). This preserves the PaaS-to-PaaS model: Heroku
users push code, EB handles builds/deploys/scaling/patching.

Fargate and EKS remain as user-selectable overrides via Q12c (renamed
from "Kubernetes preference" to "Compute target preference").

Hierarchy:
- Elastic Beanstalk (default) — managed platform like Heroku
- Fargate (override) — direct container control
- EKS (override) — full Kubernetes orchestration

Includes dyno-to-EC2 instance type mapping table, EB environment type
routing (web → LoadBalanced, worker → Worker+SQS), and backwards-
compatible fallback from compute_target to kubernetes preference field.
Fix all blocking issues identified during review:

Critical fixes:
- Revert compute_target rename back to kubernetes field (add
  "elastic_beanstalk" as new valid value alongside existing values)
- Update clarify-assemble.md validation to accept "elastic_beanstalk"
- Update design-assemble.md route gate to accept EB entries
- Add full EB Terraform generation (beanstalk.tf + pipeline.tf with
  CodePipeline for GitHub auto-deploy)
- Add EB cost estimation formula (EC2 + ALB decomposition)

High-priority fixes:
- Update design.md conditional loading table for EB path
- Update design-eks.md skip condition to include "elastic_beanstalk"

Low-priority text fixes:
- Update Q12b/Q11 Fargate references to be compute-target-agnostic
- Update SKILL.md error handling and file tree descriptions
- Update eks-mapping-table.md usage context
Critical fix:
- Rename aws_security_group.fargate → aws_security_group.app (shared
  across EB/Fargate/EKS compute targets). Add SecurityGroups option
  setting to beanstalk.tf so EB instances can reach DB/Cache/MSK.

Estimate fixes:
- Compute Savings Plans now triggers for both EB and Fargate
- Add EC2 Spot opportunity for EB worker environments
- Fargate Spot limited to Fargate override path
- Optimized tier description updated for EB

Documentation:
- design.md sub-files description mentions EB
- EKS description says "instead of default EB path"
Critical:
- MIGRATION_GUIDE Phase 3 now branches on compute target: EB path
  shows CodePipeline auto-deploy + manual CLI deploy; Fargate path
  preserved as override with original ECR/ECS flow

High:
- README artifact table uses correct filenames (beanstalk.tf,
  pipeline.tf, compute.tf, database.tf, cache.tf, messaging.tf,
  security.tf) with has_beanstalk/has_fargate conditionals
- Fix fir_intent JSON path: preferences.global.fir_intent (not
  preferences.operational.fir_intent) per clarify-assemble schema

Medium:
- Containerization Prerequisites: "for AWS deployment" (not "for
  Fargate deployment")
- Validation checklist: ECS Express Mode conditional on has_fargate;
  CodePipeline section conditional on has_beanstalk
- Q11 option A: mentions "Elastic Beanstalk or Fargate, per Q12c"
- CloudWatch Logs exception acknowledges both beanstalk.tf and
  compute.tf as valid compute config locations
- Phase 4 health check: EB environment URL vs ALB endpoint

Low:
- SKILL.md sizing example: t3.small for EB (not 0.5 CPU for Fargate)
…pipeline

Critical:
- Define has_beanstalk and has_fargate detection flags in generate-docs.md
  (Step 0 detection table + Conditional Section Rules)

Medium-High:
- Fix worker tier: Heroku workers are persistent processes (Sidekiq,
  Celery), NOT SQS-triggered. Map to SingleInstance WebServer tier
  (Docker CMD runs continuously) instead of EB Worker tier
- pipeline.tf now deploys to ALL environments (one action per env),
  not just web

Medium:
- estimate-defaults.json: add eb_environment log volume, add
  "Elastic Beanstalk" to savings plan targets, add ec2_spot entry
- generate.md gate check includes beanstalk.tf and pipeline.tf
- beanstalk.tf template updated for SingleInstance worker environments
  (tier = WebServer for all, no Worker tier used)
…ions

Critical:
- Add ec2.instances pricing data to aws-infra-pricing.json (t3.micro
  through m5.4xlarge + r5.xlarge) matching the EB dyno-to-instance table

Medium:
- Security group: app SG ingress uses VPC CIDR instead of referencing
  standalone ALB SG (EB manages its own ALB with its own SG)
- ALB SG marked as Fargate-override-only with skip note
- ECS IAM roles marked as Fargate-override-only with skip note
- Remove SQS from EB cost formula (workers are persistent processes,
  not SQS-triggered)
- Add eb_environment to log volume prose enumeration
- Fix Q5b conditional rules: "Migration approach | Postgres present"
  (was incorrectly "Migration urgency | Always")
- Remove batch size constraint text (was <=5, actual batches are larger)

Low:
- Add {{EB_ENVIRONMENT_URL}} to template variable resolution table
- Fix health check to use correct endpoint per compute target
Three issues that would cause terraform validate failure or incorrect
deployment:

1. Add vpc_cidr to always-emitted global variables (security.tf
   references it unconditionally, but it was only defined in vpc.tf
   Path B). Now always present with note on how to populate per path.

2. Add EnvironmentType setting to beanstalk.tf template (LoadBalanced
   for web, SingleInstance for worker). Without this, EB defaults to
   SingleInstance and web dynos get no ALB. Also adds basic health
   reporting for workers (no HTTP endpoint to check).

3. Add PROCESS_TYPE env var per environment so the Dockerfile
   entrypoint can branch on process type. Includes documentation
   with example entrypoint.sh showing how to mirror Heroku's Procfile
   model in a single Docker image deployed to multiple environments.
…ix SingleInstance scaling

1. Extract dyno-eb-sizing.json following the established pattern
   (Fargate has dyno-fargate-sizing.json, EKS has eks-pod-sizing.json).
   Includes _comment, _match, _on_not_found, _environment_type, and
   _scaling metadata. design-mapping.md now references the JSON file
   instead of an inline markdown table.

2. Fix _knowledge _when condition: Fargate sizing now loads only when
   "ecs-fargate" (removed "or absent" since absent = EB default).
   Added EB sizing entry with "elastic_beanstalk or absent" condition.

3. Fix SingleInstance max_instances: always 1 (EB SingleInstance cannot
   auto-scale). If source formation has quantity > 1 for workers, emit
   a warning directing users to the Fargate override for horizontal
   worker scaling.
@azpaulp azpaulp requested a review from a team as a code owner July 2, 2026 13:34
azpaulp added 2 commits July 2, 2026 09:50
Revert two changes that fixed pre-existing issues but don't belong in
this PR:
- Q5b: restore original "Migration urgency | Always" label
- Batch constraint: restore "(≤5 each)" text
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant