Skip to content

Environment Configuration Reference (AWS)

AWS deployment environments (dev / prd, etc.) are defined in infra/config/environments.ts (copied from environments.example.ts; forks commit it). Settings resolve in this order: CLI -c flags > env entry > scale defaults > built-in defaults. Note that -c only applies to environment entry fields (scale / dbEngine / enableOpenSearch, etc.) — for site-scoped fields (domainName / allowedIpRanges / basicAuth, etc.) the -c channel is ignored in multi-site environments (preventing one -c from stamping every site at once).

The top level of the file exports the CodeConnections connection ARN (shared by all envs) and, optionally, the account the pipelines are expected to be built in:

export const connectionArn = 'arn:aws:codeconnections:...:connection/...'
export const pipelineAccount = '000000000000' // optional; validated against the active credentials

The multi-site shape (declaring sites) is the standard way to create an environment. Fields split into “environment entry” fields (shared resources + CI/CD) and “site entry” fields (site-specific settings).

Separate AWS accounts are recommended for prd vs dev (isolation of failures, permissions, and billing; a contained blast radius). Setting a distinct account per environment separates them at the account boundary.

Running multiple environments in one account is also supported (for evaluation / small / cost-conscious setups), with one caveat: deploying the same commit to dev and prd near-simultaneously can race on pushing the same tag to the CDK bootstrap image ECR repository the two environments share (image tags are a hash of the build content, so the same commit yields the same tag). The conflict is transient and self-healing — re-running the failed deploy resolves it (the already-pushed image is skipped). To avoid it for good, make that ECR repository’s tags MUTABLE, or stagger the dev and prd deploys. See docs/specs/en/phase4-deploy.md in the repository for the procedure.

The account running CI/CD (CodePipeline) is chosen independently of the target accounts. The pipeline deploys by assuming the bootstrap roles of the target account, so keeping only the pipelines in a dedicated CI/CD account and dealing dev / prd out to separate accounts is supported. For the setup this needs — bootstrapping the target accounts, where the connection lives — see docs/specs/en/phase4-deploy.md in the repository.

The top-level pipelineAccount is optional, but recommended once the accounts are separated. Left unset, the pipeline is created in whichever account the credentials running cdk deploy KukanPipeline belong to — run it from a prd session and a second pipeline grows in prd. Set, synth stops when the credentials are for another account.

Note that with separate accounts the pipeline’s artifact bucket is encrypted with a KMS customer-managed key (decided automatically), adding roughly $1 per month per key.

Settings for the shared resources (database, OpenSearch, VPC, ECS cluster) and CI/CD.

Field Type Default Description
account string required Target AWS account ID. Misdeployment guard (CDK refuses to deploy with mismatched credentials)
region string ap-northeast-1 Target region
scale small | medium | large small Deployment size (controls resource sizing as one knob)
dbEngine rds | aurora scale-dependent (small=rds, medium/large=aurora) Database engine
enableOpenSearch boolean true false → PostgreSQL full-text search fallback
cpuArchitecture arm64 | x86_64 arm64 CPU of the web and worker tasks (Graviton by default). The images are built for it (see “CPU architecture” below)
rdsCaCertificate rds-ca-rsa2048-g1 | rds-ca-rsa4096-g1 | rds-ca-ecc384-g1 rds-ca-rsa2048-g1 CA that issues the database’s server certificate, which the web and worker verify when they connect (see “Database certificate” below)
bedrock object | false enabled (Titan v2) Semantic search, AI metadata suggestions, and AI-written descriptions. false disables (AI_TYPE=none). Fields under “bedrock fields” below
githubRepo string none CodeConnections source repository (owner/repo). Envs without it get no pipeline
deployBranch string main Branch that deploys this env in pipeline mode
deployNotification boolean none Post deploy outcomes to Slack (pipeline mode only). The webhook URL goes into a secret the stack creates
overrides deep-partial none Fine-grained overrides of the scale preset (see “overrides” below)
sites SiteConfig[] none (the legacy single-site shape) The sites this environment hosts. Always declare it for new deployments (one entry is fine)
deployConcurrency number 2 Sites deployed at once after the canary. Their simultaneous rolling updates count against the database connection budget; synth fails when it does not fit
ecrImageRetention number 100 Images kept (newest first) in the CDK bootstrap ECR repository. Environments in one account share the repository, so give them the same value (synth fails otherwise)
cdnLogRetentionDays number 90 Days CloudFront access logs are kept (applies to every site in the environment)
enableExecuteCommand boolean false Every site’s default for ECS Exec; a site’s enableExecuteCommand wins (see “ECS Exec” below)
Field Type Default Description
region string the deployment region Bedrock API region
embeddingModel string amazon.titan-embed-text-v2:0 Embedding model ID. Changing it needs a redeploy and regenerated embeddings
embeddingDimensions number adapter default (1024) Embedding dimensions
vectorMinSimilarity number the model’s measured recommendation Override of the similarity floor for semantic search
completionModels string[] ['jp.anthropic.claude-haiku-5-5'] Generation models the task role may InvokeModel. Also the dashboard’s model options; the first is the default for AI metadata suggestions. Needs a redeploy
summaryModel string none (no AI-written descriptions) Model that writes AI descriptions (abstracts). Synth fails unless it is in completionModels

The web and worker tasks run on arm64 (Graviton) by default, which costs about 20% less on Fargate than x86_64. Set cpuArchitecture: 'x86_64' for an environment that should run on x86_64. It is an environment-level setting; it cannot differ per site, and -c cannot override it.

  • The pipeline builds the images on a CodeBuild of the tasks’ architecture (Tokyo and Osaka have ARM). Set x86_64 for a pipeline in a Region without ARM CodeBuild (ARM_CONTAINER)
  • A standalone deploy can build images for an architecture other than the local machine’s. Docker Desktop (Windows / Mac) needs nothing more. On Linux with Docker Engine alone, run docker run --privileged --rm tonistiigi/binfmt --install arm64 once
  • An existing environment switches over in an ordinary deploy (the tasks are replaced)

The web and worker verify the server certificate on their TLS connections to RDS / Aurora (POSTGRES_SSLMODE=verify-full). The CA bundle they check it against is in the images. In a multi-site deployment, the Lambda that creates each site’s database verifies the certificate too (against the CAs bundled with the Lambda runtime).

  • rdsCaCertificate pins the CA that issues the server certificate. Left out, the account’s default would apply, and modify-certificates can change that. The default rds-ca-rsa2048-g1 is offered in every Region; the other two are not offered in some, such as Mumbai and Zurich
  • The bundle in the images covers every Region of the commercial partition. GovCloud and China Regions publish their own bundles, which it cannot verify (unsupported)
  • On an existing environment whose instances use a CA other than rdsCaCertificate, the deploy switches the CA and the database restarts
  • It is an environment-level setting (the database is shared by the environment’s sites); -c cannot override it

Site-specific settings. They cannot appear on the environment entry (synth fails), except bedrock / overrides / enableExecuteCommand: there the environment value is every site’s default, and the site’s value goes on top of it.

Field Type Default Description
name string required Site key (^[a-z][a-z0-9]{1,15}$), used in resource names kukan-<env>-<site>-* and database kukan_<site>
brand string default (the brand brands/default) Web image brand (KUKAN_BRAND). Defaults to default; omitting it and brand: 'default' are equivalent (the brands/default brand). Any other value requires a matching apps/web/brands/<brand>/ (an unknown brand fails the image build)
albPriority number derived from the site name Listener rule priority on the shared ALB (ADR-049). Omit → a stable hash of the site name (1000–49999); set 1–999 explicitly on the site being added only when synth reports a collision
domainName string none Custom domain (defaults to the CloudFront domain)
hostedZoneId string none Route53 hosted zone ID (needed with domainName)
hostedZoneName string none Route53 hosted zone name (same)
certificateArn string auto-created in standalone mode us-east-1 ACM certificate ARN. Required in pipeline mode when domainName is set
webAclArn string auto-created in standalone mode us-east-1 WAF WebACL ARN (sharable across sites). Required in pipeline mode when WAF is on
enableWaf boolean ON (OFF when allowedIpRanges / basicAuth set) WAF managed rules on CloudFront (~$9/month)
allowedIpRanges string[] none IP restriction (CloudFront Function, IPv4 CIDR + IPv6)
basicAuth { username, password } none Basic auth gate, OR-combined with allowedIpRanges (light gate only)
enableIpv6Dns boolean ON (OFF when allowedIpRanges set) An AAAA alias for the domain, so it is reached over IPv6 too. Behind allowedIpRanges, turn it on only once the list holds the IPv6 prefixes of every place it admits (a device admitted by IPv4 would come over IPv6 and get a 403)
bucketName string auto-named S3 bucket name (globally unique auto-naming when omitted)
enableGa4DataApi boolean false GA4 access analytics dashboard
nameSiteInUserAgent boolean false Name the site’s URL in the worker’s User-Agent (USER_AGENT_URL), so operators of the servers it fetches from can reach you. Set true only on a site anyone may visit and search engines may index; synth rejects it alongside allowedIpRanges / basicAuth
enableExecuteCommand boolean the environment’s (else false) Allow ECS Exec into this site’s web and worker tasks. It may also be written on the environment entry, where it becomes every site’s default (see “ECS Exec” below)
timeZone string Asia/Tokyo IANA zone times are prerendered in; the browser then shows the viewer’s
bedrock object | false the environment’s Override of the environment’s bedrock (deep-merged; arrays replace). false turns AI off for this site alone. Synth fails when the environment has bedrock: false
overrides partial none Site-owned sections only (see “overrides” below)

If you later switch certificateArn / webAclArn to external ARNs, the previously auto-created certificate / WebACL is not deleted (RemovalPolicy.RETAIN, so deleting a resource CloudFront still references cannot fail the deploy). How you clean up afterward depends on whether any managed resource remains:

  • Only one of the certificate / WAF switched to an external ARN (the other stays auto-created): the GlobalStack remains, and only the switched resource leaves the template and is detached by RETAIN. Delete it manually if unneeded
  • Both switched (i.e. the last managed resource): needsGlobalStack=false, so the whole GlobalStack drops out of the assembly — no “remove the resource” update is ever sent, and the physical GlobalStack lingers still managing the resources. Deleting the certificate / WebACL directly here would drift the stack. Instead, after confirming RETAIN is persisted with get-template, delete the GlobalStack by physical stack name (RETAIN keeps the resources alive through the stack deletion), then delete the certificate / WebACL manually if they are genuinely unused (a WebACL keeps billing while it exists)

That retain policy is only persisted to CloudFormation by a deploy where the resource is still present in the template. So upgrading from an older version that predates this behavior and switching the ARN in the same deploy would remove the resource under its old (delete) policy and fail the deploy while CloudFront still references it. On existing environments, do it in two steps (environments created fresh on a version with this behavior carry RETAIN from the first deploy, so they can switch in one):

  1. Deploy the latest version first to persist RETAIN on the GlobalStack, then confirm it landed with aws cloudformation get-template (us-east-1) — the certificate / WebACL should show DeletionPolicy: Retain
  2. Switch the ARN in a separate later deploy

Opening a shell in the web and worker containers with aws ecs execute-command is off by default. Set enableExecuteCommand: true on the entry of the site you investigate, and deploy.

  • It is a site-level setting and applies to both the web and worker of that site. Only that site’s task roles get the ECS Exec permissions
  • Written on the environment entry, it becomes every site’s default; a value on a site entry wins. In the single-site shape (no sites), write it on the environment entry
  • -c cannot override it (the pipeline never sees -c, so a site that has it on says so in the config file)
  • A deploy changes only the service setting and the task role’s permissions, not the task definition, so it does not replace the tasks. Whether ECS Exec works is decided when a task starts
    • Turned on: tasks started after the deploy accept sessions. To use it right away, replace the tasks with aws ecs update-service --cluster <cluster> --service <service> --force-new-deployment (or wait for the next image deploy)
    • Turned off: the task role loses the ECS Exec permissions, so new sessions cannot open
  • A shell in a container can read the secret values injected as environment variables
  • Who opened a session, when, and into which task is in the CloudTrail event history (the ExecuteCommand management event, 90 days). What is typed and shown inside the session is not recorded

Fine-grained overrides of the scale preset. They apply as a deep merge — scale defaults ← env overrides ← site overrides — and the allowed range differs per level:

  • Environment entry: every section (web / worker / db / opensearch / dbPool / backup). Shared-resource sizing, DB retention, and the AWS Backup schedule are environment-level only
  • Site entry: site-owned sections only (web / worker (except healthPort) / dbPool, plus the S3 versioning settings of backup). Overriding db / opensearch / backup.awsBackup / backup.dbBackupRetentionDays is rejected at synth

For the per-scale default values, see “Scale defaults” in docs/specs/en/phase4-deploy.md in the repository.

Backward compatibility: the single-site shape

Section titled “Backward compatibility: the single-site shape”

Every site entry field except name / brand / albPriority may also be written directly on the environment entry — but only in an environment that does not declare sites. This is backward compatibility with the earlier single-site shape (the all-in-one stack) and exists for already-deployed environments.

  • Do not use this shape for new deployments — declare sites even for a single site
  • Mixing the shapes (sites plus env-level site fields) is rejected at synth (except bedrock / overrides / enableExecuteCommand, the exceptions above)
  • sites cannot be added to an already-deployed single-site-shape environment (that would replace resources; see the blue/green procedure in Multi-Site Operation)