Skip to content

Environments

A single fork can deploy more than one environment — typically a development stack and a production stack — by binding each to a GitHub Environment with its own set of variables and secrets. The workflows are identical; only the values they read differ.

Each deployable environment maps to a GitHub Environment under Settings → Environments. Variables and secrets defined on an environment override the repository-level defaults when a workflow runs against it, so the same CDK_PROJECT_PREFIX, CDK_DOMAIN_NAME, and certificate ARNs resolve to different values per environment.

Keep each environment on its own domain so they never share state or DNS. The reference deployment runs two:

EnvironmentBranchDomain
Developmentdevelopdev.boisestate.ai
Productionmainboisestate.ai

Development is a subdomain, so the TLS wildcard-depth rule applies: a *.boisestate.ai cert does not cover artifacts.dev.boisestate.ai, so the dev environment needs a us-east-1 cert covering its own dev.boisestate.ai and *.dev.boisestate.ai (which covers artifacts.dev.boisestate.ai and mcp-sandbox.dev.boisestate.ai).

Production serves at the apex boisestate.ai. The SPA sits on the bare apex, which a wildcard does not match, while the artifacts. and mcp-sandbox. origins are single-label subdomains that a wildcard does cover — so the prod cert needs both boisestate.ai and *.boisestate.ai as SANs. See the wildcard-depth note on the Platform page.

CDK normally creates the Route53 ALIAS/A records for the SPA, ALB, artifacts, and mcp-sandbox origins. It does this with HostedZone.fromLookup, which runs under the deploy account’s credentials — so it only works when the hosted zone lives in the same account as the deployment.

When the zone for an environment’s domain lives in a different AWS account (as the production boisestate.ai zone does), set CDK_MANAGE_DNS_RECORDS=false on that GitHub Environment. The stack then:

  • still attaches the custom domain + ACM cert to every CloudFront origin and the ALB listener, so each origin serves its domain over TLS, and
  • skips the in-account zone lookup + record creation that would otherwise fail cross-account, and
  • emits CfnOutputs with the record name and alias target for each origin so you can create the records by hand in the zone’s account:
OriginRecord-name outputAlias-target output
SPA{prefix}-frontend-dns-record-name{prefix}-frontend-dns-alias-target
ALB{prefix}-alb-dns-record-name{prefix}-alb-dns-alias-target
Artifacts{prefix}-artifacts-dns-record-name{prefix}-artifacts-dns-alias-target
MCP sandbox{prefix}-mcp-sandbox-dns-record-name{prefix}-mcp-sandbox-dns-alias-target

In each pair, create an ALIAS (or CNAME) at the record name pointing to the alias target. The CloudFront origins target a *.cloudfront.net domain; the ALB record targets the ALB DNS name.

Leave CDK_MANAGE_DNS_RECORDS unset (defaults to true) for any environment whose hosted zone is in the deploy account — e.g. the dev stack — and CDK manages the records automatically.

Beyond the required variables, most tuning knobs are optional and naturally differ between a dev and a prod stack:

ConcernVariables
ECS / Runtime sizingCDK_APP_API_CPU, CDK_APP_API_MEMORY, CDK_APP_API_DESIRED_COUNT, CDK_APP_API_MAX_CAPACITY, and the matching CDK_INFERENCE_API_*
CloudFrontCDK_FRONTEND_CLOUDFRONT_PRICE_CLASS (PriceClass_100 / 200 / All)
CORSCDK_CORS_ORIGINS and per-module overrides — add http://localhost:4200 to point a local SPA at a deployed environment
Frame ancestorsCDK_ARTIFACTS_EXTRA_FRAME_ANCESTORS, CDK_MCP_SANDBOX_EXTRA_FRAME_ANCESTORS — leave unset in production
NetworkingCDK_VPC_CIDR
RetentionCDK_RETAIN_DATA_ON_DELETE, CDK_ARTIFACTS_RETENTION_DAYS

CDK_RETAIN_DATA_ON_DELETE controls what happens to stateful resources when the stack is deleted. With it set to true, CloudFormation retains DynamoDB tables, S3 buckets, Cognito, secrets, and KMS keys instead of deleting them — which protects production data but means a later redeploy must reconcile those retained resources (see Upgrading from Multi-Stack). On a disposable dev stack, leaving it false lets a teardown clean up completely.

The variables above are a curated subset. For every available variable and secret — type, default, and the subsystem it tunes — see the GitHub Actions Configuration Reference, and the Configuration section for how these map onto runtime behavior.