grada-run 0.0.2 → 0.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/deploy-docs.yml +37 -0
- package/.github/workflows/iac-validation.yml +303 -0
- package/.github/workflows/publish.yml +68 -0
- package/.github/workflows/sync-bedrock-models.yml +57 -0
- package/.github/workflows/test.yml +43 -0
- package/.muserules +31 -0
- package/LICENSE +21 -0
- package/README.md +193 -3
- package/apps/docs/.astro/collections/docs.schema.json +644 -0
- package/apps/docs/.astro/content-assets.mjs +4 -0
- package/apps/docs/.astro/content-modules.mjs +4 -0
- package/apps/docs/.astro/content.d.ts +179 -0
- package/apps/docs/.astro/data-store.json +1 -0
- package/apps/docs/.astro/dev.json +14 -0
- package/apps/docs/.astro/settings.json +5 -0
- package/apps/docs/.astro/types.d.ts +2 -0
- package/apps/docs/astro.config.mjs +97 -0
- package/apps/docs/package.json +17 -0
- package/apps/docs/src/content/docs/adrs/0001-s3-native-state-locking.md +37 -0
- package/apps/docs/src/content/docs/adrs/0002-eject-mechanism-pure-iac.md +39 -0
- package/apps/docs/src/content/docs/adrs/0003-sync-ai-context-strategy.md +48 -0
- package/apps/docs/src/content/docs/adrs/0004-iac-driven-diagnostic-context.md +37 -0
- package/apps/docs/src/content/docs/adrs/0005-ecs-fargate-alb-runtime-target.md +38 -0
- package/apps/docs/src/content/docs/adrs/0006-github-oidc-no-stored-keys.md +37 -0
- package/apps/docs/src/content/docs/adrs/0007-framework-detection-with-fallback.md +37 -0
- package/apps/docs/src/content/docs/adrs/0008-secrets-names-in-git-values-in-aws.md +37 -0
- package/apps/docs/src/content/docs/adrs/0009-regenerate-with-backup-on-rerun.md +37 -0
- package/apps/docs/src/content/docs/adrs/0010-advisory-only-security-scans.md +37 -0
- package/apps/docs/src/content/docs/cli/add.md +84 -0
- package/apps/docs/src/content/docs/cli/apply.md +32 -0
- package/apps/docs/src/content/docs/cli/db.md +200 -0
- package/apps/docs/src/content/docs/cli/destroy.md +31 -0
- package/apps/docs/src/content/docs/cli/diagnose.md +37 -0
- package/apps/docs/src/content/docs/cli/doctor.md +28 -0
- package/apps/docs/src/content/docs/cli/domain.md +57 -0
- package/apps/docs/src/content/docs/cli/drift.md +40 -0
- package/apps/docs/src/content/docs/cli/eject.md +29 -0
- package/apps/docs/src/content/docs/cli/exec.md +49 -0
- package/apps/docs/src/content/docs/cli/gc.md +37 -0
- package/apps/docs/src/content/docs/cli/init.md +72 -0
- package/apps/docs/src/content/docs/cli/logs.md +39 -0
- package/apps/docs/src/content/docs/cli/rollback.md +51 -0
- package/apps/docs/src/content/docs/cli/secrets.md +73 -0
- package/apps/docs/src/content/docs/cli/sleep.md +53 -0
- package/apps/docs/src/content/docs/cli/status.md +34 -0
- package/apps/docs/src/content/docs/cli/sync-ai.md +27 -0
- package/apps/docs/src/content/docs/guides/architecture.md +87 -0
- package/apps/docs/src/content/docs/guides/aws-credentials.md +72 -0
- package/apps/docs/src/content/docs/guides/background-workers.md +45 -0
- package/apps/docs/src/content/docs/guides/cicd-pipeline.md +64 -0
- package/apps/docs/src/content/docs/guides/database-connections.md +64 -0
- package/apps/docs/src/content/docs/guides/docker-compose.md +37 -0
- package/apps/docs/src/content/docs/guides/dockerfiles.md +46 -0
- package/apps/docs/src/content/docs/guides/ephemeral-pr-previews.md +39 -0
- package/apps/docs/src/content/docs/guides/examples.md +50 -0
- package/apps/docs/src/content/docs/guides/frameworks.md +88 -0
- package/apps/docs/src/content/docs/guides/headless.md +75 -0
- package/apps/docs/src/content/docs/guides/quickstart.md +52 -0
- package/apps/docs/src/content/docs/guides/rerun-init.md +43 -0
- package/apps/docs/src/content/docs/guides/secrets-management.md +83 -0
- package/apps/docs/src/content/docs/guides/understanding-your-bill.md +63 -0
- package/apps/docs/src/content/docs/index.mdx +103 -0
- package/apps/docs/src/content/docs/migrations/astro-vercel-to-aws.md +55 -0
- package/apps/docs/src/content/docs/migrations/heroku-procfile-to-aws.md +41 -0
- package/apps/docs/src/content/docs/migrations/nextjs-vercel-to-aws.md +51 -0
- package/apps/docs/src/content/docs/migrations/sveltekit-vercel-to-aws.md +63 -0
- package/apps/docs/src/content/docs/roadmap.md +97 -0
- package/apps/docs/src/content/docs/testing-strategy.md +32 -0
- package/apps/docs/src/content.config.ts +7 -0
- package/apps/docs/src/custom.css +14 -0
- package/apps/docs/tsconfig.json +6 -0
- package/bin/cli.js +140 -0
- package/package.json +105 -7
- package/scripts/sync-bedrock-models.js +22 -0
- package/scripts/test-iac.js +261 -0
- package/specs/add-redis-sqs-bedrock.md +128 -0
- package/specs/add-storage-dynamodb.md +106 -0
- package/specs/bedrock-model-catalog.md +131 -0
- package/specs/ci-pipeline.md +17 -0
- package/specs/cost-transparency.md +115 -0
- package/specs/custom-domains-and-ses.md +153 -0
- package/specs/database-suite-expansion.md +151 -0
- package/specs/db-connect.md +69 -0
- package/specs/db-lifecycle-migrations.md +159 -0
- package/specs/dependency-aware-init.md +176 -0
- package/specs/deploy-stack-to-grada-run-rebrand.md +45 -0
- package/specs/deployment-safety.md +170 -0
- package/specs/diagnose.md +16 -0
- package/specs/docs-hub.md +16 -0
- package/specs/dx-polish.md +46 -0
- package/specs/exec.md +25 -0
- package/specs/finops-cron-drift.md +161 -0
- package/specs/gc.md +26 -0
- package/specs/integration-suite.md +16 -0
- package/specs/logs.md +32 -0
- package/specs/rollback-live-polling.md +40 -0
- package/specs/secrets-pull-audit.md +51 -0
- package/specs/serverless-lambda-target.md +133 -0
- package/specs/status.md +31 -0
- package/specs/telemetry-and-spawn-hardening.md +69 -0
- package/specs/telemetry-hardening.md +35 -0
- package/src/commands/add.js +1111 -0
- package/src/commands/apply.js +214 -0
- package/src/commands/db/backup.js +229 -0
- package/src/commands/db/connect.js +304 -0
- package/src/commands/db/enable-vector.js +344 -0
- package/src/commands/db/import.js +604 -0
- package/src/commands/db/migrate.js +477 -0
- package/src/commands/db/restore.js +361 -0
- package/src/commands/db.js +87 -0
- package/src/commands/destroy.js +217 -0
- package/src/commands/diagnose.js +460 -0
- package/src/commands/doctor.js +109 -0
- package/src/commands/domain.js +685 -0
- package/src/commands/drift.js +243 -0
- package/src/commands/eject.js +127 -0
- package/src/commands/exec.js +222 -0
- package/src/commands/gc.js +250 -0
- package/src/commands/init.js +649 -0
- package/src/commands/logs.js +256 -0
- package/src/commands/rollback.js +323 -0
- package/src/commands/secrets.js +485 -0
- package/src/commands/sleep.js +347 -0
- package/src/commands/status.js +309 -0
- package/src/commands/sync-ai.js +115 -0
- package/src/commands/wake.js +337 -0
- package/src/core/parser.js +126 -0
- package/src/core/telemetry.js +244 -0
- package/src/data/bedrock-models.json +896 -0
- package/src/utils/addons.js +126 -0
- package/src/utils/ai-rules.js +59 -0
- package/src/utils/args.js +91 -0
- package/src/utils/aws.js +178 -0
- package/src/utils/backup.js +69 -0
- package/src/utils/bedrock-catalog.js +511 -0
- package/src/utils/capabilities.js +500 -0
- package/src/utils/command.js +65 -0
- package/src/utils/db-tunnel.js +164 -0
- package/src/utils/detector.js +298 -0
- package/src/utils/dockerCompose.js +65 -0
- package/src/utils/domains.js +73 -0
- package/src/utils/ecs-runner.js +289 -0
- package/src/utils/ecs.js +92 -0
- package/src/utils/frameworks.js +55 -0
- package/src/utils/generator.js +527 -0
- package/src/utils/hcl.js +426 -0
- package/src/utils/lambda-ecr.js +185 -0
- package/src/utils/prompts.js +278 -0
- package/src/utils/rds.js +131 -0
- package/src/utils/resolvers.js +174 -0
- package/src/utils/sleep-state.js +140 -0
- package/src/utils/sleep-targets.js +139 -0
- package/src/utils/system.js +42 -0
- package/src/utils/terraform.js +70 -0
- package/src/utils/visualizer.js +381 -0
- package/src/utils/warnings.js +49 -0
- package/templates/README.md +150 -0
- package/templates/docker/django.Dockerfile +40 -0
- package/templates/docker/go.Dockerfile +23 -0
- package/templates/docker/nestjs.Dockerfile +33 -0
- package/templates/docker/nextjs.Dockerfile +55 -0
- package/templates/docker/node.Dockerfile +24 -0
- package/templates/docker/nuxt.Dockerfile +47 -0
- package/templates/docker/python.Dockerfile +38 -0
- package/templates/docker/rails.Dockerfile +59 -0
- package/templates/docker/static.Dockerfile +32 -0
- package/templates/docker/svelte.Dockerfile +52 -0
- package/templates/github/deploy-lambda.yml +120 -0
- package/templates/github/deploy.yml +138 -0
- package/templates/github/drift.yml +112 -0
- package/templates/github/preview-lambda.yml +86 -0
- package/templates/github/preview.yml +69 -0
- package/templates/github/teardown.yml +43 -0
- package/templates/terraform/addons/bedrock.tf +34 -0
- package/templates/terraform/addons/cron-lambda.tf +78 -0
- package/templates/terraform/addons/cron.tf +101 -0
- package/templates/terraform/addons/dynamodb.tf +73 -0
- package/templates/terraform/addons/redis.tf +64 -0
- package/templates/terraform/addons/s3.tf +143 -0
- package/templates/terraform/addons/ses.tf +73 -0
- package/templates/terraform/addons/sqs.tf +67 -0
- package/templates/terraform/backend.tf +22 -0
- package/templates/terraform/cloudfront-lambda.tf +80 -0
- package/templates/terraform/cloudfront.tf +80 -0
- package/templates/terraform/database-aurora-postgresql.tf +92 -0
- package/templates/terraform/database-mysql.tf +72 -0
- package/templates/terraform/database.tf +71 -0
- package/templates/terraform/main-lambda.tf +229 -0
- package/templates/terraform/main.tf +296 -0
- package/templates/terraform/network.tf +95 -0
- package/templates/terraform/oidc.tf +64 -0
- package/templates/terraform/secrets.tf +31 -0
- package/templates/terraform/worker.tf +69 -0
- package/tests/__snapshots__/generator.test.js.snap +9633 -0
- package/tests/add.test.js +2037 -0
- package/tests/ai.test.js +94 -0
- package/tests/apply.test.js +488 -0
- package/tests/args.test.js +86 -0
- package/tests/aws.test.js +244 -0
- package/tests/capabilities.test.js +307 -0
- package/tests/cli.test.js +29 -0
- package/tests/command.test.js +100 -0
- package/tests/commands-import.test.js +74 -0
- package/tests/db.test.js +2704 -0
- package/tests/destroy.test.js +391 -0
- package/tests/detector.test.js +79 -0
- package/tests/diagnose.test.js +779 -0
- package/tests/doctor.test.js +202 -0
- package/tests/domain.test.js +899 -0
- package/tests/drift.test.js +243 -0
- package/tests/ecs.test.js +130 -0
- package/tests/eject.test.js +65 -0
- package/tests/exec.test.js +380 -0
- package/tests/gc.test.js +496 -0
- package/tests/generator.test.js +794 -0
- package/tests/headless.test.js +562 -0
- package/tests/lambda-ecr.test.js +185 -0
- package/tests/logs.test.js +447 -0
- package/tests/parser.test.js +160 -0
- package/tests/rds.test.js +244 -0
- package/tests/resolvers.test.js +282 -0
- package/tests/rollback.test.js +692 -0
- package/tests/secrets.test.js +752 -0
- package/tests/sleep-wake.test.js +1016 -0
- package/tests/status.test.js +370 -0
- package/tests/system.test.js +70 -0
- package/tests/telemetry.test.js +520 -0
- package/tests/terraform.test.js +84 -0
- package/tests/visualizer.test.js +496 -0
- package/vitest.config.js +9 -0
- package/index.js +0 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[["Map",1,2,1203,1204],"docs",["Map",3,4,45,46,73,74,101,102,126,127,151,152,176,177,201,202,226,227,251,252,276,277,304,305,329,330,352,353,375,376,398,399,424,425,450,451,474,475,497,498,530,531,553,554,576,577,612,613,649,650,679,680,716,717,750,751,780,781,815,816,857,858,888,889,925,926,959,960,995,996,1010,1011,1042,1043,1074,1075,1102,1103,1129,1130,1169,1170],"adrs/0001-s3-native-state-locking",{"id":3,"data":5,"body":14,"filePath":15,"digest":16,"rendered":17},{"title":6,"description":7,"editUrl":8,"head":9,"template":10,"sidebar":11,"pagefind":8,"draft":12},"S3 Native State Locking","Use S3-native locking for Terraform remote state without DynamoDB.",true,[],"doc",{"hidden":12,"attrs":13},false,{},"* **Status:** Accepted\n* **Date:** 2026-08-15 (Retroactive)\n\n## Context and Problem Statement\n\nWhen deploying infrastructure via Terraform across local developer workstations and automated CI/CD pipelines, remote state management is required to prevent race conditions, state drift, and concurrent apply corruption. \n\nTraditionally, managing Terraform remote state on AWS required provisioning both an S3 bucket (for storage) and a dedicated DynamoDB table (for state locking). This added operational overhead, increased the baseline AWS resource footprint, and required developers to manage extra IAM permissions solely for locking metadata.\n\n## Decision Drivers\n\n* **Simplicity:** Minimize the number of AWS resources a user has to provision and manage day-to-day.\n* **Cost Efficiency:** Eliminate unnecessary idle infrastructure costs (e.g., DynamoDB provisioned capacity).\n* **Reliability:** Guarantee that concurrent CI/CD pipeline runs and local CLI executions cannot corrupt Terraform state files.\n\n## Considered Options\n\n1. **S3 + DynamoDB Table:** The traditional HashiCorp recommendation for remote state locking.\n2. **S3 Native State Locking:** Utilizing S3's native conditional write support for state locking directly within the S3 bucket backend.\n3. **Third-Party State Backends:** (e.g., Terraform Cloud) — rejected to preserve zero-vendor-lock-in and keep execution local to the user's AWS account.\n\n## Decision Outcome\n\n**Chosen Option:** Use an encrypted Amazon S3 bucket as the remote state backend leveraging Terraform's native S3 state locking capabilities.\n\n### Positive Consequences\n* **Zero Maintenance:** Users do not have to monitor, manage, or pay for an extra DynamoDB table.\n* **Tighter Security:** Simplifies the IAM policy scope required for the `deploy-stack` state bucket helper, adhering strictly to least privilege.\n* **Frictionless Onboarding:** Streamlines the bootstrapping experience during the initial `npx deploy-stack` run.\n\n### Negative Consequences\n* Relies on modern Terraform backend behavior that supports S3 native locks. Edge cases involving highly outdated, legacy Terraform CLI versions are not supported.","src/content/docs/adrs/0001-s3-native-state-locking.md","1ef062fc7883318e",{"html":18,"metadata":19},"\u003Cul>\n\u003Cli>\u003Cstrong>Status:\u003C/strong> Accepted\u003C/li>\n\u003Cli>\u003Cstrong>Date:\u003C/strong> 2026-08-15 (Retroactive)\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"context-and-problem-statement\">Context and Problem Statement\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#context-and-problem-statement\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Context and Problem Statement”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>When deploying infrastructure via Terraform across local developer workstations and automated CI/CD pipelines, remote state management is required to prevent race conditions, state drift, and concurrent apply corruption.\u003C/p>\n\u003Cp>Traditionally, managing Terraform remote state on AWS required provisioning both an S3 bucket (for storage) and a dedicated DynamoDB table (for state locking). This added operational overhead, increased the baseline AWS resource footprint, and required developers to manage extra IAM permissions solely for locking metadata.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"decision-drivers\">Decision Drivers\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#decision-drivers\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Decision Drivers”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>\u003Cstrong>Simplicity:\u003C/strong> Minimize the number of AWS resources a user has to provision and manage day-to-day.\u003C/li>\n\u003Cli>\u003Cstrong>Cost Efficiency:\u003C/strong> Eliminate unnecessary idle infrastructure costs (e.g., DynamoDB provisioned capacity).\u003C/li>\n\u003Cli>\u003Cstrong>Reliability:\u003C/strong> Guarantee that concurrent CI/CD pipeline runs and local CLI executions cannot corrupt Terraform state files.\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"considered-options\">Considered Options\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#considered-options\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Considered Options”\u003C/span>\u003C/a>\u003C/div>\n\u003Col>\n\u003Cli>\u003Cstrong>S3 + DynamoDB Table:\u003C/strong> The traditional HashiCorp recommendation for remote state locking.\u003C/li>\n\u003Cli>\u003Cstrong>S3 Native State Locking:\u003C/strong> Utilizing S3’s native conditional write support for state locking directly within the S3 bucket backend.\u003C/li>\n\u003Cli>\u003Cstrong>Third-Party State Backends:\u003C/strong> (e.g., Terraform Cloud) — rejected to preserve zero-vendor-lock-in and keep execution local to the user’s AWS account.\u003C/li>\n\u003C/ol>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"decision-outcome\">Decision Outcome\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#decision-outcome\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Decision Outcome”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>\u003Cstrong>Chosen Option:\u003C/strong> Use an encrypted Amazon S3 bucket as the remote state backend leveraging Terraform’s native S3 state locking capabilities.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h3\">\u003Ch3 id=\"positive-consequences\">Positive Consequences\u003C/h3>\u003Ca class=\"sl-anchor-link\" href=\"#positive-consequences\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Positive Consequences”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>\u003Cstrong>Zero Maintenance:\u003C/strong> Users do not have to monitor, manage, or pay for an extra DynamoDB table.\u003C/li>\n\u003Cli>\u003Cstrong>Tighter Security:\u003C/strong> Simplifies the IAM policy scope required for the \u003Ccode dir=\"auto\">deploy-stack\u003C/code> state bucket helper, adhering strictly to least privilege.\u003C/li>\n\u003Cli>\u003Cstrong>Frictionless Onboarding:\u003C/strong> Streamlines the bootstrapping experience during the initial \u003Ccode dir=\"auto\">npx deploy-stack\u003C/code> run.\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h3\">\u003Ch3 id=\"negative-consequences\">Negative Consequences\u003C/h3>\u003Ca class=\"sl-anchor-link\" href=\"#negative-consequences\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Negative Consequences”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>Relies on modern Terraform backend behavior that supports S3 native locks. Edge cases involving highly outdated, legacy Terraform CLI versions are not supported.\u003C/li>\n\u003C/ul>\n",{"headings":20,"localImagePaths":41,"remoteImagePaths":42,"frontmatter":43,"imagePaths":44},[21,25,28,31,34,38],{"depth":22,"slug":23,"text":24},2,"context-and-problem-statement","Context and Problem Statement",{"depth":22,"slug":26,"text":27},"decision-drivers","Decision Drivers",{"depth":22,"slug":29,"text":30},"considered-options","Considered Options",{"depth":22,"slug":32,"text":33},"decision-outcome","Decision Outcome",{"depth":35,"slug":36,"text":37},3,"positive-consequences","Positive Consequences",{"depth":35,"slug":39,"text":40},"negative-consequences","Negative Consequences",[],[],{"title":6,"description":7},[],"adrs/0002-eject-mechanism-pure-iac",{"id":45,"data":47,"body":53,"filePath":54,"digest":55,"rendered":56},{"title":48,"description":49,"editUrl":8,"head":50,"template":10,"sidebar":51,"pagefind":8,"draft":12},"Eject Mechanism for Pure IaC","Keep generated Terraform and leave the CLI with the eject mechanism.",[],{"hidden":12,"attrs":52},{},"* **Status:** Accepted\n* **Date:** 2026-08-20 (Retroactive)\n\n## Context and Problem Statement\n\n`deploy-stack` abstracts away the complexity of writing raw Terraform for ECS Fargate, ALBs, CloudFront, OIDC, and Secrets Manager. However, a primary reason senior platform teams hesitate to adopt deployment generators is the fear of **tool lock-in**. Teams need a guarantee that if their architecture outgrows the CLI, or if they wish to take 100% manual control of the codebase, they can do so without starting from scratch.\n\n## Decision Drivers\n\n* **Zero Vendor Lock-In:** Uphold the foundational promise that developers permanently own their infrastructure code.\n* **Auditability & Freedom:** Provide teams with an unambiguous \"escape hatch\" to sever ties with `deploy-stack` management metadata while maintaining a perfectly functioning infrastructure pipeline.\n\n## Considered Options\n\n1. **No Eject Command:** Require users to manually delete `ManagedBy` tags and untangle state/workflows by hand.\n2. **Submodule / Framework Wrapper:** Keep the Terraform code hidden inside a remote module (rejected, as it violates the core premise of transparent, readable IaC).\n3. **Explicit `eject` Command:** Build a dedicated `npx deploy-stack eject` utility that strips all CLI metadata and tracking tags, leaving behind clean, standard Terraform and GitHub Actions files.\n\n## Decision Outcome\n\n**Chosen Option:** Implement an explicit `npx deploy-stack eject` command as a core feature.\n\n### Technical Implementation Details\nWhen invoked, `eject`:\n* Removes or sanitizes internal `ManagedBy = \"deploy-stack\"` default tags across all generated files.\n* Preserves all generated `.tf`, `Dockerfile`, and `.github/workflows/` files safely in place.\n* Detaches the project from the CLI entirely, leaving valid Terraform code that can be managed directly via the `terraform` or `opentofu` binaries.\n\n### Positive Consequences\n* Builds trust with engineers and security teams who refuse black-box wrappers.\n* Eliminates friction during adoption; users know they can safely leave at any time.\n\n### Negative Consequences\n* Ejected repositories permanently lose access to automated security patches, template updates, or CLI-driven drift synchronization.","src/content/docs/adrs/0002-eject-mechanism-pure-iac.md","b3dfabca07b3a356",{"html":57,"metadata":58},"\u003Cul>\n\u003Cli>\u003Cstrong>Status:\u003C/strong> Accepted\u003C/li>\n\u003Cli>\u003Cstrong>Date:\u003C/strong> 2026-08-20 (Retroactive)\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"context-and-problem-statement\">Context and Problem Statement\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#context-and-problem-statement\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Context and Problem Statement”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>\u003Ccode dir=\"auto\">deploy-stack\u003C/code> abstracts away the complexity of writing raw Terraform for ECS Fargate, ALBs, CloudFront, OIDC, and Secrets Manager. However, a primary reason senior platform teams hesitate to adopt deployment generators is the fear of \u003Cstrong>tool lock-in\u003C/strong>. Teams need a guarantee that if their architecture outgrows the CLI, or if they wish to take 100% manual control of the codebase, they can do so without starting from scratch.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"decision-drivers\">Decision Drivers\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#decision-drivers\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Decision Drivers”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>\u003Cstrong>Zero Vendor Lock-In:\u003C/strong> Uphold the foundational promise that developers permanently own their infrastructure code.\u003C/li>\n\u003Cli>\u003Cstrong>Auditability & Freedom:\u003C/strong> Provide teams with an unambiguous “escape hatch” to sever ties with \u003Ccode dir=\"auto\">deploy-stack\u003C/code> management metadata while maintaining a perfectly functioning infrastructure pipeline.\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"considered-options\">Considered Options\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#considered-options\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Considered Options”\u003C/span>\u003C/a>\u003C/div>\n\u003Col>\n\u003Cli>\u003Cstrong>No Eject Command:\u003C/strong> Require users to manually delete \u003Ccode dir=\"auto\">ManagedBy\u003C/code> tags and untangle state/workflows by hand.\u003C/li>\n\u003Cli>\u003Cstrong>Submodule / Framework Wrapper:\u003C/strong> Keep the Terraform code hidden inside a remote module (rejected, as it violates the core premise of transparent, readable IaC).\u003C/li>\n\u003Cli>\u003Cstrong>Explicit \u003Ccode dir=\"auto\">eject\u003C/code> Command:\u003C/strong> Build a dedicated \u003Ccode dir=\"auto\">npx deploy-stack eject\u003C/code> utility that strips all CLI metadata and tracking tags, leaving behind clean, standard Terraform and GitHub Actions files.\u003C/li>\n\u003C/ol>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"decision-outcome\">Decision Outcome\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#decision-outcome\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Decision Outcome”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>\u003Cstrong>Chosen Option:\u003C/strong> Implement an explicit \u003Ccode dir=\"auto\">npx deploy-stack eject\u003C/code> command as a core feature.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h3\">\u003Ch3 id=\"technical-implementation-details\">Technical Implementation Details\u003C/h3>\u003Ca class=\"sl-anchor-link\" href=\"#technical-implementation-details\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Technical Implementation Details”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>When invoked, \u003Ccode dir=\"auto\">eject\u003C/code>:\u003C/p>\n\u003Cul>\n\u003Cli>Removes or sanitizes internal \u003Ccode dir=\"auto\">ManagedBy = \"deploy-stack\"\u003C/code> default tags across all generated files.\u003C/li>\n\u003Cli>Preserves all generated \u003Ccode dir=\"auto\">.tf\u003C/code>, \u003Ccode dir=\"auto\">Dockerfile\u003C/code>, and \u003Ccode dir=\"auto\">.github/workflows/\u003C/code> files safely in place.\u003C/li>\n\u003Cli>Detaches the project from the CLI entirely, leaving valid Terraform code that can be managed directly via the \u003Ccode dir=\"auto\">terraform\u003C/code> or \u003Ccode dir=\"auto\">opentofu\u003C/code> binaries.\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h3\">\u003Ch3 id=\"positive-consequences\">Positive Consequences\u003C/h3>\u003Ca class=\"sl-anchor-link\" href=\"#positive-consequences\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Positive Consequences”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>Builds trust with engineers and security teams who refuse black-box wrappers.\u003C/li>\n\u003Cli>Eliminates friction during adoption; users know they can safely leave at any time.\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h3\">\u003Ch3 id=\"negative-consequences\">Negative Consequences\u003C/h3>\u003Ca class=\"sl-anchor-link\" href=\"#negative-consequences\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Negative Consequences”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>Ejected repositories permanently lose access to automated security patches, template updates, or CLI-driven drift synchronization.\u003C/li>\n\u003C/ul>\n",{"headings":59,"localImagePaths":69,"remoteImagePaths":70,"frontmatter":71,"imagePaths":72},[60,61,62,63,64,67,68],{"depth":22,"slug":23,"text":24},{"depth":22,"slug":26,"text":27},{"depth":22,"slug":29,"text":30},{"depth":22,"slug":32,"text":33},{"depth":35,"slug":65,"text":66},"technical-implementation-details","Technical Implementation Details",{"depth":35,"slug":36,"text":37},{"depth":35,"slug":39,"text":40},[],[],{"title":48,"description":49},[],"adrs/0003-sync-ai-context-strategy",{"id":73,"data":75,"body":81,"filePath":82,"digest":83,"rendered":84},{"title":76,"description":77,"editUrl":8,"head":78,"template":10,"sidebar":79,"pagefind":8,"draft":12},"AI Context Synchronization Strategy","Sync IaC context into AI coding assistants with sync-ai.",[],{"hidden":12,"attrs":80},{},"* **Status:** Accepted\n* **Date:** 2026-09-02 (Retroactive)\n\n## Context and Problem Statement\n\nModern engineering teams heavily utilize AI coding assistants (Cursor, GitHub Copilot, Windsurf, Claude Code, etc.) in their local IDEs. However, when dealing with Infrastructure-as-Code (IaC), AI models frequently hallucinate invalid Terraform syntax, recommend destructive manual AWS CLI commands, or ignore critical project-specific constraints like unprivileged container ports and OIDC auth flows.\n\nFurthermore, automatically writing instruction files into user repositories carries a high risk of clobbering a team's existing, carefully crafted agent prompts.\n\n## Decision Drivers\n\n* **Hallucination Mitigation:** Provide structured, deterministic instructions to IDE AI assistants to ensure they generate valid Terraform and safe workflows.\n* **Non-Destructive Integration:** Guarantee that existing `.cursorrules`, `CLAUDE.md`, or shared workspace instruction files are never accidentally overwritten or destroyed.\n* **Multi-Tool Support:** Support the highly fragmented landscape of AI coding tools without forcing users into a specific IDE.\n\n## Considered Options\n\n1. **Single Global Instruction File:** Only support `.cursorrules` (rejected as too narrow for modern multi-tool teams).\n2. **Blind Overwrite of Agent Files:** Replace existing AI rule files with `deploy-stack` defaults (rejected due to the unacceptable risk of destroying user configuration).\n3. **Isolated Rule Files + Delimited Block Injection (`sync-ai`):** Create dedicated files where supported (e.g., `deploy-stack.mdc`), and safely inject delimited, managed markdown blocks into existing shared instruction files where necessary.\n\n## Decision Outcome\n\n**Chosen Option:** Build a dedicated `npx deploy-stack sync-ai` command and a non-destructive auto-injection engine.\n\n### Supported Targets\nThe engine intelligently maps instructions to the following environments:\n* **Cursor:** `.cursor/rules/deploy-stack.mdc`\n* **Roo Code / Roo-Cline:** `.roo/rules/deploy-stack.md`\n* **Trae:** `.trae/rules/project_rules.md` (managed block injection)\n* **Continue:** `.prompts/deploy-stack.prompt`\n* **Windsurf:** `.windsurfrules` (managed block injection)\n* **GitHub Copilot:** `.github/copilot-instructions.md` (managed block injection)\n* **Claude Code:** `CLAUDE.md` (managed block injection)\n* **Goose:** `.goosehints`\n* **Aider:** `.aider.conf.yml` / `.aider.model.settings.yml`\n\n### Positive Consequences\n* Dramatically reduces AI-induced infrastructure errors and dangerous AWS CLI recommendations.\n* Safe, idempotent execution allows teams to run `npx deploy-stack sync-ai` whenever their architecture parameters (like AWS region or ports) change, without fear of losing their own prompts.\n\n### Negative Consequences\n* Requires ongoing maintenance of parser logic and block delimiters as AI coding assistant vendors rapidly change their configuration file specifications.","src/content/docs/adrs/0003-sync-ai-context-strategy.md","356fce44b5b78818",{"html":85,"metadata":86},"\u003Cul>\n\u003Cli>\u003Cstrong>Status:\u003C/strong> Accepted\u003C/li>\n\u003Cli>\u003Cstrong>Date:\u003C/strong> 2026-09-02 (Retroactive)\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"context-and-problem-statement\">Context and Problem Statement\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#context-and-problem-statement\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Context and Problem Statement”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>Modern engineering teams heavily utilize AI coding assistants (Cursor, GitHub Copilot, Windsurf, Claude Code, etc.) in their local IDEs. However, when dealing with Infrastructure-as-Code (IaC), AI models frequently hallucinate invalid Terraform syntax, recommend destructive manual AWS CLI commands, or ignore critical project-specific constraints like unprivileged container ports and OIDC auth flows.\u003C/p>\n\u003Cp>Furthermore, automatically writing instruction files into user repositories carries a high risk of clobbering a team’s existing, carefully crafted agent prompts.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"decision-drivers\">Decision Drivers\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#decision-drivers\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Decision Drivers”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>\u003Cstrong>Hallucination Mitigation:\u003C/strong> Provide structured, deterministic instructions to IDE AI assistants to ensure they generate valid Terraform and safe workflows.\u003C/li>\n\u003Cli>\u003Cstrong>Non-Destructive Integration:\u003C/strong> Guarantee that existing \u003Ccode dir=\"auto\">.cursorrules\u003C/code>, \u003Ccode dir=\"auto\">CLAUDE.md\u003C/code>, or shared workspace instruction files are never accidentally overwritten or destroyed.\u003C/li>\n\u003Cli>\u003Cstrong>Multi-Tool Support:\u003C/strong> Support the highly fragmented landscape of AI coding tools without forcing users into a specific IDE.\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"considered-options\">Considered Options\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#considered-options\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Considered Options”\u003C/span>\u003C/a>\u003C/div>\n\u003Col>\n\u003Cli>\u003Cstrong>Single Global Instruction File:\u003C/strong> Only support \u003Ccode dir=\"auto\">.cursorrules\u003C/code> (rejected as too narrow for modern multi-tool teams).\u003C/li>\n\u003Cli>\u003Cstrong>Blind Overwrite of Agent Files:\u003C/strong> Replace existing AI rule files with \u003Ccode dir=\"auto\">deploy-stack\u003C/code> defaults (rejected due to the unacceptable risk of destroying user configuration).\u003C/li>\n\u003Cli>\u003Cstrong>Isolated Rule Files + Delimited Block Injection (\u003Ccode dir=\"auto\">sync-ai\u003C/code>):\u003C/strong> Create dedicated files where supported (e.g., \u003Ccode dir=\"auto\">deploy-stack.mdc\u003C/code>), and safely inject delimited, managed markdown blocks into existing shared instruction files where necessary.\u003C/li>\n\u003C/ol>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"decision-outcome\">Decision Outcome\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#decision-outcome\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Decision Outcome”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>\u003Cstrong>Chosen Option:\u003C/strong> Build a dedicated \u003Ccode dir=\"auto\">npx deploy-stack sync-ai\u003C/code> command and a non-destructive auto-injection engine.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h3\">\u003Ch3 id=\"supported-targets\">Supported Targets\u003C/h3>\u003Ca class=\"sl-anchor-link\" href=\"#supported-targets\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Supported Targets”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>The engine intelligently maps instructions to the following environments:\u003C/p>\n\u003Cul>\n\u003Cli>\u003Cstrong>Cursor:\u003C/strong> \u003Ccode dir=\"auto\">.cursor/rules/deploy-stack.mdc\u003C/code>\u003C/li>\n\u003Cli>\u003Cstrong>Roo Code / Roo-Cline:\u003C/strong> \u003Ccode dir=\"auto\">.roo/rules/deploy-stack.md\u003C/code>\u003C/li>\n\u003Cli>\u003Cstrong>Trae:\u003C/strong> \u003Ccode dir=\"auto\">.trae/rules/project_rules.md\u003C/code> (managed block injection)\u003C/li>\n\u003Cli>\u003Cstrong>Continue:\u003C/strong> \u003Ccode dir=\"auto\">.prompts/deploy-stack.prompt\u003C/code>\u003C/li>\n\u003Cli>\u003Cstrong>Windsurf:\u003C/strong> \u003Ccode dir=\"auto\">.windsurfrules\u003C/code> (managed block injection)\u003C/li>\n\u003Cli>\u003Cstrong>GitHub Copilot:\u003C/strong> \u003Ccode dir=\"auto\">.github/copilot-instructions.md\u003C/code> (managed block injection)\u003C/li>\n\u003Cli>\u003Cstrong>Claude Code:\u003C/strong> \u003Ccode dir=\"auto\">CLAUDE.md\u003C/code> (managed block injection)\u003C/li>\n\u003Cli>\u003Cstrong>Goose:\u003C/strong> \u003Ccode dir=\"auto\">.goosehints\u003C/code>\u003C/li>\n\u003Cli>\u003Cstrong>Aider:\u003C/strong> \u003Ccode dir=\"auto\">.aider.conf.yml\u003C/code> / \u003Ccode dir=\"auto\">.aider.model.settings.yml\u003C/code>\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h3\">\u003Ch3 id=\"positive-consequences\">Positive Consequences\u003C/h3>\u003Ca class=\"sl-anchor-link\" href=\"#positive-consequences\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Positive Consequences”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>Dramatically reduces AI-induced infrastructure errors and dangerous AWS CLI recommendations.\u003C/li>\n\u003Cli>Safe, idempotent execution allows teams to run \u003Ccode dir=\"auto\">npx deploy-stack sync-ai\u003C/code> whenever their architecture parameters (like AWS region or ports) change, without fear of losing their own prompts.\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h3\">\u003Ch3 id=\"negative-consequences\">Negative Consequences\u003C/h3>\u003Ca class=\"sl-anchor-link\" href=\"#negative-consequences\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Negative Consequences”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>Requires ongoing maintenance of parser logic and block delimiters as AI coding assistant vendors rapidly change their configuration file specifications.\u003C/li>\n\u003C/ul>\n",{"headings":87,"localImagePaths":97,"remoteImagePaths":98,"frontmatter":99,"imagePaths":100},[88,89,90,91,92,95,96],{"depth":22,"slug":23,"text":24},{"depth":22,"slug":26,"text":27},{"depth":22,"slug":29,"text":30},{"depth":22,"slug":32,"text":33},{"depth":35,"slug":93,"text":94},"supported-targets","Supported Targets",{"depth":35,"slug":36,"text":37},{"depth":35,"slug":39,"text":40},[],[],{"title":76,"description":77},[],"adrs/0004-iac-driven-diagnostic-context",{"id":101,"data":103,"body":109,"filePath":110,"digest":111,"rendered":112},{"title":104,"description":105,"editUrl":8,"head":106,"template":10,"sidebar":107,"pagefind":8,"draft":12},"IaC-Driven Diagnostic Context (Stateless CLI)","Let diagnose fetch CloudWatch and ECS context without local state.",[],{"hidden":12,"attrs":108},{},"* **Status:** Accepted\n* **Date:** 2026-09-19\n\n## Context and Problem Statement\n\nTo provide a seamless developer experience, the `deploy-stack diagnose` command needs to automatically fetch CloudWatch logs and ECS task failures without requiring the user to manually input their AWS Region, Cluster Name, or Log Group. \n\nWe needed a mechanism to persist or infer the deployment context locally so the CLI knows where to look for errors.\n\n## Decision Drivers\n\n* **Statelessness:** The CLI should avoid managing internal database files or proprietary local state files that can fall out of sync with actual infrastructure.\n* **Single Source of Truth:** Terraform is already the declarative source of truth for the project's infrastructure.\n* **Ecosystem Compatibility:** Developers often delete node_modules or switch laptops; context retrieval must survive typical Git workflows.\n\n## Considered Options\n\n1. **Local State File:** Create a `.deploy-stack/context.json` file upon generation. (Rejected: creates state drift and pollutes version control).\n2. **AWS Tag Querying:** Use the AWS SDK to query all clusters for a specific tag. (Rejected: too slow, requires broad IAM `ListClusters` permissions, and fails if multiple environments exist).\n3. **IaC Parsing (Stateless):** Parse the generated `terraform/main.tf` to extract the AWS Region and infer the cluster name from the local directory structure.\n\n## Decision Outcome\n\n**Chosen Option:** IaC Parsing (Stateless). The `diagnose` command reads the AWS Region directly via regex from `terraform/main.tf` and constructs standard AWS resource names based on the current working directory.\n\n### Positive Consequences\n* The CLI remains entirely stateless. If the Terraform files exist, the diagnostics work.\n* Enforces the architectural philosophy that the generated IaC is the ultimate source of truth.\n* Zero additional files are added to the user's repository.\n\n### Negative Consequences\n* If a user manually alters the `region` string inside `main.tf` using non-standard formatting, the regex parser may fail to detect it, falling back to a default region.","src/content/docs/adrs/0004-iac-driven-diagnostic-context.md","d1c593cccc9423eb",{"html":113,"metadata":114},"\u003Cul>\n\u003Cli>\u003Cstrong>Status:\u003C/strong> Accepted\u003C/li>\n\u003Cli>\u003Cstrong>Date:\u003C/strong> 2026-09-19\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"context-and-problem-statement\">Context and Problem Statement\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#context-and-problem-statement\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Context and Problem Statement”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>To provide a seamless developer experience, the \u003Ccode dir=\"auto\">deploy-stack diagnose\u003C/code> command needs to automatically fetch CloudWatch logs and ECS task failures without requiring the user to manually input their AWS Region, Cluster Name, or Log Group.\u003C/p>\n\u003Cp>We needed a mechanism to persist or infer the deployment context locally so the CLI knows where to look for errors.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"decision-drivers\">Decision Drivers\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#decision-drivers\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Decision Drivers”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>\u003Cstrong>Statelessness:\u003C/strong> The CLI should avoid managing internal database files or proprietary local state files that can fall out of sync with actual infrastructure.\u003C/li>\n\u003Cli>\u003Cstrong>Single Source of Truth:\u003C/strong> Terraform is already the declarative source of truth for the project’s infrastructure.\u003C/li>\n\u003Cli>\u003Cstrong>Ecosystem Compatibility:\u003C/strong> Developers often delete node_modules or switch laptops; context retrieval must survive typical Git workflows.\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"considered-options\">Considered Options\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#considered-options\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Considered Options”\u003C/span>\u003C/a>\u003C/div>\n\u003Col>\n\u003Cli>\u003Cstrong>Local State File:\u003C/strong> Create a \u003Ccode dir=\"auto\">.deploy-stack/context.json\u003C/code> file upon generation. (Rejected: creates state drift and pollutes version control).\u003C/li>\n\u003Cli>\u003Cstrong>AWS Tag Querying:\u003C/strong> Use the AWS SDK to query all clusters for a specific tag. (Rejected: too slow, requires broad IAM \u003Ccode dir=\"auto\">ListClusters\u003C/code> permissions, and fails if multiple environments exist).\u003C/li>\n\u003Cli>\u003Cstrong>IaC Parsing (Stateless):\u003C/strong> Parse the generated \u003Ccode dir=\"auto\">terraform/main.tf\u003C/code> to extract the AWS Region and infer the cluster name from the local directory structure.\u003C/li>\n\u003C/ol>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"decision-outcome\">Decision Outcome\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#decision-outcome\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Decision Outcome”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>\u003Cstrong>Chosen Option:\u003C/strong> IaC Parsing (Stateless). The \u003Ccode dir=\"auto\">diagnose\u003C/code> command reads the AWS Region directly via regex from \u003Ccode dir=\"auto\">terraform/main.tf\u003C/code> and constructs standard AWS resource names based on the current working directory.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h3\">\u003Ch3 id=\"positive-consequences\">Positive Consequences\u003C/h3>\u003Ca class=\"sl-anchor-link\" href=\"#positive-consequences\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Positive Consequences”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>The CLI remains entirely stateless. If the Terraform files exist, the diagnostics work.\u003C/li>\n\u003Cli>Enforces the architectural philosophy that the generated IaC is the ultimate source of truth.\u003C/li>\n\u003Cli>Zero additional files are added to the user’s repository.\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h3\">\u003Ch3 id=\"negative-consequences\">Negative Consequences\u003C/h3>\u003Ca class=\"sl-anchor-link\" href=\"#negative-consequences\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Negative Consequences”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>If a user manually alters the \u003Ccode dir=\"auto\">region\u003C/code> string inside \u003Ccode dir=\"auto\">main.tf\u003C/code> using non-standard formatting, the regex parser may fail to detect it, falling back to a default region.\u003C/li>\n\u003C/ul>\n",{"headings":115,"localImagePaths":122,"remoteImagePaths":123,"frontmatter":124,"imagePaths":125},[116,117,118,119,120,121],{"depth":22,"slug":23,"text":24},{"depth":22,"slug":26,"text":27},{"depth":22,"slug":29,"text":30},{"depth":22,"slug":32,"text":33},{"depth":35,"slug":36,"text":37},{"depth":35,"slug":39,"text":40},[],[],{"title":104,"description":105},[],"adrs/0005-ecs-fargate-alb-runtime-target",{"id":126,"data":128,"body":134,"filePath":135,"digest":136,"rendered":137},{"title":129,"description":130,"editUrl":8,"head":131,"template":10,"sidebar":132,"pagefind":8,"draft":12},"ECS Fargate and ALB as the Single Runtime Target","Run every supported framework on ECS Fargate behind an application load balancer.",[],{"hidden":12,"attrs":133},{},"* **Status:** Accepted\n* **Date:** 2026-09-24 (Retroactive)\n\n## Context and Problem Statement\n\n`deploy-stack` promises that any supported framework deploys with one command. Every framework-specific Dockerfile, health-check rule, Terraform module (`templates/terraform/`), and diagnostic runbook only works if there is exactly one production runtime to target.\n\nWe needed to pick a single AWS compute and ingress combination that covers long-running web servers, workers, and static sites without per-framework infrastructure branches.\n\n## Decision Drivers\n\n* **Uniformity:** One set of Terraform modules, one container contract, one diagnostic story for all frameworks.\n* **No-ops fit:** Users choosing this tool do not run an infra team; the runtime must be serverless and scale to zero operational burden.\n* **Health-checkability:** The load balancer must probe container health so failed deploys surface as ALB signals, not silent black holes.\n\n## Considered Options\n\n1. **AWS Lambda / App Runner per framework.** (Rejected: request/response and timeout limits exclude long-running servers and workers; would force framework-specific branches.)\n2. **EC2 / self-managed clusters.** (Rejected: reintroduces the server management the tool exists to remove.)\n3. **ECS Fargate + Application Load Balancer.** Containers stay portable across frameworks; ALB gives path-based health checks, listener rules (translated from `vercel.json`), and zero-downtime rolling deploys.\n\n## Decision Outcome\n\n**Chosen Option:** ECS Fargate + ALB for all dynamic workloads. Every generated project provisions the same cluster/service/ALB shape; only the image contents and container port vary per framework.\n\n### Positive Consequences\n* Framework support reduces to Dockerfile + port + bind-address requirements (`src/utils/detector.js`, `src/utils/warnings.js`).\n* `diagnose` can assume ALB health-check semantics for every project.\n* Static sites reuse the same pipeline with an Nginx image instead of a framework server.\n\n### Negative Consequences\n* Cold-start-sensitive or GPU workloads are out of scope; the tool cannot serve them without a second runtime target.\n* Users pay Fargate minimums even for idle preview environments.","src/content/docs/adrs/0005-ecs-fargate-alb-runtime-target.md","5ccb19d57dd41aff",{"html":138,"metadata":139},"\u003Cul>\n\u003Cli>\u003Cstrong>Status:\u003C/strong> Accepted\u003C/li>\n\u003Cli>\u003Cstrong>Date:\u003C/strong> 2026-09-24 (Retroactive)\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"context-and-problem-statement\">Context and Problem Statement\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#context-and-problem-statement\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Context and Problem Statement”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>\u003Ccode dir=\"auto\">deploy-stack\u003C/code> promises that any supported framework deploys with one command. Every framework-specific Dockerfile, health-check rule, Terraform module (\u003Ccode dir=\"auto\">templates/terraform/\u003C/code>), and diagnostic runbook only works if there is exactly one production runtime to target.\u003C/p>\n\u003Cp>We needed to pick a single AWS compute and ingress combination that covers long-running web servers, workers, and static sites without per-framework infrastructure branches.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"decision-drivers\">Decision Drivers\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#decision-drivers\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Decision Drivers”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>\u003Cstrong>Uniformity:\u003C/strong> One set of Terraform modules, one container contract, one diagnostic story for all frameworks.\u003C/li>\n\u003Cli>\u003Cstrong>No-ops fit:\u003C/strong> Users choosing this tool do not run an infra team; the runtime must be serverless and scale to zero operational burden.\u003C/li>\n\u003Cli>\u003Cstrong>Health-checkability:\u003C/strong> The load balancer must probe container health so failed deploys surface as ALB signals, not silent black holes.\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"considered-options\">Considered Options\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#considered-options\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Considered Options”\u003C/span>\u003C/a>\u003C/div>\n\u003Col>\n\u003Cli>\u003Cstrong>AWS Lambda / App Runner per framework.\u003C/strong> (Rejected: request/response and timeout limits exclude long-running servers and workers; would force framework-specific branches.)\u003C/li>\n\u003Cli>\u003Cstrong>EC2 / self-managed clusters.\u003C/strong> (Rejected: reintroduces the server management the tool exists to remove.)\u003C/li>\n\u003Cli>\u003Cstrong>ECS Fargate + Application Load Balancer.\u003C/strong> Containers stay portable across frameworks; ALB gives path-based health checks, listener rules (translated from \u003Ccode dir=\"auto\">vercel.json\u003C/code>), and zero-downtime rolling deploys.\u003C/li>\n\u003C/ol>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"decision-outcome\">Decision Outcome\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#decision-outcome\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Decision Outcome”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>\u003Cstrong>Chosen Option:\u003C/strong> ECS Fargate + ALB for all dynamic workloads. Every generated project provisions the same cluster/service/ALB shape; only the image contents and container port vary per framework.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h3\">\u003Ch3 id=\"positive-consequences\">Positive Consequences\u003C/h3>\u003Ca class=\"sl-anchor-link\" href=\"#positive-consequences\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Positive Consequences”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>Framework support reduces to Dockerfile + port + bind-address requirements (\u003Ccode dir=\"auto\">src/utils/detector.js\u003C/code>, \u003Ccode dir=\"auto\">src/utils/warnings.js\u003C/code>).\u003C/li>\n\u003Cli>\u003Ccode dir=\"auto\">diagnose\u003C/code> can assume ALB health-check semantics for every project.\u003C/li>\n\u003Cli>Static sites reuse the same pipeline with an Nginx image instead of a framework server.\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h3\">\u003Ch3 id=\"negative-consequences\">Negative Consequences\u003C/h3>\u003Ca class=\"sl-anchor-link\" href=\"#negative-consequences\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Negative Consequences”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>Cold-start-sensitive or GPU workloads are out of scope; the tool cannot serve them without a second runtime target.\u003C/li>\n\u003Cli>Users pay Fargate minimums even for idle preview environments.\u003C/li>\n\u003C/ul>\n",{"headings":140,"localImagePaths":147,"remoteImagePaths":148,"frontmatter":149,"imagePaths":150},[141,142,143,144,145,146],{"depth":22,"slug":23,"text":24},{"depth":22,"slug":26,"text":27},{"depth":22,"slug":29,"text":30},{"depth":22,"slug":32,"text":33},{"depth":35,"slug":36,"text":37},{"depth":35,"slug":39,"text":40},[],[],{"title":129,"description":130},[],"adrs/0006-github-oidc-no-stored-keys",{"id":151,"data":153,"body":159,"filePath":160,"digest":161,"rendered":162},{"title":154,"description":155,"editUrl":8,"head":156,"template":10,"sidebar":157,"pagefind":8,"draft":12},"GitHub OIDC Authentication, No Stored AWS Keys","Authenticate CI/CD with short-lived OIDC tokens instead of long-lived AWS keys.",[],{"hidden":12,"attrs":158},{},"* **Status:** Accepted\n* **Date:** 2026-09-24 (Retroactive)\n\n## Context and Problem Statement\n\nThe generated workflow (`templates/github/deploy.yml`) must authenticate to AWS on every run to sync Terraform and deploy containers. The conventional approach — storing `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` as GitHub secrets — creates rotation burden and a leak surface: any secret exfiltrated from CI grants durable AWS access.\n\nWe needed an authentication mechanism with no long-lived credential to store, rotate, or leak.\n\n## Decision Drivers\n\n* **Zero-secret CI:** Nothing credential-shaped should live in the repository or GitHub secret store.\n* **Least privilege:** Each run should receive credentials scoped to that run only.\n* **First-deploy fit:** Setup must work on a fresh AWS account without pre-provisioned IAM users.\n\n## Considered Options\n\n1. **Long-lived IAM user keys in GitHub Secrets.** (Rejected: rotation burden, durable blast radius on leak, contradicts the zero-secret philosophy.)\n2. **GitHub OIDC federation.** The workflow declares `id-token: write`, and Terraform (`templates/terraform/oidc.tf`) registers GitHub as an OIDC identity provider with a per-project role. Each run mints a short-lived token; there is nothing to rotate.\n3. **User-supplied role assumption.** (Rejected as default: pushes IAM setup onto the first-deploy path; kept possible via `create_oidc_provider = false` for accounts that already federate GitHub.)\n\n## Decision Outcome\n\n**Chosen Option:** GitHub OIDC federation by default. Generation creates the provider (unless one exists) and a `\u003Cproject>-github-actions-role`; the workflow assumes it every run.\n\n### Positive Consequences\n* No AWS keys exist anywhere in CI: nothing to rotate, nothing durable to leak.\n* The weekly drift-convergence cron reuses the same mechanism with no extra setup.\n\n### Negative Consequences\n* First `apply` must create the OIDC provider, adding one IAM dependency to the happy path.\n* Accounts with restrictive IAM policies may need an admin to approve provider creation.","src/content/docs/adrs/0006-github-oidc-no-stored-keys.md","5e820e5a304da1f3",{"html":163,"metadata":164},"\u003Cul>\n\u003Cli>\u003Cstrong>Status:\u003C/strong> Accepted\u003C/li>\n\u003Cli>\u003Cstrong>Date:\u003C/strong> 2026-09-24 (Retroactive)\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"context-and-problem-statement\">Context and Problem Statement\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#context-and-problem-statement\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Context and Problem Statement”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>The generated workflow (\u003Ccode dir=\"auto\">templates/github/deploy.yml\u003C/code>) must authenticate to AWS on every run to sync Terraform and deploy containers. The conventional approach — storing \u003Ccode dir=\"auto\">AWS_ACCESS_KEY_ID\u003C/code> / \u003Ccode dir=\"auto\">AWS_SECRET_ACCESS_KEY\u003C/code> as GitHub secrets — creates rotation burden and a leak surface: any secret exfiltrated from CI grants durable AWS access.\u003C/p>\n\u003Cp>We needed an authentication mechanism with no long-lived credential to store, rotate, or leak.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"decision-drivers\">Decision Drivers\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#decision-drivers\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Decision Drivers”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>\u003Cstrong>Zero-secret CI:\u003C/strong> Nothing credential-shaped should live in the repository or GitHub secret store.\u003C/li>\n\u003Cli>\u003Cstrong>Least privilege:\u003C/strong> Each run should receive credentials scoped to that run only.\u003C/li>\n\u003Cli>\u003Cstrong>First-deploy fit:\u003C/strong> Setup must work on a fresh AWS account without pre-provisioned IAM users.\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"considered-options\">Considered Options\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#considered-options\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Considered Options”\u003C/span>\u003C/a>\u003C/div>\n\u003Col>\n\u003Cli>\u003Cstrong>Long-lived IAM user keys in GitHub Secrets.\u003C/strong> (Rejected: rotation burden, durable blast radius on leak, contradicts the zero-secret philosophy.)\u003C/li>\n\u003Cli>\u003Cstrong>GitHub OIDC federation.\u003C/strong> The workflow declares \u003Ccode dir=\"auto\">id-token: write\u003C/code>, and Terraform (\u003Ccode dir=\"auto\">templates/terraform/oidc.tf\u003C/code>) registers GitHub as an OIDC identity provider with a per-project role. Each run mints a short-lived token; there is nothing to rotate.\u003C/li>\n\u003Cli>\u003Cstrong>User-supplied role assumption.\u003C/strong> (Rejected as default: pushes IAM setup onto the first-deploy path; kept possible via \u003Ccode dir=\"auto\">create_oidc_provider = false\u003C/code> for accounts that already federate GitHub.)\u003C/li>\n\u003C/ol>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"decision-outcome\">Decision Outcome\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#decision-outcome\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Decision Outcome”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>\u003Cstrong>Chosen Option:\u003C/strong> GitHub OIDC federation by default. Generation creates the provider (unless one exists) and a \u003Ccode dir=\"auto\"><project>-github-actions-role\u003C/code>; the workflow assumes it every run.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h3\">\u003Ch3 id=\"positive-consequences\">Positive Consequences\u003C/h3>\u003Ca class=\"sl-anchor-link\" href=\"#positive-consequences\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Positive Consequences”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>No AWS keys exist anywhere in CI: nothing to rotate, nothing durable to leak.\u003C/li>\n\u003Cli>The weekly drift-convergence cron reuses the same mechanism with no extra setup.\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h3\">\u003Ch3 id=\"negative-consequences\">Negative Consequences\u003C/h3>\u003Ca class=\"sl-anchor-link\" href=\"#negative-consequences\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Negative Consequences”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>First \u003Ccode dir=\"auto\">apply\u003C/code> must create the OIDC provider, adding one IAM dependency to the happy path.\u003C/li>\n\u003Cli>Accounts with restrictive IAM policies may need an admin to approve provider creation.\u003C/li>\n\u003C/ul>\n",{"headings":165,"localImagePaths":172,"remoteImagePaths":173,"frontmatter":174,"imagePaths":175},[166,167,168,169,170,171],{"depth":22,"slug":23,"text":24},{"depth":22,"slug":26,"text":27},{"depth":22,"slug":29,"text":30},{"depth":22,"slug":32,"text":33},{"depth":35,"slug":36,"text":37},{"depth":35,"slug":39,"text":40},[],[],{"title":154,"description":155},[],"adrs/0007-framework-detection-with-fallback",{"id":176,"data":178,"body":184,"filePath":185,"digest":186,"rendered":187},{"title":179,"description":180,"editUrl":8,"head":181,"template":10,"sidebar":182,"pagefind":8,"draft":12},"Framework Auto-Detection with Graceful Fallback","Detect the framework from repo signals and fall back to static instead of failing.",[],{"hidden":12,"attrs":183},{},"* **Status:** Accepted\n* **Date:** 2026-09-24 (Retroactive)\n\n## Context and Problem Statement\n\nSetup must preselect the right framework preset without interrogating the user about files they may not understand — but repository signals are unreliable: `package.json` may be malformed, `vercel.json` may be empty, and unknown stacks must still produce a working project.\n\nWe needed detection rules that are helpful when signals exist and harmless when they do not.\n\n## Decision Drivers\n\n* **Never block generation:** A strange repo must still scaffold; a wrong guess the user can override beats a fatal error.\n* **Deterministic precedence:** Overlapping signals (e.g. `express` inside a NestJS app) must resolve the same way every run.\n* **Advance warning:** Framework-specific runtime requirements (bind address, build output, adapter) should surface before infrastructure exists, not after the first 502.\n\n## Considered Options\n\n1. **Interactive-only selection.** (Rejected: slow, and headless/CI generation needs a non-interactive path.)\n2. **Strict detection, fail on ambiguity.** (Rejected: malformed or partial configs would block scaffolding entirely.)\n3. **Precedence-ordered detection with silent ignore + static fallback.** Checks run top-down (`src/utils/detector.js`); malformed configs are treated as absent, never fatal; anything unmatched falls back to `static`. Post-detection checks (`src/utils/warnings.js`) flag fixable issues with copy-paste remedies.\n\n## Decision Outcome\n\n**Chosen Option:** Precedence-ordered detection with graceful fallback. Both `dependencies` and `devDependencies` are searched; empty or malformed signal files are ignored; headless mode without `--framework` resolves the same way.\n\n### Positive Consequences\n* `npx deploy-stack` succeeds on repos the tool has never seen, producing a deployable static project the user can refine.\n* Warnings arrive with exact fixes at setup time, when they are cheapest to apply.\n\n### Negative Consequences\n* A wrong-but-plausible guess (e.g. Express detected inside a larger framework) silently generates the wrong preset until the user notices.\n* Detection rules must be maintained alongside the ecosystem or they rot into misdetection.","src/content/docs/adrs/0007-framework-detection-with-fallback.md","3d61354c9c896a34",{"html":188,"metadata":189},"\u003Cul>\n\u003Cli>\u003Cstrong>Status:\u003C/strong> Accepted\u003C/li>\n\u003Cli>\u003Cstrong>Date:\u003C/strong> 2026-09-24 (Retroactive)\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"context-and-problem-statement\">Context and Problem Statement\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#context-and-problem-statement\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Context and Problem Statement”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>Setup must preselect the right framework preset without interrogating the user about files they may not understand — but repository signals are unreliable: \u003Ccode dir=\"auto\">package.json\u003C/code> may be malformed, \u003Ccode dir=\"auto\">vercel.json\u003C/code> may be empty, and unknown stacks must still produce a working project.\u003C/p>\n\u003Cp>We needed detection rules that are helpful when signals exist and harmless when they do not.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"decision-drivers\">Decision Drivers\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#decision-drivers\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Decision Drivers”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>\u003Cstrong>Never block generation:\u003C/strong> A strange repo must still scaffold; a wrong guess the user can override beats a fatal error.\u003C/li>\n\u003Cli>\u003Cstrong>Deterministic precedence:\u003C/strong> Overlapping signals (e.g. \u003Ccode dir=\"auto\">express\u003C/code> inside a NestJS app) must resolve the same way every run.\u003C/li>\n\u003Cli>\u003Cstrong>Advance warning:\u003C/strong> Framework-specific runtime requirements (bind address, build output, adapter) should surface before infrastructure exists, not after the first 502.\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"considered-options\">Considered Options\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#considered-options\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Considered Options”\u003C/span>\u003C/a>\u003C/div>\n\u003Col>\n\u003Cli>\u003Cstrong>Interactive-only selection.\u003C/strong> (Rejected: slow, and headless/CI generation needs a non-interactive path.)\u003C/li>\n\u003Cli>\u003Cstrong>Strict detection, fail on ambiguity.\u003C/strong> (Rejected: malformed or partial configs would block scaffolding entirely.)\u003C/li>\n\u003Cli>\u003Cstrong>Precedence-ordered detection with silent ignore + static fallback.\u003C/strong> Checks run top-down (\u003Ccode dir=\"auto\">src/utils/detector.js\u003C/code>); malformed configs are treated as absent, never fatal; anything unmatched falls back to \u003Ccode dir=\"auto\">static\u003C/code>. Post-detection checks (\u003Ccode dir=\"auto\">src/utils/warnings.js\u003C/code>) flag fixable issues with copy-paste remedies.\u003C/li>\n\u003C/ol>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"decision-outcome\">Decision Outcome\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#decision-outcome\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Decision Outcome”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>\u003Cstrong>Chosen Option:\u003C/strong> Precedence-ordered detection with graceful fallback. Both \u003Ccode dir=\"auto\">dependencies\u003C/code> and \u003Ccode dir=\"auto\">devDependencies\u003C/code> are searched; empty or malformed signal files are ignored; headless mode without \u003Ccode dir=\"auto\">--framework\u003C/code> resolves the same way.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h3\">\u003Ch3 id=\"positive-consequences\">Positive Consequences\u003C/h3>\u003Ca class=\"sl-anchor-link\" href=\"#positive-consequences\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Positive Consequences”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>\u003Ccode dir=\"auto\">npx deploy-stack\u003C/code> succeeds on repos the tool has never seen, producing a deployable static project the user can refine.\u003C/li>\n\u003Cli>Warnings arrive with exact fixes at setup time, when they are cheapest to apply.\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h3\">\u003Ch3 id=\"negative-consequences\">Negative Consequences\u003C/h3>\u003Ca class=\"sl-anchor-link\" href=\"#negative-consequences\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Negative Consequences”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>A wrong-but-plausible guess (e.g. Express detected inside a larger framework) silently generates the wrong preset until the user notices.\u003C/li>\n\u003Cli>Detection rules must be maintained alongside the ecosystem or they rot into misdetection.\u003C/li>\n\u003C/ul>\n",{"headings":190,"localImagePaths":197,"remoteImagePaths":198,"frontmatter":199,"imagePaths":200},[191,192,193,194,195,196],{"depth":22,"slug":23,"text":24},{"depth":22,"slug":26,"text":27},{"depth":22,"slug":29,"text":30},{"depth":22,"slug":32,"text":33},{"depth":35,"slug":36,"text":37},{"depth":35,"slug":39,"text":40},[],[],{"title":179,"description":180},[],"adrs/0008-secrets-names-in-git-values-in-aws",{"id":201,"data":203,"body":209,"filePath":210,"digest":211,"rendered":212},{"title":204,"description":205,"editUrl":8,"head":206,"template":10,"sidebar":207,"pagefind":8,"draft":12},"Secrets Contract: Names in Git, Values in AWS","Commit secret key names to Terraform while values live only in Secrets Manager.",[],{"hidden":12,"attrs":208},{},"* **Status:** Accepted\n* **Date:** 2026-09-24 (Retroactive)\n\n## Context and Problem Statement\n\nDeployed containers need environment secrets, but committing `.env` contents would leak credentials into git history, and baking values into CI configuration would spread them across logs and workflow files. At the same time, Terraform must know *which* variables exist to wire them into the ECS task definition.\n\nWe needed a split that keeps values out of version control while keeping the variable set declarative and reviewable.\n\n## Decision Drivers\n\n* **Zero plaintext in git:** No secret value may ever be committed, including in history-friendly JSON files.\n* **Declarative wiring:** The task definition must be built from a committed, diffable source so secret rotation is a normal code review.\n* **Day-2 ergonomics:** Adding a variable vs. changing a value must have obviously different, safe procedures.\n\n## Considered Options\n\n1. **Commit `.env` and inject at build time.** (Rejected: values enter git history permanently and leak into image layers and CI logs.)\n2. **Names-only contract.** `secrets push` uploads values to the `\u003Cproject>-secrets` vault and writes only key *names* to `terraform/secret_keys.json`, which is committed. Terraform maps each name into the task definition; ECS resolves values from Secrets Manager at runtime.\n3. **Fully external management.** (Rejected: forces users onto out-of-band secret workflows on day one instead of the guided push/pull/audit loop.)\n\n## Decision Outcome\n\n**Chosen Option:** Names-only contract. Key-set changes require committing `secret_keys.json` and redeploying (the task definition is rebuilt); value-only changes take a rolling ECS restart with no redeploy.\n\n### Positive Consequences\n* `secret_keys.json` diffs in pull requests show exactly which variables were added or removed, with zero leak risk.\n* `secrets pull` / `secrets audit` close the onboarding and rotation loops without ever printing values into CI.\n\n### Negative Consequences\n* Forgetting to commit `secret_keys.json` after adding a variable produces a deploy that silently lacks it — a failure mode users must learn once.\n* Secret values remain invisible to code review by design, so a wrong value can only be caught at runtime.","src/content/docs/adrs/0008-secrets-names-in-git-values-in-aws.md","e55b417c69d72c8a",{"html":213,"metadata":214},"\u003Cul>\n\u003Cli>\u003Cstrong>Status:\u003C/strong> Accepted\u003C/li>\n\u003Cli>\u003Cstrong>Date:\u003C/strong> 2026-09-24 (Retroactive)\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"context-and-problem-statement\">Context and Problem Statement\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#context-and-problem-statement\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Context and Problem Statement”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>Deployed containers need environment secrets, but committing \u003Ccode dir=\"auto\">.env\u003C/code> contents would leak credentials into git history, and baking values into CI configuration would spread them across logs and workflow files. At the same time, Terraform must know \u003Cem>which\u003C/em> variables exist to wire them into the ECS task definition.\u003C/p>\n\u003Cp>We needed a split that keeps values out of version control while keeping the variable set declarative and reviewable.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"decision-drivers\">Decision Drivers\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#decision-drivers\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Decision Drivers”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>\u003Cstrong>Zero plaintext in git:\u003C/strong> No secret value may ever be committed, including in history-friendly JSON files.\u003C/li>\n\u003Cli>\u003Cstrong>Declarative wiring:\u003C/strong> The task definition must be built from a committed, diffable source so secret rotation is a normal code review.\u003C/li>\n\u003Cli>\u003Cstrong>Day-2 ergonomics:\u003C/strong> Adding a variable vs. changing a value must have obviously different, safe procedures.\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"considered-options\">Considered Options\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#considered-options\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Considered Options”\u003C/span>\u003C/a>\u003C/div>\n\u003Col>\n\u003Cli>\u003Cstrong>Commit \u003Ccode dir=\"auto\">.env\u003C/code> and inject at build time.\u003C/strong> (Rejected: values enter git history permanently and leak into image layers and CI logs.)\u003C/li>\n\u003Cli>\u003Cstrong>Names-only contract.\u003C/strong> \u003Ccode dir=\"auto\">secrets push\u003C/code> uploads values to the \u003Ccode dir=\"auto\"><project>-secrets\u003C/code> vault and writes only key \u003Cem>names\u003C/em> to \u003Ccode dir=\"auto\">terraform/secret_keys.json\u003C/code>, which is committed. Terraform maps each name into the task definition; ECS resolves values from Secrets Manager at runtime.\u003C/li>\n\u003Cli>\u003Cstrong>Fully external management.\u003C/strong> (Rejected: forces users onto out-of-band secret workflows on day one instead of the guided push/pull/audit loop.)\u003C/li>\n\u003C/ol>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"decision-outcome\">Decision Outcome\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#decision-outcome\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Decision Outcome”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>\u003Cstrong>Chosen Option:\u003C/strong> Names-only contract. Key-set changes require committing \u003Ccode dir=\"auto\">secret_keys.json\u003C/code> and redeploying (the task definition is rebuilt); value-only changes take a rolling ECS restart with no redeploy.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h3\">\u003Ch3 id=\"positive-consequences\">Positive Consequences\u003C/h3>\u003Ca class=\"sl-anchor-link\" href=\"#positive-consequences\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Positive Consequences”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>\u003Ccode dir=\"auto\">secret_keys.json\u003C/code> diffs in pull requests show exactly which variables were added or removed, with zero leak risk.\u003C/li>\n\u003Cli>\u003Ccode dir=\"auto\">secrets pull\u003C/code> / \u003Ccode dir=\"auto\">secrets audit\u003C/code> close the onboarding and rotation loops without ever printing values into CI.\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h3\">\u003Ch3 id=\"negative-consequences\">Negative Consequences\u003C/h3>\u003Ca class=\"sl-anchor-link\" href=\"#negative-consequences\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Negative Consequences”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>Forgetting to commit \u003Ccode dir=\"auto\">secret_keys.json\u003C/code> after adding a variable produces a deploy that silently lacks it — a failure mode users must learn once.\u003C/li>\n\u003Cli>Secret values remain invisible to code review by design, so a wrong value can only be caught at runtime.\u003C/li>\n\u003C/ul>\n",{"headings":215,"localImagePaths":222,"remoteImagePaths":223,"frontmatter":224,"imagePaths":225},[216,217,218,219,220,221],{"depth":22,"slug":23,"text":24},{"depth":22,"slug":26,"text":27},{"depth":22,"slug":29,"text":30},{"depth":22,"slug":32,"text":33},{"depth":35,"slug":36,"text":37},{"depth":35,"slug":39,"text":40},[],[],{"title":204,"description":205},[],"adrs/0009-regenerate-with-backup-on-rerun",{"id":226,"data":228,"body":234,"filePath":235,"digest":236,"rendered":237},{"title":229,"description":230,"editUrl":8,"head":231,"template":10,"sidebar":232,"pagefind":8,"draft":12},"Regenerate from Scratch with Backup on Re-Run","Re-running init backs up generated files and regenerates instead of merging.",[],{"hidden":12,"attrs":233},{},"* **Status:** Accepted\n* **Date:** 2026-09-24 (Retroactive)\n\n## Context and Problem Statement\n\nUsers re-run `npx deploy-stack` to change region, size, or framework — but by then the target directory contains previously generated `terraform/`, `Dockerfile`, and workflow files, possibly hand-edited. Merging new output into edited files risks silent half-applied configurations that are worse than either version.\n\nWe needed re-runs to be safe, predictable, and recoverable.\n\n## Decision Drivers\n\n* **Predictability:** Post-run state must equal what generation produces for the new inputs — no merge ghosts.\n* **Recoverability:** Hand edits and previous outputs must never be destroyed without a way back.\n* **Explicitness:** The user must always know exactly what moved and what to do next.\n\n## Considered Options\n\n1. **Three-way merge with user files.** (Rejected: generated IaC has no stable merge grammar; conflicts would be resolved by guessing.)\n2. **Refuse to overwrite.** (Rejected: makes legitimate reconfiguration (region, size, framework) a manual file-deletion chore.)\n3. **Backup and regenerate.** On conflict (`src/utils/backup.js`), offer Backup & Regenerate: move existing outputs to `.bak` files (additionally git-ignored so clutter never reaches GitHub), regenerate from scratch, and print the exact next steps.\n\n## Decision Outcome\n\n**Chosen Option:** Backup and regenerate. Setup never merges; it backs up, regenerates, and reports.\n\n### Positive Consequences\n* Re-runs are idempotent in effect: same inputs always yield the same tree.\n* No user file is ever destroyed; recovery is a file copy away.\n\n### Negative Consequences\n* Hand edits to generated files are silently forked into `.bak` copies the user must reconcile manually.\n* Repeated re-runs accumulate `.bak` clutter locally (mitigated by git-ignoring the pattern).","src/content/docs/adrs/0009-regenerate-with-backup-on-rerun.md","aaa15bc1f73bd2e8",{"html":238,"metadata":239},"\u003Cul>\n\u003Cli>\u003Cstrong>Status:\u003C/strong> Accepted\u003C/li>\n\u003Cli>\u003Cstrong>Date:\u003C/strong> 2026-09-24 (Retroactive)\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"context-and-problem-statement\">Context and Problem Statement\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#context-and-problem-statement\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Context and Problem Statement”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>Users re-run \u003Ccode dir=\"auto\">npx deploy-stack\u003C/code> to change region, size, or framework — but by then the target directory contains previously generated \u003Ccode dir=\"auto\">terraform/\u003C/code>, \u003Ccode dir=\"auto\">Dockerfile\u003C/code>, and workflow files, possibly hand-edited. Merging new output into edited files risks silent half-applied configurations that are worse than either version.\u003C/p>\n\u003Cp>We needed re-runs to be safe, predictable, and recoverable.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"decision-drivers\">Decision Drivers\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#decision-drivers\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Decision Drivers”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>\u003Cstrong>Predictability:\u003C/strong> Post-run state must equal what generation produces for the new inputs — no merge ghosts.\u003C/li>\n\u003Cli>\u003Cstrong>Recoverability:\u003C/strong> Hand edits and previous outputs must never be destroyed without a way back.\u003C/li>\n\u003Cli>\u003Cstrong>Explicitness:\u003C/strong> The user must always know exactly what moved and what to do next.\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"considered-options\">Considered Options\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#considered-options\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Considered Options”\u003C/span>\u003C/a>\u003C/div>\n\u003Col>\n\u003Cli>\u003Cstrong>Three-way merge with user files.\u003C/strong> (Rejected: generated IaC has no stable merge grammar; conflicts would be resolved by guessing.)\u003C/li>\n\u003Cli>\u003Cstrong>Refuse to overwrite.\u003C/strong> (Rejected: makes legitimate reconfiguration (region, size, framework) a manual file-deletion chore.)\u003C/li>\n\u003Cli>\u003Cstrong>Backup and regenerate.\u003C/strong> On conflict (\u003Ccode dir=\"auto\">src/utils/backup.js\u003C/code>), offer Backup & Regenerate: move existing outputs to \u003Ccode dir=\"auto\">.bak\u003C/code> files (additionally git-ignored so clutter never reaches GitHub), regenerate from scratch, and print the exact next steps.\u003C/li>\n\u003C/ol>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"decision-outcome\">Decision Outcome\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#decision-outcome\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Decision Outcome”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>\u003Cstrong>Chosen Option:\u003C/strong> Backup and regenerate. Setup never merges; it backs up, regenerates, and reports.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h3\">\u003Ch3 id=\"positive-consequences\">Positive Consequences\u003C/h3>\u003Ca class=\"sl-anchor-link\" href=\"#positive-consequences\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Positive Consequences”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>Re-runs are idempotent in effect: same inputs always yield the same tree.\u003C/li>\n\u003Cli>No user file is ever destroyed; recovery is a file copy away.\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h3\">\u003Ch3 id=\"negative-consequences\">Negative Consequences\u003C/h3>\u003Ca class=\"sl-anchor-link\" href=\"#negative-consequences\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Negative Consequences”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>Hand edits to generated files are silently forked into \u003Ccode dir=\"auto\">.bak\u003C/code> copies the user must reconcile manually.\u003C/li>\n\u003Cli>Repeated re-runs accumulate \u003Ccode dir=\"auto\">.bak\u003C/code> clutter locally (mitigated by git-ignoring the pattern).\u003C/li>\n\u003C/ul>\n",{"headings":240,"localImagePaths":247,"remoteImagePaths":248,"frontmatter":249,"imagePaths":250},[241,242,243,244,245,246],{"depth":22,"slug":23,"text":24},{"depth":22,"slug":26,"text":27},{"depth":22,"slug":29,"text":30},{"depth":22,"slug":32,"text":33},{"depth":35,"slug":36,"text":37},{"depth":35,"slug":39,"text":40},[],[],{"title":229,"description":230},[],"adrs/0010-advisory-only-security-scans",{"id":251,"data":253,"body":259,"filePath":260,"digest":261,"rendered":262},{"title":254,"description":255,"editUrl":8,"head":256,"template":10,"sidebar":257,"pagefind":8,"draft":12},"Advisory-Only Security Scans in the Pipeline","Trivy scans report vulnerabilities without blocking builds or deploys.",[],{"hidden":12,"attrs":258},{},"* **Status:** Accepted\n* **Date:** 2026-09-24 (Retroactive)\n\n## Context and Problem Statement\n\nThe generated pipeline scans both Terraform (`CRITICAL,HIGH`) and the built container image on every deploy. Failing the build on findings would enforce security posture — but base-image and transitive-dependency findings routinely arrive faster than fixes, which would turn the deployment pipeline into a lottery where routine pushes fail for reasons outside the user's code.\n\nWe needed scans to inform without holding deploys hostage.\n\n## Decision Drivers\n\n* **Deploy velocity:** A routine push must not fail because an upstream base image published a CVE overnight.\n* **Visibility:** Findings must still land where the team looks, on every run, not rot in a dashboard nobody opens.\n* **Reversibility:** The day the signal is clean enough to gate on, flipping the default must be a one-line change.\n\n## Considered Options\n\n1. **Blocking scans (`exit-code: 1`).** (Rejected: couples deploy success to upstream vulnerability disclosure timing; guarantees false-positive outages.)\n2. **No scans.** (Rejected: surrenders the IaC and image visibility the pipeline is well placed to provide.)\n3. **Advisory scans (`exit-code: 0`).** Trivy runs on every deploy for both IaC and image; results land in the GitHub step summary; the build proceeds regardless.\n\n## Decision Outcome\n\n**Chosen Option:** Advisory-only scans. Every finding is reported in the step summary; none blocks the rollout.\n\n### Positive Consequences\n* Deploys never fail for third-party CVEs; security signal accumulates without operational pain.\n* Teams adopt the pipeline without fearing day-one red builds from pre-existing findings.\n\n### Negative Consequences\n* Genuinely critical misconfigurations ship unless a human reads the summary — the signal only works if someone looks.\n* Without a ratchet (e.g. fail only on *new* findings), vulnerability debt can grow silently.","src/content/docs/adrs/0010-advisory-only-security-scans.md","d87a076b36710486",{"html":263,"metadata":264},"\u003Cul>\n\u003Cli>\u003Cstrong>Status:\u003C/strong> Accepted\u003C/li>\n\u003Cli>\u003Cstrong>Date:\u003C/strong> 2026-09-24 (Retroactive)\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"context-and-problem-statement\">Context and Problem Statement\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#context-and-problem-statement\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Context and Problem Statement”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>The generated pipeline scans both Terraform (\u003Ccode dir=\"auto\">CRITICAL,HIGH\u003C/code>) and the built container image on every deploy. Failing the build on findings would enforce security posture — but base-image and transitive-dependency findings routinely arrive faster than fixes, which would turn the deployment pipeline into a lottery where routine pushes fail for reasons outside the user’s code.\u003C/p>\n\u003Cp>We needed scans to inform without holding deploys hostage.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"decision-drivers\">Decision Drivers\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#decision-drivers\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Decision Drivers”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>\u003Cstrong>Deploy velocity:\u003C/strong> A routine push must not fail because an upstream base image published a CVE overnight.\u003C/li>\n\u003Cli>\u003Cstrong>Visibility:\u003C/strong> Findings must still land where the team looks, on every run, not rot in a dashboard nobody opens.\u003C/li>\n\u003Cli>\u003Cstrong>Reversibility:\u003C/strong> The day the signal is clean enough to gate on, flipping the default must be a one-line change.\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"considered-options\">Considered Options\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#considered-options\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Considered Options”\u003C/span>\u003C/a>\u003C/div>\n\u003Col>\n\u003Cli>\u003Cstrong>Blocking scans (\u003Ccode dir=\"auto\">exit-code: 1\u003C/code>).\u003C/strong> (Rejected: couples deploy success to upstream vulnerability disclosure timing; guarantees false-positive outages.)\u003C/li>\n\u003Cli>\u003Cstrong>No scans.\u003C/strong> (Rejected: surrenders the IaC and image visibility the pipeline is well placed to provide.)\u003C/li>\n\u003Cli>\u003Cstrong>Advisory scans (\u003Ccode dir=\"auto\">exit-code: 0\u003C/code>).\u003C/strong> Trivy runs on every deploy for both IaC and image; results land in the GitHub step summary; the build proceeds regardless.\u003C/li>\n\u003C/ol>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"decision-outcome\">Decision Outcome\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#decision-outcome\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Decision Outcome”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>\u003Cstrong>Chosen Option:\u003C/strong> Advisory-only scans. Every finding is reported in the step summary; none blocks the rollout.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h3\">\u003Ch3 id=\"positive-consequences\">Positive Consequences\u003C/h3>\u003Ca class=\"sl-anchor-link\" href=\"#positive-consequences\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Positive Consequences”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>Deploys never fail for third-party CVEs; security signal accumulates without operational pain.\u003C/li>\n\u003Cli>Teams adopt the pipeline without fearing day-one red builds from pre-existing findings.\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h3\">\u003Ch3 id=\"negative-consequences\">Negative Consequences\u003C/h3>\u003Ca class=\"sl-anchor-link\" href=\"#negative-consequences\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Negative Consequences”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>Genuinely critical misconfigurations ship unless a human reads the summary — the signal only works if someone looks.\u003C/li>\n\u003Cli>Without a ratchet (e.g. fail only on \u003Cem>new\u003C/em> findings), vulnerability debt can grow silently.\u003C/li>\n\u003C/ul>\n",{"headings":265,"localImagePaths":272,"remoteImagePaths":273,"frontmatter":274,"imagePaths":275},[266,267,268,269,270,271],{"depth":22,"slug":23,"text":24},{"depth":22,"slug":26,"text":27},{"depth":22,"slug":29,"text":30},{"depth":22,"slug":32,"text":33},{"depth":35,"slug":36,"text":37},{"depth":35,"slug":39,"text":40},[],[],{"title":254,"description":255},[],"cli/apply",{"id":276,"data":278,"body":284,"filePath":285,"digest":286,"rendered":287},{"title":279,"description":280,"editUrl":8,"head":281,"template":10,"sidebar":282,"pagefind":8,"draft":12},"apply","Provision or update your AWS infrastructure with Terraform.",[],{"hidden":12,"attrs":283},{},"Run the Terraform plan/apply flow against the generated configuration.\n\n## What it does\n\n- Verifies you are in a deploy-stack project (`terraform/main.tf` must exist), exiting otherwise, so `apply` never runs against the wrong directory.\n- Renders an infrastructure preview from your Terraform config and framework detection. With `--dry-run` it stops there and provisions nothing.\n- Otherwise runs `terraform init -upgrade` followed by `terraform apply -auto-approve` in `terraform/`, streaming progress, then prints the live URLs from the Terraform outputs (`cloudfront_url` and `alb_direct_url`) plus the `git push` command that deploys your app and clears the initial 503.\n- Without `--autoApprove`, asks for confirmation after the preview; declining aborts without provisioning anything.\n- If the S3 state bucket is missing (e.g. deleted manually), offers to recreate it and resume automatically instead of failing.\n- On the known GitHub OIDC provider conflict (`EntityAlreadyExists` for `token.actions.githubusercontent.com`), tells you to set `create_oidc_provider = false` in `terraform/oidc.tf` and re-run; other failures print the Terraform error and the manual `cd terraform && terraform apply` fallback.\n- Emits an `infrastructure_applied` telemetry event recording success or the error code.\n\n## Usage\n\n```bash\nnpx deploy-stack apply\nnpx deploy-stack apply --dry-run\n```\n\n## Flags\n\n| Flag | Description |\n| ---- | ----------- |\n| `--dry-run` | Render a preview of the planned changes without applying them. |\n\n`apply` shells out to the `terraform` binary in your generated `terraform/` directory and streams progress while it runs.","src/content/docs/cli/apply.md","114e13a3e2fbaf43",{"html":288,"metadata":289},"\u003Cp>Run the Terraform plan/apply flow against the generated configuration.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"what-it-does\">What it does\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#what-it-does\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “What it does”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>Verifies you are in a deploy-stack project (\u003Ccode dir=\"auto\">terraform/main.tf\u003C/code> must exist), exiting otherwise, so \u003Ccode dir=\"auto\">apply\u003C/code> never runs against the wrong directory.\u003C/li>\n\u003Cli>Renders an infrastructure preview from your Terraform config and framework detection. With \u003Ccode dir=\"auto\">--dry-run\u003C/code> it stops there and provisions nothing.\u003C/li>\n\u003Cli>Otherwise runs \u003Ccode dir=\"auto\">terraform init -upgrade\u003C/code> followed by \u003Ccode dir=\"auto\">terraform apply -auto-approve\u003C/code> in \u003Ccode dir=\"auto\">terraform/\u003C/code>, streaming progress, then prints the live URLs from the Terraform outputs (\u003Ccode dir=\"auto\">cloudfront_url\u003C/code> and \u003Ccode dir=\"auto\">alb_direct_url\u003C/code>) plus the \u003Ccode dir=\"auto\">git push\u003C/code> command that deploys your app and clears the initial 503.\u003C/li>\n\u003Cli>Without \u003Ccode dir=\"auto\">--autoApprove\u003C/code>, asks for confirmation after the preview; declining aborts without provisioning anything.\u003C/li>\n\u003Cli>If the S3 state bucket is missing (e.g. deleted manually), offers to recreate it and resume automatically instead of failing.\u003C/li>\n\u003Cli>On the known GitHub OIDC provider conflict (\u003Ccode dir=\"auto\">EntityAlreadyExists\u003C/code> for \u003Ccode dir=\"auto\">token.actions.githubusercontent.com\u003C/code>), tells you to set \u003Ccode dir=\"auto\">create_oidc_provider = false\u003C/code> in \u003Ccode dir=\"auto\">terraform/oidc.tf\u003C/code> and re-run; other failures print the Terraform error and the manual \u003Ccode dir=\"auto\">cd terraform && terraform apply\u003C/code> fallback.\u003C/li>\n\u003Cli>Emits an \u003Ccode dir=\"auto\">infrastructure_applied\u003C/code> telemetry event recording success or the error code.\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"usage\">Usage\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#usage\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Usage”\u003C/span>\u003C/a>\u003C/div>\n\u003Cdiv class=\"expressive-code\">\u003Clink rel=\"stylesheet\" href=\"/deploy-stack/_astro/ec.w36nc.css\">\u003Cscript type=\"module\" src=\"/deploy-stack/_astro/ec.0vx5m.js\">\u003C/script>\u003Cfigure class=\"frame is-terminal not-content\">\u003Cfigcaption class=\"header\">\u003Cspan class=\"title\">\u003C/span>\u003Cspan class=\"sr-only\">Terminal window\u003C/span>\u003C/figcaption>\u003Cpre data-language=\"bash\">\u003Ccode>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">npx\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">deploy-stack\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">apply\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">npx\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">deploy-stack\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">apply\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">--dry-run\u003C/span>\u003C/div>\u003C/div>\u003C/code>\u003C/pre>\u003Cdiv class=\"copy\">\u003Cdiv aria-live=\"polite\">\u003C/div>\u003Cbutton title=\"Copy to clipboard\" data-copied=\"Copied!\" data-code=\"npx deploy-stack applynpx deploy-stack apply --dry-run\">\u003Cdiv>\u003C/div>\u003C/button>\u003C/div>\u003C/figure>\u003C/div>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"flags\">Flags\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#flags\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Flags”\u003C/span>\u003C/a>\u003C/div>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>Flag\u003C/th>\n\u003Cth>Description\u003C/th>\n\u003C/tr>\n\u003C/thead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Ccode dir=\"auto\">--dry-run\u003C/code>\u003C/td>\n\u003Ctd>Render a preview of the planned changes without applying them.\u003C/td>\n\u003C/tr>\n\u003C/tbody>\n\u003C/table>\n\u003Cp>\u003Ccode dir=\"auto\">apply\u003C/code> shells out to the \u003Ccode dir=\"auto\">terraform\u003C/code> binary in your generated \u003Ccode dir=\"auto\">terraform/\u003C/code> directory and streams progress while it runs.\u003C/p>\n",{"headings":290,"localImagePaths":300,"remoteImagePaths":301,"frontmatter":302,"imagePaths":303},[291,294,297],{"depth":22,"slug":292,"text":293},"what-it-does","What it does",{"depth":22,"slug":295,"text":296},"usage","Usage",{"depth":22,"slug":298,"text":299},"flags","Flags",[],[],{"title":279,"description":280},[],"cli/destroy",{"id":304,"data":306,"body":312,"filePath":313,"digest":314,"rendered":315},{"title":307,"description":308,"editUrl":8,"head":309,"template":10,"sidebar":310,"pagefind":8,"draft":12},"destroy","Tear down all AWS resources provisioned for this project.",[],{"hidden":12,"attrs":311},{},"Permanently delete the AWS infrastructure created by `apply` when a project is retired or needs a clean rebuild, protecting you from ongoing AWS charges.\n\n## What it does\n\n- Verifies you are in a deploy-stack project (`terraform/backend.tf` must exist) and that the `terraform` binary is installed, exiting otherwise.\n- Asks for explicit confirmation before doing anything destructive; declining cancels with no changes.\n- Runs `terraform destroy -auto-approve` in `terraform/`, streaming progress, so all compute resources (ECS, ALB, database, and related resources) are removed.\n- Parses the state bucket name and region out of `terraform/backend.tf` (region defaults to `us-east-2` when not found), then optionally asks whether to also empty and delete the S3 state bucket via `teardownStateBucket`. Answering \"No\" keeps the bucket so `apply` can restore the infrastructure later.\n- Emits an `infrastructure_destroyed` telemetry event recording success and whether the state bucket was retained.\n\n## Usage\n\n```bash\nnpx deploy-stack destroy\n```\n\n## Flags\n\nThis command accepts no CLI flags. Both confirmation prompts are interactive.\n\n## See also\n\n- [apply](/deploy-stack/cli/apply/)\n- [doctor](/deploy-stack/cli/doctor/)","src/content/docs/cli/destroy.md","43734f953a6f2a86",{"html":316,"metadata":317},"\u003Cp>Permanently delete the AWS infrastructure created by \u003Ccode dir=\"auto\">apply\u003C/code> when a project is retired or needs a clean rebuild, protecting you from ongoing AWS charges.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"what-it-does\">What it does\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#what-it-does\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “What it does”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>Verifies you are in a deploy-stack project (\u003Ccode dir=\"auto\">terraform/backend.tf\u003C/code> must exist) and that the \u003Ccode dir=\"auto\">terraform\u003C/code> binary is installed, exiting otherwise.\u003C/li>\n\u003Cli>Asks for explicit confirmation before doing anything destructive; declining cancels with no changes.\u003C/li>\n\u003Cli>Runs \u003Ccode dir=\"auto\">terraform destroy -auto-approve\u003C/code> in \u003Ccode dir=\"auto\">terraform/\u003C/code>, streaming progress, so all compute resources (ECS, ALB, database, and related resources) are removed.\u003C/li>\n\u003Cli>Parses the state bucket name and region out of \u003Ccode dir=\"auto\">terraform/backend.tf\u003C/code> (region defaults to \u003Ccode dir=\"auto\">us-east-2\u003C/code> when not found), then optionally asks whether to also empty and delete the S3 state bucket via \u003Ccode dir=\"auto\">teardownStateBucket\u003C/code>. Answering “No” keeps the bucket so \u003Ccode dir=\"auto\">apply\u003C/code> can restore the infrastructure later.\u003C/li>\n\u003Cli>Emits an \u003Ccode dir=\"auto\">infrastructure_destroyed\u003C/code> telemetry event recording success and whether the state bucket was retained.\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"usage\">Usage\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#usage\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Usage”\u003C/span>\u003C/a>\u003C/div>\n\u003Cdiv class=\"expressive-code\">\u003Clink rel=\"stylesheet\" href=\"/deploy-stack/_astro/ec.w36nc.css\">\u003Cscript type=\"module\" src=\"/deploy-stack/_astro/ec.0vx5m.js\">\u003C/script>\u003Cfigure class=\"frame is-terminal not-content\">\u003Cfigcaption class=\"header\">\u003Cspan class=\"title\">\u003C/span>\u003Cspan class=\"sr-only\">Terminal window\u003C/span>\u003C/figcaption>\u003Cpre data-language=\"bash\">\u003Ccode>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">npx\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">deploy-stack\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">destroy\u003C/span>\u003C/div>\u003C/div>\u003C/code>\u003C/pre>\u003Cdiv class=\"copy\">\u003Cdiv aria-live=\"polite\">\u003C/div>\u003Cbutton title=\"Copy to clipboard\" data-copied=\"Copied!\" data-code=\"npx deploy-stack destroy\">\u003Cdiv>\u003C/div>\u003C/button>\u003C/div>\u003C/figure>\u003C/div>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"flags\">Flags\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#flags\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Flags”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>This command accepts no CLI flags. Both confirmation prompts are interactive.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"see-also\">See also\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#see-also\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “See also”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>\u003Ca href=\"/deploy-stack/cli/apply/\">apply\u003C/a>\u003C/li>\n\u003Cli>\u003Ca href=\"/deploy-stack/cli/doctor/\">doctor\u003C/a>\u003C/li>\n\u003C/ul>\n",{"headings":318,"localImagePaths":325,"remoteImagePaths":326,"frontmatter":327,"imagePaths":328},[319,320,321,322],{"depth":22,"slug":292,"text":293},{"depth":22,"slug":295,"text":296},{"depth":22,"slug":298,"text":299},{"depth":22,"slug":323,"text":324},"see-also","See also",[],[],{"title":307,"description":308},[],"cli/diagnose",{"id":329,"data":331,"body":337,"filePath":338,"digest":339,"rendered":340},{"title":332,"description":333,"editUrl":8,"head":334,"template":10,"sidebar":335,"pagefind":8,"draft":12},"diagnose","Diagnose failing ECS deployments from logs and task state.",[],{"hidden":12,"attrs":336},{},"Inspect recent ECS task failures and CloudWatch logs for the current project.\n\n## What it does\n\n- Saves you from digging through the AWS console by finding the most recently stopped ECS task for this project and showing why it stopped plus the failing container's recent log events (up to 50 lines).\n- Resolves its inputs automatically: the AWS region from `AWS_REGION`, falling back to `region` in `terraform/main.tf` (default `us-east-2`); the cluster (`\u003Cproject-name>-cluster`, overridable via `ECS_CLUSTER`); and the log group (`/ecs/\u003Cproject-name>`, overridable via `ECS_LOG_GROUP`).\n- Lists up to 10 recent stopped tasks, describes up to 5 of them, and diagnoses the most recently stopped one: stopped reason, failing container name, exit code, and container reason.\n- Makes no changes to your infrastructure; it is read-only. Prints a healthy message and exits when no stopped tasks exist.\n- On expired AWS credentials, points you to `aws sso login` / `aws configure` and the [AWS credentials guide](/deploy-stack/guides/aws-credentials/), then exits with code 1 instead of throwing.\n- Emits a `diagnose_run` telemetry event recording success and whether the service was healthy.\n\n## Usage\n\n```bash\nnpx deploy-stack diagnose\nnpx deploy-stack wtf\n```\n\n`wtf` is an alias for `diagnose`.\n\n## Flags\n\nThis command accepts no CLI flags. Region, cluster, and log group are resolved as described above, not from flags.\n\n## See also\n\n- [exec](/deploy-stack/cli/exec/)\n- [status](/deploy-stack/cli/status/)","src/content/docs/cli/diagnose.md","d2d3353612a0d225",{"html":341,"metadata":342},"\u003Cp>Inspect recent ECS task failures and CloudWatch logs for the current project.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"what-it-does\">What it does\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#what-it-does\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “What it does”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>Saves you from digging through the AWS console by finding the most recently stopped ECS task for this project and showing why it stopped plus the failing container’s recent log events (up to 50 lines).\u003C/li>\n\u003Cli>Resolves its inputs automatically: the AWS region from \u003Ccode dir=\"auto\">AWS_REGION\u003C/code>, falling back to \u003Ccode dir=\"auto\">region\u003C/code> in \u003Ccode dir=\"auto\">terraform/main.tf\u003C/code> (default \u003Ccode dir=\"auto\">us-east-2\u003C/code>); the cluster (\u003Ccode dir=\"auto\"><project-name>-cluster\u003C/code>, overridable via \u003Ccode dir=\"auto\">ECS_CLUSTER\u003C/code>); and the log group (\u003Ccode dir=\"auto\">/ecs/<project-name>\u003C/code>, overridable via \u003Ccode dir=\"auto\">ECS_LOG_GROUP\u003C/code>).\u003C/li>\n\u003Cli>Lists up to 10 recent stopped tasks, describes up to 5 of them, and diagnoses the most recently stopped one: stopped reason, failing container name, exit code, and container reason.\u003C/li>\n\u003Cli>Makes no changes to your infrastructure; it is read-only. Prints a healthy message and exits when no stopped tasks exist.\u003C/li>\n\u003Cli>On expired AWS credentials, points you to \u003Ccode dir=\"auto\">aws sso login\u003C/code> / \u003Ccode dir=\"auto\">aws configure\u003C/code> and the \u003Ca href=\"/deploy-stack/guides/aws-credentials/\">AWS credentials guide\u003C/a>, then exits with code 1 instead of throwing.\u003C/li>\n\u003Cli>Emits a \u003Ccode dir=\"auto\">diagnose_run\u003C/code> telemetry event recording success and whether the service was healthy.\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"usage\">Usage\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#usage\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Usage”\u003C/span>\u003C/a>\u003C/div>\n\u003Cdiv class=\"expressive-code\">\u003Clink rel=\"stylesheet\" href=\"/deploy-stack/_astro/ec.w36nc.css\">\u003Cscript type=\"module\" src=\"/deploy-stack/_astro/ec.0vx5m.js\">\u003C/script>\u003Cfigure class=\"frame is-terminal not-content\">\u003Cfigcaption class=\"header\">\u003Cspan class=\"title\">\u003C/span>\u003Cspan class=\"sr-only\">Terminal window\u003C/span>\u003C/figcaption>\u003Cpre data-language=\"bash\">\u003Ccode>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">npx\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">deploy-stack\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">diagnose\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">npx\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">deploy-stack\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">wtf\u003C/span>\u003C/div>\u003C/div>\u003C/code>\u003C/pre>\u003Cdiv class=\"copy\">\u003Cdiv aria-live=\"polite\">\u003C/div>\u003Cbutton title=\"Copy to clipboard\" data-copied=\"Copied!\" data-code=\"npx deploy-stack diagnosenpx deploy-stack wtf\">\u003Cdiv>\u003C/div>\u003C/button>\u003C/div>\u003C/figure>\u003C/div>\n\u003Cp>\u003Ccode dir=\"auto\">wtf\u003C/code> is an alias for \u003Ccode dir=\"auto\">diagnose\u003C/code>.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"flags\">Flags\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#flags\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Flags”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>This command accepts no CLI flags. Region, cluster, and log group are resolved as described above, not from flags.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"see-also\">See also\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#see-also\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “See also”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>\u003Ca href=\"/deploy-stack/cli/exec/\">exec\u003C/a>\u003C/li>\n\u003Cli>\u003Ca href=\"/deploy-stack/cli/status/\">status\u003C/a>\u003C/li>\n\u003C/ul>\n",{"headings":343,"localImagePaths":348,"remoteImagePaths":349,"frontmatter":350,"imagePaths":351},[344,345,346,347],{"depth":22,"slug":292,"text":293},{"depth":22,"slug":295,"text":296},{"depth":22,"slug":298,"text":299},{"depth":22,"slug":323,"text":324},[],[],{"title":332,"description":333},[],"cli/doctor",{"id":352,"data":354,"body":360,"filePath":361,"digest":362,"rendered":363},{"title":355,"description":356,"editUrl":8,"head":357,"template":10,"sidebar":358,"pagefind":8,"draft":12},"doctor","Check that required tools are installed before provisioning.",[],{"hidden":12,"attrs":359},{},"Verify your machine is ready to provision and deploy, telling you exactly which dependency to install when something is missing.\n\n## What it does\n\n- Checks for the four required binaries — `terraform`, `aws` (AWS CLI), `docker`, and `git` — and prints a pass/fail line for each with a Homebrew install hint for anything missing.\n- Makes no changes to your project or cloud resources; it is a read-only check.\n- Prints a success message when everything is present, or a reminder to install the missing dependencies first.\n- Emits a `doctor_run` telemetry event recording whether all checks passed.\n\n## Usage\n\n```bash\nnpx deploy-stack doctor\n```\n\n## Flags\n\nThis command accepts no CLI flags.\n\n## See also\n\n- [npx deploy-stack (init)](/deploy-stack/cli/init/)\n- [apply](/deploy-stack/cli/apply/)","src/content/docs/cli/doctor.md","64b50414e9f1a2cf",{"html":364,"metadata":365},"\u003Cp>Verify your machine is ready to provision and deploy, telling you exactly which dependency to install when something is missing.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"what-it-does\">What it does\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#what-it-does\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “What it does”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>Checks for the four required binaries — \u003Ccode dir=\"auto\">terraform\u003C/code>, \u003Ccode dir=\"auto\">aws\u003C/code> (AWS CLI), \u003Ccode dir=\"auto\">docker\u003C/code>, and \u003Ccode dir=\"auto\">git\u003C/code> — and prints a pass/fail line for each with a Homebrew install hint for anything missing.\u003C/li>\n\u003Cli>Makes no changes to your project or cloud resources; it is a read-only check.\u003C/li>\n\u003Cli>Prints a success message when everything is present, or a reminder to install the missing dependencies first.\u003C/li>\n\u003Cli>Emits a \u003Ccode dir=\"auto\">doctor_run\u003C/code> telemetry event recording whether all checks passed.\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"usage\">Usage\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#usage\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Usage”\u003C/span>\u003C/a>\u003C/div>\n\u003Cdiv class=\"expressive-code\">\u003Clink rel=\"stylesheet\" href=\"/deploy-stack/_astro/ec.w36nc.css\">\u003Cscript type=\"module\" src=\"/deploy-stack/_astro/ec.0vx5m.js\">\u003C/script>\u003Cfigure class=\"frame is-terminal not-content\">\u003Cfigcaption class=\"header\">\u003Cspan class=\"title\">\u003C/span>\u003Cspan class=\"sr-only\">Terminal window\u003C/span>\u003C/figcaption>\u003Cpre data-language=\"bash\">\u003Ccode>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">npx\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">deploy-stack\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">doctor\u003C/span>\u003C/div>\u003C/div>\u003C/code>\u003C/pre>\u003Cdiv class=\"copy\">\u003Cdiv aria-live=\"polite\">\u003C/div>\u003Cbutton title=\"Copy to clipboard\" data-copied=\"Copied!\" data-code=\"npx deploy-stack doctor\">\u003Cdiv>\u003C/div>\u003C/button>\u003C/div>\u003C/figure>\u003C/div>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"flags\">Flags\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#flags\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Flags”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>This command accepts no CLI flags.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"see-also\">See also\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#see-also\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “See also”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>\u003Ca href=\"/deploy-stack/cli/init/\">npx deploy-stack (init)\u003C/a>\u003C/li>\n\u003Cli>\u003Ca href=\"/deploy-stack/cli/apply/\">apply\u003C/a>\u003C/li>\n\u003C/ul>\n",{"headings":366,"localImagePaths":371,"remoteImagePaths":372,"frontmatter":373,"imagePaths":374},[367,368,369,370],{"depth":22,"slug":292,"text":293},{"depth":22,"slug":295,"text":296},{"depth":22,"slug":298,"text":299},{"depth":22,"slug":323,"text":324},[],[],{"title":355,"description":356},[],"cli/eject",{"id":375,"data":377,"body":383,"filePath":384,"digest":385,"rendered":386},{"title":378,"description":379,"editUrl":8,"head":380,"template":10,"sidebar":381,"pagefind":8,"draft":12},"eject","Decouple your project from deploy-stack into vanilla Terraform.",[],{"hidden":12,"attrs":382},{},"Take permanent, sole ownership of your infrastructure files when you no longer want the CLI managing them, while keeping everything running in AWS.\n\n## What it does\n\n- Asks for explicit confirmation (defaulting to \"No\"); declining cancels with no changes.\n- Strips deploy-stack metadata from your local files: removes the `# deploy-stack generated infrastructure` header and the `default_tags { tags = { ManagedBy = \"deploy-stack\" } }` block from `terraform/main.tf`, and removes the `# deploy-stack backups` block from `.gitignore`.\n- Recursively deletes every `*.bak.*` backup file in the project (skipping `node_modules` and `.git`).\n- Leaves your infrastructure fully operational as raw, standalone Terraform. As a final step, run `terraform apply` inside `terraform/` so AWS syncs state and removes the live `ManagedBy` tags.\n- Emits a `project_ejected` telemetry event. This cannot be undone.\n\n## Usage\n\n```bash\nnpx deploy-stack eject\n```\n\n## Flags\n\nThis command accepts no CLI flags. The confirmation prompt is interactive.\n\n## See also\n\n- [apply](/deploy-stack/cli/apply/)\n- [destroy](/deploy-stack/cli/destroy/)","src/content/docs/cli/eject.md","9626cd5513197c7a",{"html":387,"metadata":388},"\u003Cp>Take permanent, sole ownership of your infrastructure files when you no longer want the CLI managing them, while keeping everything running in AWS.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"what-it-does\">What it does\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#what-it-does\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “What it does”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>Asks for explicit confirmation (defaulting to “No”); declining cancels with no changes.\u003C/li>\n\u003Cli>Strips deploy-stack metadata from your local files: removes the \u003Ccode dir=\"auto\"># deploy-stack generated infrastructure\u003C/code> header and the \u003Ccode dir=\"auto\">default_tags { tags = { ManagedBy = \"deploy-stack\" } }\u003C/code> block from \u003Ccode dir=\"auto\">terraform/main.tf\u003C/code>, and removes the \u003Ccode dir=\"auto\"># deploy-stack backups\u003C/code> block from \u003Ccode dir=\"auto\">.gitignore\u003C/code>.\u003C/li>\n\u003Cli>Recursively deletes every \u003Ccode dir=\"auto\">*.bak.*\u003C/code> backup file in the project (skipping \u003Ccode dir=\"auto\">node_modules\u003C/code> and \u003Ccode dir=\"auto\">.git\u003C/code>).\u003C/li>\n\u003Cli>Leaves your infrastructure fully operational as raw, standalone Terraform. As a final step, run \u003Ccode dir=\"auto\">terraform apply\u003C/code> inside \u003Ccode dir=\"auto\">terraform/\u003C/code> so AWS syncs state and removes the live \u003Ccode dir=\"auto\">ManagedBy\u003C/code> tags.\u003C/li>\n\u003Cli>Emits a \u003Ccode dir=\"auto\">project_ejected\u003C/code> telemetry event. This cannot be undone.\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"usage\">Usage\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#usage\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Usage”\u003C/span>\u003C/a>\u003C/div>\n\u003Cdiv class=\"expressive-code\">\u003Clink rel=\"stylesheet\" href=\"/deploy-stack/_astro/ec.w36nc.css\">\u003Cscript type=\"module\" src=\"/deploy-stack/_astro/ec.0vx5m.js\">\u003C/script>\u003Cfigure class=\"frame is-terminal not-content\">\u003Cfigcaption class=\"header\">\u003Cspan class=\"title\">\u003C/span>\u003Cspan class=\"sr-only\">Terminal window\u003C/span>\u003C/figcaption>\u003Cpre data-language=\"bash\">\u003Ccode>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">npx\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">deploy-stack\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">eject\u003C/span>\u003C/div>\u003C/div>\u003C/code>\u003C/pre>\u003Cdiv class=\"copy\">\u003Cdiv aria-live=\"polite\">\u003C/div>\u003Cbutton title=\"Copy to clipboard\" data-copied=\"Copied!\" data-code=\"npx deploy-stack eject\">\u003Cdiv>\u003C/div>\u003C/button>\u003C/div>\u003C/figure>\u003C/div>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"flags\">Flags\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#flags\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Flags”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>This command accepts no CLI flags. The confirmation prompt is interactive.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"see-also\">See also\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#see-also\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “See also”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>\u003Ca href=\"/deploy-stack/cli/apply/\">apply\u003C/a>\u003C/li>\n\u003Cli>\u003Ca href=\"/deploy-stack/cli/destroy/\">destroy\u003C/a>\u003C/li>\n\u003C/ul>\n",{"headings":389,"localImagePaths":394,"remoteImagePaths":395,"frontmatter":396,"imagePaths":397},[390,391,392,393],{"depth":22,"slug":292,"text":293},{"depth":22,"slug":295,"text":296},{"depth":22,"slug":298,"text":299},{"depth":22,"slug":323,"text":324},[],[],{"title":378,"description":379},[],"cli/exec",{"id":398,"data":400,"body":406,"filePath":407,"digest":408,"rendered":409},{"title":401,"description":402,"editUrl":8,"head":403,"template":10,"sidebar":404,"pagefind":8,"draft":12},"exec","Open an interactive shell inside your running ECS container.",[],{"hidden":12,"attrs":405},{},"Drop into a secure shell inside your live Fargate container to inspect files, check environment variables, or debug a running app — without opening the AWS console.\n\n## What it does\n\n- Finds a running container for the current project automatically and opens an interactive shell (`/bin/sh` by default) via ECS Exec.\n- Resolves its inputs automatically: cluster (`\u003Cproject-name>-cluster`, overridable via `ECS_CLUSTER`), service (`\u003Cproject-name>-service`, overridable via `ECS_SERVICE`), container (`\u003Cproject-name>-container`, overridable via `ECS_CONTAINER`), and region (`--region` → `AWS_REGION` → `terraform/main.tf` → `us-east-2`).\n- Checks that the AWS CLI is installed first; if missing, prints install links and exits 1 instead of failing cryptically.\n- Checks that the Session Manager plugin is installed next; if missing, prints install instructions for your OS (`brew install session-manager-plugin` on Mac, download links on Windows/Linux) and exits 1.\n- When no containers are running (e.g. scaled to zero or still booting), explains that a running container is required, points you to `status` and `apply`, and exits 1.\n- On expired AWS credentials, points you to `aws sso login` / `aws configure` and exits 1 instead of throwing.\n- Emits an `exec_run` telemetry event recording success and outcome.\n\n## Usage\n\n```bash\nnpx deploy-stack exec\nnpx deploy-stack exec --command /bin/bash\nnpx deploy-stack exec --service myapp-service --container myapp-container\n```\n\nType `exit` to leave the shell.\n\n## Flags\n\n| Flag | Description |\n| ---- | ----------- |\n| `[service]` | Service to connect to. Defaults to the project service. |\n| `--service \u003Cname>` | Explicit service name override. |\n| `--cluster \u003Cname>` | Explicit cluster name override. |\n| `--container \u003Cname>` | Explicit container name override. |\n| `--command \u003Ccmd>` | Shell to open (default `/bin/sh`). |\n| `--region \u003Cregion>` | Explicit AWS region override. |\n\n## Prerequisites\n\n- Run `npx deploy-stack apply` first: ECS Exec access (`enable_execute_command` plus the container's session permissions) is provisioned with your infrastructure. If the connection is refused on an older deployment, re-run `apply` to enable it.\n- Install the AWS CLI and the Session Manager plugin (`brew install session-manager-plugin` on Mac; the command prints the right instructions for your OS when it's missing). On expired credentials, refresh with `aws sso login` or `aws configure`. See the [AWS credentials guide](/deploy-stack/guides/aws-credentials/).\n\n## See also\n\n- [status](/deploy-stack/cli/status/)\n- [logs](/deploy-stack/cli/logs/)\n- [diagnose](/deploy-stack/cli/diagnose/)","src/content/docs/cli/exec.md","814ea63782bee473",{"html":410,"metadata":411},"\u003Cp>Drop into a secure shell inside your live Fargate container to inspect files, check environment variables, or debug a running app — without opening the AWS console.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"what-it-does\">What it does\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#what-it-does\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “What it does”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>Finds a running container for the current project automatically and opens an interactive shell (\u003Ccode dir=\"auto\">/bin/sh\u003C/code> by default) via ECS Exec.\u003C/li>\n\u003Cli>Resolves its inputs automatically: cluster (\u003Ccode dir=\"auto\"><project-name>-cluster\u003C/code>, overridable via \u003Ccode dir=\"auto\">ECS_CLUSTER\u003C/code>), service (\u003Ccode dir=\"auto\"><project-name>-service\u003C/code>, overridable via \u003Ccode dir=\"auto\">ECS_SERVICE\u003C/code>), container (\u003Ccode dir=\"auto\"><project-name>-container\u003C/code>, overridable via \u003Ccode dir=\"auto\">ECS_CONTAINER\u003C/code>), and region (\u003Ccode dir=\"auto\">--region\u003C/code> → \u003Ccode dir=\"auto\">AWS_REGION\u003C/code> → \u003Ccode dir=\"auto\">terraform/main.tf\u003C/code> → \u003Ccode dir=\"auto\">us-east-2\u003C/code>).\u003C/li>\n\u003Cli>Checks that the AWS CLI is installed first; if missing, prints install links and exits 1 instead of failing cryptically.\u003C/li>\n\u003Cli>Checks that the Session Manager plugin is installed next; if missing, prints install instructions for your OS (\u003Ccode dir=\"auto\">brew install session-manager-plugin\u003C/code> on Mac, download links on Windows/Linux) and exits 1.\u003C/li>\n\u003Cli>When no containers are running (e.g. scaled to zero or still booting), explains that a running container is required, points you to \u003Ccode dir=\"auto\">status\u003C/code> and \u003Ccode dir=\"auto\">apply\u003C/code>, and exits 1.\u003C/li>\n\u003Cli>On expired AWS credentials, points you to \u003Ccode dir=\"auto\">aws sso login\u003C/code> / \u003Ccode dir=\"auto\">aws configure\u003C/code> and exits 1 instead of throwing.\u003C/li>\n\u003Cli>Emits an \u003Ccode dir=\"auto\">exec_run\u003C/code> telemetry event recording success and outcome.\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"usage\">Usage\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#usage\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Usage”\u003C/span>\u003C/a>\u003C/div>\n\u003Cdiv class=\"expressive-code\">\u003Clink rel=\"stylesheet\" href=\"/deploy-stack/_astro/ec.w36nc.css\">\u003Cscript type=\"module\" src=\"/deploy-stack/_astro/ec.0vx5m.js\">\u003C/script>\u003Cfigure class=\"frame is-terminal not-content\">\u003Cfigcaption class=\"header\">\u003Cspan class=\"title\">\u003C/span>\u003Cspan class=\"sr-only\">Terminal window\u003C/span>\u003C/figcaption>\u003Cpre data-language=\"bash\">\u003Ccode>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">npx\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">deploy-stack\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">exec\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">npx\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">deploy-stack\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">exec\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">--command\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">/bin/bash\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">npx\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">deploy-stack\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">exec\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">--service\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">myapp-service\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">--container\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">myapp-container\u003C/span>\u003C/div>\u003C/div>\u003C/code>\u003C/pre>\u003Cdiv class=\"copy\">\u003Cdiv aria-live=\"polite\">\u003C/div>\u003Cbutton title=\"Copy to clipboard\" data-copied=\"Copied!\" data-code=\"npx deploy-stack execnpx deploy-stack exec --command /bin/bashnpx deploy-stack exec --service myapp-service --container myapp-container\">\u003Cdiv>\u003C/div>\u003C/button>\u003C/div>\u003C/figure>\u003C/div>\n\u003Cp>Type \u003Ccode dir=\"auto\">exit\u003C/code> to leave the shell.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"flags\">Flags\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#flags\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Flags”\u003C/span>\u003C/a>\u003C/div>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>Flag\u003C/th>\n\u003Cth>Description\u003C/th>\n\u003C/tr>\n\u003C/thead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Ccode dir=\"auto\">[service]\u003C/code>\u003C/td>\n\u003Ctd>Service to connect to. Defaults to the project service.\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode dir=\"auto\">--service <name>\u003C/code>\u003C/td>\n\u003Ctd>Explicit service name override.\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode dir=\"auto\">--cluster <name>\u003C/code>\u003C/td>\n\u003Ctd>Explicit cluster name override.\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode dir=\"auto\">--container <name>\u003C/code>\u003C/td>\n\u003Ctd>Explicit container name override.\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode dir=\"auto\">--command <cmd>\u003C/code>\u003C/td>\n\u003Ctd>Shell to open (default \u003Ccode dir=\"auto\">/bin/sh\u003C/code>).\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode dir=\"auto\">--region <region>\u003C/code>\u003C/td>\n\u003Ctd>Explicit AWS region override.\u003C/td>\n\u003C/tr>\n\u003C/tbody>\n\u003C/table>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"prerequisites\">Prerequisites\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#prerequisites\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Prerequisites”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>Run \u003Ccode dir=\"auto\">npx deploy-stack apply\u003C/code> first: ECS Exec access (\u003Ccode dir=\"auto\">enable_execute_command\u003C/code> plus the container’s session permissions) is provisioned with your infrastructure. If the connection is refused on an older deployment, re-run \u003Ccode dir=\"auto\">apply\u003C/code> to enable it.\u003C/li>\n\u003Cli>Install the AWS CLI and the Session Manager plugin (\u003Ccode dir=\"auto\">brew install session-manager-plugin\u003C/code> on Mac; the command prints the right instructions for your OS when it’s missing). On expired credentials, refresh with \u003Ccode dir=\"auto\">aws sso login\u003C/code> or \u003Ccode dir=\"auto\">aws configure\u003C/code>. See the \u003Ca href=\"/deploy-stack/guides/aws-credentials/\">AWS credentials guide\u003C/a>.\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"see-also\">See also\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#see-also\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “See also”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>\u003Ca href=\"/deploy-stack/cli/status/\">status\u003C/a>\u003C/li>\n\u003Cli>\u003Ca href=\"/deploy-stack/cli/logs/\">logs\u003C/a>\u003C/li>\n\u003Cli>\u003Ca href=\"/deploy-stack/cli/diagnose/\">diagnose\u003C/a>\u003C/li>\n\u003C/ul>\n",{"headings":412,"localImagePaths":420,"remoteImagePaths":421,"frontmatter":422,"imagePaths":423},[413,414,415,416,419],{"depth":22,"slug":292,"text":293},{"depth":22,"slug":295,"text":296},{"depth":22,"slug":298,"text":299},{"depth":22,"slug":417,"text":418},"prerequisites","Prerequisites",{"depth":22,"slug":323,"text":324},[],[],{"title":401,"description":402},[],"cli/gc",{"id":424,"data":426,"body":432,"filePath":433,"digest":434,"rendered":435},{"title":427,"description":428,"editUrl":8,"head":429,"template":10,"sidebar":430,"pagefind":8,"draft":12},"gc","Discover and delete orphaned ECR images, CloudWatch log groups, and Elastic IPs.",[],{"hidden":12,"attrs":431},{},"Dry-run discovery and interactive deletion of orphaned AWS resources left behind by failed deployments, deleted PR previews, or manual console changes — protecting your AWS bill without leaving the terminal.\n\n## What it does\n\n- Scans ECR repositories matching `\u003Cproject-name>-*` for untagged images and batch-deletes them.\n- Scans CloudWatch log groups under `/ecs/\u003Cproject-name>-*` (preview leftovers; the live `/ecs/\u003Cproject-name>` group is never matched) and deletes them.\n- Scans Elastic IPs and releases any without an association (stops the hourly unused-EIP charge).\n- Paginates all discovery APIs, so large accounts are fully scanned.\n- Prints a categorized dry-run summary with per-target counts before asking anything.\n\n## Usage\n\n```bash\nnpx deploy-stack gc\nnpx deploy-stack gc --region eu-west-1\n```\n\n## Flags\n\n| Flag | Description |\n| ---- | ----------- |\n| `--region \u003Cregion>` | Explicit AWS region override. |\n| `--project-name \u003Cname>` | Explicit project name override (defaults to the current directory name). |\n\n## Safety\n\nDeletion requires explicit interactive confirmation (`Are you sure you want to permanently delete these orphaned resources? (y/N)`, defaulting to no). There is intentionally no `--yes` flag, so the command can never wipe resources from a CI pipeline by accident. Declining or cancelling deletes nothing; finding nothing skips the prompt entirely.\n\n## See also\n\n- [status](/deploy-stack/cli/status/)\n- [Ephemeral PR Previews](/deploy-stack/guides/ephemeral-pr-previews/)","src/content/docs/cli/gc.md","1313ea81dc390dcb",{"html":436,"metadata":437},"\u003Cp>Dry-run discovery and interactive deletion of orphaned AWS resources left behind by failed deployments, deleted PR previews, or manual console changes — protecting your AWS bill without leaving the terminal.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"what-it-does\">What it does\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#what-it-does\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “What it does”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>Scans ECR repositories matching \u003Ccode dir=\"auto\"><project-name>-*\u003C/code> for untagged images and batch-deletes them.\u003C/li>\n\u003Cli>Scans CloudWatch log groups under \u003Ccode dir=\"auto\">/ecs/<project-name>-*\u003C/code> (preview leftovers; the live \u003Ccode dir=\"auto\">/ecs/<project-name>\u003C/code> group is never matched) and deletes them.\u003C/li>\n\u003Cli>Scans Elastic IPs and releases any without an association (stops the hourly unused-EIP charge).\u003C/li>\n\u003Cli>Paginates all discovery APIs, so large accounts are fully scanned.\u003C/li>\n\u003Cli>Prints a categorized dry-run summary with per-target counts before asking anything.\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"usage\">Usage\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#usage\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Usage”\u003C/span>\u003C/a>\u003C/div>\n\u003Cdiv class=\"expressive-code\">\u003Clink rel=\"stylesheet\" href=\"/deploy-stack/_astro/ec.w36nc.css\">\u003Cscript type=\"module\" src=\"/deploy-stack/_astro/ec.0vx5m.js\">\u003C/script>\u003Cfigure class=\"frame is-terminal not-content\">\u003Cfigcaption class=\"header\">\u003Cspan class=\"title\">\u003C/span>\u003Cspan class=\"sr-only\">Terminal window\u003C/span>\u003C/figcaption>\u003Cpre data-language=\"bash\">\u003Ccode>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">npx\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">deploy-stack\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">gc\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">npx\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">deploy-stack\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">gc\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">--region\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">eu-west-1\u003C/span>\u003C/div>\u003C/div>\u003C/code>\u003C/pre>\u003Cdiv class=\"copy\">\u003Cdiv aria-live=\"polite\">\u003C/div>\u003Cbutton title=\"Copy to clipboard\" data-copied=\"Copied!\" data-code=\"npx deploy-stack gcnpx deploy-stack gc --region eu-west-1\">\u003Cdiv>\u003C/div>\u003C/button>\u003C/div>\u003C/figure>\u003C/div>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"flags\">Flags\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#flags\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Flags”\u003C/span>\u003C/a>\u003C/div>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>Flag\u003C/th>\n\u003Cth>Description\u003C/th>\n\u003C/tr>\n\u003C/thead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Ccode dir=\"auto\">--region <region>\u003C/code>\u003C/td>\n\u003Ctd>Explicit AWS region override.\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode dir=\"auto\">--project-name <name>\u003C/code>\u003C/td>\n\u003Ctd>Explicit project name override (defaults to the current directory name).\u003C/td>\n\u003C/tr>\n\u003C/tbody>\n\u003C/table>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"safety\">Safety\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#safety\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Safety”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>Deletion requires explicit interactive confirmation (\u003Ccode dir=\"auto\">Are you sure you want to permanently delete these orphaned resources? (y/N)\u003C/code>, defaulting to no). There is intentionally no \u003Ccode dir=\"auto\">--yes\u003C/code> flag, so the command can never wipe resources from a CI pipeline by accident. Declining or cancelling deletes nothing; finding nothing skips the prompt entirely.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"see-also\">See also\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#see-also\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “See also”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>\u003Ca href=\"/deploy-stack/cli/status/\">status\u003C/a>\u003C/li>\n\u003Cli>\u003Ca href=\"/deploy-stack/guides/ephemeral-pr-previews/\">Ephemeral PR Previews\u003C/a>\u003C/li>\n\u003C/ul>\n",{"headings":438,"localImagePaths":446,"remoteImagePaths":447,"frontmatter":448,"imagePaths":449},[439,440,441,442,445],{"depth":22,"slug":292,"text":293},{"depth":22,"slug":295,"text":296},{"depth":22,"slug":298,"text":299},{"depth":22,"slug":443,"text":444},"safety","Safety",{"depth":22,"slug":323,"text":324},[],[],{"title":427,"description":428},[],"cli/init",{"id":450,"data":452,"body":458,"filePath":459,"digest":460,"rendered":461},{"title":453,"description":454,"editUrl":8,"head":455,"template":10,"sidebar":456,"pagefind":8,"draft":12},"Initializing Project (npx deploy-stack)","Scaffold production-ready AWS infrastructure and CI/CD pipelines.",[],{"hidden":12,"attrs":457},{},"Generate Terraform, Docker, and GitHub Actions files for your project.\n\n## What it does\n\n- Turns your codebase into a deployable AWS project: auto-detects your framework, `Procfile`, `vercel.json`, and `docker-compose.yml`, warns about framework-specific migration issues (NestJS bind address, Next.js standalone output, SvelteKit/Astro adapters), then provisions the remote-state S3 bucket and synthesizes Terraform, Docker, and CI/CD files.\n- Backs up any existing generated files before overwriting them, and writes AI assistant rule files for the assistants you choose (advanced mode) or the ones already present in your repo (quickstart mode).\n- Finishes with the exact next steps: the `apply` command to provision, and the `git` commands to commit and push.\n- Emits `project_provisioned` and `cli-error` telemetry events (disable with `--no-telemetry`).\n\n## Usage\n\n```bash\nnpx deploy-stack\nnpx deploy-stack --headless --framework=nextjs --region=us-east-2\n```\n\nRunning with no subcommand starts the interactive setup wizard (`init` is the default command).\n\n## Headless flags\n\n| Flag | Description |\n| ---- | ----------- |\n| `--headless` | Bypass all interactive prompts (for CI/CD and automation). |\n| `--framework=\u003Cname>` | `node`, `nestjs`, `nextjs`, `nuxt`, `svelte`, `python`, `django`, `rails`, `go`, `static`. |\n| `--region=\u003Cregion>` | AWS region (e.g. `us-east-1`). |\n| `--port=\u003Cport>` | Container port your app listens on. |\n| `--size=\u003Csize>` | Fargate task size preset. |\n| `--healthCheckPath=\u003Cpath>` | ALB health-check path. |\n| `--desiredCount=\u003Cn>` | Number of tasks to run. |\n| `--branch=\u003Cname>` | Branch the CI workflow deploys. |\n| `--needsDatabase` | Provision a managed database. |\n| `--enablePrPreviews` | Enable ephemeral PR preview environments. |\n| `--dir=\u003Cpath>` | Target directory for generated files. |\n| `--preconfigured` | Skip framework-specific warnings (for preconfigured setups). |\n| `--no-telemetry` | Disable telemetry for this run. |\n\nNew here? Start with the [Quickstart](/deploy-stack/guides/quickstart/).\n\nSee the [Supported Frameworks](/deploy-stack/guides/frameworks/) guide for detection rules and per-framework requirements, and the [Headless Mode guide](/deploy-stack/guides/headless/) for automation examples.\n\nAfter scaffolding, continue with [CI/CD Pipeline & First Deploy](/deploy-stack/guides/cicd-pipeline/).","src/content/docs/cli/init.md","87034502d6476a33",{"html":462,"metadata":463},"\u003Cp>Generate Terraform, Docker, and GitHub Actions files for your project.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"what-it-does\">What it does\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#what-it-does\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “What it does”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>Turns your codebase into a deployable AWS project: auto-detects your framework, \u003Ccode dir=\"auto\">Procfile\u003C/code>, \u003Ccode dir=\"auto\">vercel.json\u003C/code>, and \u003Ccode dir=\"auto\">docker-compose.yml\u003C/code>, warns about framework-specific migration issues (NestJS bind address, Next.js standalone output, SvelteKit/Astro adapters), then provisions the remote-state S3 bucket and synthesizes Terraform, Docker, and CI/CD files.\u003C/li>\n\u003Cli>Backs up any existing generated files before overwriting them, and writes AI assistant rule files for the assistants you choose (advanced mode) or the ones already present in your repo (quickstart mode).\u003C/li>\n\u003Cli>Finishes with the exact next steps: the \u003Ccode dir=\"auto\">apply\u003C/code> command to provision, and the \u003Ccode dir=\"auto\">git\u003C/code> commands to commit and push.\u003C/li>\n\u003Cli>Emits \u003Ccode dir=\"auto\">project_provisioned\u003C/code> and \u003Ccode dir=\"auto\">cli-error\u003C/code> telemetry events (disable with \u003Ccode dir=\"auto\">--no-telemetry\u003C/code>).\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"usage\">Usage\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#usage\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Usage”\u003C/span>\u003C/a>\u003C/div>\n\u003Cdiv class=\"expressive-code\">\u003Clink rel=\"stylesheet\" href=\"/deploy-stack/_astro/ec.w36nc.css\">\u003Cscript type=\"module\" src=\"/deploy-stack/_astro/ec.0vx5m.js\">\u003C/script>\u003Cfigure class=\"frame is-terminal not-content\">\u003Cfigcaption class=\"header\">\u003Cspan class=\"title\">\u003C/span>\u003Cspan class=\"sr-only\">Terminal window\u003C/span>\u003C/figcaption>\u003Cpre data-language=\"bash\">\u003Ccode>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">npx\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">deploy-stack\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">npx\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">deploy-stack\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">--headless\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">--framework=nextjs\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">--region=us-east-2\u003C/span>\u003C/div>\u003C/div>\u003C/code>\u003C/pre>\u003Cdiv class=\"copy\">\u003Cdiv aria-live=\"polite\">\u003C/div>\u003Cbutton title=\"Copy to clipboard\" data-copied=\"Copied!\" data-code=\"npx deploy-stacknpx deploy-stack --headless --framework=nextjs --region=us-east-2\">\u003Cdiv>\u003C/div>\u003C/button>\u003C/div>\u003C/figure>\u003C/div>\n\u003Cp>Running with no subcommand starts the interactive setup wizard (\u003Ccode dir=\"auto\">init\u003C/code> is the default command).\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"headless-flags\">Headless flags\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#headless-flags\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Headless flags”\u003C/span>\u003C/a>\u003C/div>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>Flag\u003C/th>\n\u003Cth>Description\u003C/th>\n\u003C/tr>\n\u003C/thead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Ccode dir=\"auto\">--headless\u003C/code>\u003C/td>\n\u003Ctd>Bypass all interactive prompts (for CI/CD and automation).\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode dir=\"auto\">--framework=<name>\u003C/code>\u003C/td>\n\u003Ctd>\u003Ccode dir=\"auto\">node\u003C/code>, \u003Ccode dir=\"auto\">nestjs\u003C/code>, \u003Ccode dir=\"auto\">nextjs\u003C/code>, \u003Ccode dir=\"auto\">nuxt\u003C/code>, \u003Ccode dir=\"auto\">svelte\u003C/code>, \u003Ccode dir=\"auto\">python\u003C/code>, \u003Ccode dir=\"auto\">django\u003C/code>, \u003Ccode dir=\"auto\">rails\u003C/code>, \u003Ccode dir=\"auto\">go\u003C/code>, \u003Ccode dir=\"auto\">static\u003C/code>.\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode dir=\"auto\">--region=<region>\u003C/code>\u003C/td>\n\u003Ctd>AWS region (e.g. \u003Ccode dir=\"auto\">us-east-1\u003C/code>).\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode dir=\"auto\">--port=<port>\u003C/code>\u003C/td>\n\u003Ctd>Container port your app listens on.\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode dir=\"auto\">--size=<size>\u003C/code>\u003C/td>\n\u003Ctd>Fargate task size preset.\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode dir=\"auto\">--healthCheckPath=<path>\u003C/code>\u003C/td>\n\u003Ctd>ALB health-check path.\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode dir=\"auto\">--desiredCount=<n>\u003C/code>\u003C/td>\n\u003Ctd>Number of tasks to run.\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode dir=\"auto\">--branch=<name>\u003C/code>\u003C/td>\n\u003Ctd>Branch the CI workflow deploys.\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode dir=\"auto\">--needsDatabase\u003C/code>\u003C/td>\n\u003Ctd>Provision a managed database.\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode dir=\"auto\">--enablePrPreviews\u003C/code>\u003C/td>\n\u003Ctd>Enable ephemeral PR preview environments.\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode dir=\"auto\">--dir=<path>\u003C/code>\u003C/td>\n\u003Ctd>Target directory for generated files.\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode dir=\"auto\">--preconfigured\u003C/code>\u003C/td>\n\u003Ctd>Skip framework-specific warnings (for preconfigured setups).\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode dir=\"auto\">--no-telemetry\u003C/code>\u003C/td>\n\u003Ctd>Disable telemetry for this run.\u003C/td>\n\u003C/tr>\n\u003C/tbody>\n\u003C/table>\n\u003Cp>New here? Start with the \u003Ca href=\"/deploy-stack/guides/quickstart/\">Quickstart\u003C/a>.\u003C/p>\n\u003Cp>See the \u003Ca href=\"/deploy-stack/guides/frameworks/\">Supported Frameworks\u003C/a> guide for detection rules and per-framework requirements, and the \u003Ca href=\"/deploy-stack/guides/headless/\">Headless Mode guide\u003C/a> for automation examples.\u003C/p>\n\u003Cp>After scaffolding, continue with \u003Ca href=\"/deploy-stack/guides/cicd-pipeline/\">CI/CD Pipeline & First Deploy\u003C/a>.\u003C/p>\n",{"headings":464,"localImagePaths":470,"remoteImagePaths":471,"frontmatter":472,"imagePaths":473},[465,466,467],{"depth":22,"slug":292,"text":293},{"depth":22,"slug":295,"text":296},{"depth":22,"slug":468,"text":469},"headless-flags","Headless flags",[],[],{"title":453,"description":454},[],"cli/logs",{"id":474,"data":476,"body":482,"filePath":483,"digest":484,"rendered":485},{"title":477,"description":478,"editUrl":8,"head":479,"template":10,"sidebar":480,"pagefind":8,"draft":12},"logs","Stream CloudWatch logs for your ECS service.",[],{"hidden":12,"attrs":481},{},"Stream recent and live CloudWatch logs for the current project, without opening the AWS console.\n\n## What it does\n\n- Resolves the log group (`/ecs/\u003Cproject-name>`, overridable via `ECS_LOG_GROUP`) and region (`--region` → `AWS_REGION` → `terraform/main.tf` → `us-east-2`) automatically.\n- Prints recent lines with dimmed ISO timestamps and task IDs; errors in red, warnings in yellow.\n- With `-f`, polls every 2 seconds until Ctrl+C, which exits cleanly.\n- On expired credentials, prints the `aws sso login` / `aws configure` hint and exits 1; on a missing log group, suggests the matching `aws logs describe-log-groups` lookup instead of throwing.\n- Emits a `logs_streamed` telemetry event recording success and filter options.\n\n## Usage\n\n```bash\nnpx deploy-stack logs\nnpx deploy-stack logs api --tail 100 --error\nnpx deploy-stack logs -f --since 5m\n```\n\n## Flags\n\n| Flag | Description |\n| ---- | ----------- |\n| `[service]` | Service/container filter. Defaults to the project service. |\n| `--tail \u003Cn>` | Recent lines to show (default `50`). |\n| `-f, --follow` | Stream live until interrupted. |\n| `--error` | Show only error lines (`ERROR`, `FATAL`, `Exception`, `fail`, `5XX`). |\n| `--since \u003Cduration>` | Look-back window, e.g. `5m`, `1h`, `1d` (default `1h` for one-shot reads). |\n| `--region \u003Cregion>` | Explicit AWS region override. |\n\n## See also\n\n- [exec](/deploy-stack/cli/exec/)\n- [status](/deploy-stack/cli/status/)","src/content/docs/cli/logs.md","ec383fe7378b8eb1",{"html":486,"metadata":487},"\u003Cp>Stream recent and live CloudWatch logs for the current project, without opening the AWS console.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"what-it-does\">What it does\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#what-it-does\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “What it does”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>Resolves the log group (\u003Ccode dir=\"auto\">/ecs/<project-name>\u003C/code>, overridable via \u003Ccode dir=\"auto\">ECS_LOG_GROUP\u003C/code>) and region (\u003Ccode dir=\"auto\">--region\u003C/code> → \u003Ccode dir=\"auto\">AWS_REGION\u003C/code> → \u003Ccode dir=\"auto\">terraform/main.tf\u003C/code> → \u003Ccode dir=\"auto\">us-east-2\u003C/code>) automatically.\u003C/li>\n\u003Cli>Prints recent lines with dimmed ISO timestamps and task IDs; errors in red, warnings in yellow.\u003C/li>\n\u003Cli>With \u003Ccode dir=\"auto\">-f\u003C/code>, polls every 2 seconds until Ctrl+C, which exits cleanly.\u003C/li>\n\u003Cli>On expired credentials, prints the \u003Ccode dir=\"auto\">aws sso login\u003C/code> / \u003Ccode dir=\"auto\">aws configure\u003C/code> hint and exits 1; on a missing log group, suggests the matching \u003Ccode dir=\"auto\">aws logs describe-log-groups\u003C/code> lookup instead of throwing.\u003C/li>\n\u003Cli>Emits a \u003Ccode dir=\"auto\">logs_streamed\u003C/code> telemetry event recording success and filter options.\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"usage\">Usage\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#usage\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Usage”\u003C/span>\u003C/a>\u003C/div>\n\u003Cdiv class=\"expressive-code\">\u003Clink rel=\"stylesheet\" href=\"/deploy-stack/_astro/ec.w36nc.css\">\u003Cscript type=\"module\" src=\"/deploy-stack/_astro/ec.0vx5m.js\">\u003C/script>\u003Cfigure class=\"frame is-terminal not-content\">\u003Cfigcaption class=\"header\">\u003Cspan class=\"title\">\u003C/span>\u003Cspan class=\"sr-only\">Terminal window\u003C/span>\u003C/figcaption>\u003Cpre data-language=\"bash\">\u003Ccode>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">npx\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">deploy-stack\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">logs\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">npx\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">deploy-stack\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">logs\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">api\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">--tail\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#F78C6C;--1:#AA0982\">100\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">--error\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">npx\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">deploy-stack\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">logs\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">-f\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">--since\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">5m\u003C/span>\u003C/div>\u003C/div>\u003C/code>\u003C/pre>\u003Cdiv class=\"copy\">\u003Cdiv aria-live=\"polite\">\u003C/div>\u003Cbutton title=\"Copy to clipboard\" data-copied=\"Copied!\" data-code=\"npx deploy-stack logsnpx deploy-stack logs api --tail 100 --errornpx deploy-stack logs -f --since 5m\">\u003Cdiv>\u003C/div>\u003C/button>\u003C/div>\u003C/figure>\u003C/div>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"flags\">Flags\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#flags\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Flags”\u003C/span>\u003C/a>\u003C/div>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>Flag\u003C/th>\n\u003Cth>Description\u003C/th>\n\u003C/tr>\n\u003C/thead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Ccode dir=\"auto\">[service]\u003C/code>\u003C/td>\n\u003Ctd>Service/container filter. Defaults to the project service.\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode dir=\"auto\">--tail <n>\u003C/code>\u003C/td>\n\u003Ctd>Recent lines to show (default \u003Ccode dir=\"auto\">50\u003C/code>).\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode dir=\"auto\">-f, --follow\u003C/code>\u003C/td>\n\u003Ctd>Stream live until interrupted.\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode dir=\"auto\">--error\u003C/code>\u003C/td>\n\u003Ctd>Show only error lines (\u003Ccode dir=\"auto\">ERROR\u003C/code>, \u003Ccode dir=\"auto\">FATAL\u003C/code>, \u003Ccode dir=\"auto\">Exception\u003C/code>, \u003Ccode dir=\"auto\">fail\u003C/code>, \u003Ccode dir=\"auto\">5XX\u003C/code>).\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode dir=\"auto\">--since <duration>\u003C/code>\u003C/td>\n\u003Ctd>Look-back window, e.g. \u003Ccode dir=\"auto\">5m\u003C/code>, \u003Ccode dir=\"auto\">1h\u003C/code>, \u003Ccode dir=\"auto\">1d\u003C/code> (default \u003Ccode dir=\"auto\">1h\u003C/code> for one-shot reads).\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode dir=\"auto\">--region <region>\u003C/code>\u003C/td>\n\u003Ctd>Explicit AWS region override.\u003C/td>\n\u003C/tr>\n\u003C/tbody>\n\u003C/table>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"see-also\">See also\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#see-also\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “See also”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>\u003Ca href=\"/deploy-stack/cli/exec/\">exec\u003C/a>\u003C/li>\n\u003Cli>\u003Ca href=\"/deploy-stack/cli/status/\">status\u003C/a>\u003C/li>\n\u003C/ul>\n",{"headings":488,"localImagePaths":493,"remoteImagePaths":494,"frontmatter":495,"imagePaths":496},[489,490,491,492],{"depth":22,"slug":292,"text":293},{"depth":22,"slug":295,"text":296},{"depth":22,"slug":298,"text":299},{"depth":22,"slug":323,"text":324},[],[],{"title":477,"description":478},[],"cli/secrets",{"id":497,"data":499,"body":505,"filePath":506,"digest":507,"rendered":508},{"title":500,"description":501,"editUrl":8,"head":502,"template":10,"sidebar":503,"pagefind":8,"draft":12},"secrets","Push, pull, and audit environment secrets synced with AWS Secrets Manager.",[],{"hidden":12,"attrs":504},{},"Sync your local `.env` file with the Secrets Manager vault provisioned for this project, so your deployed app reads the values at runtime without plaintext secrets ever touching the repo or CI/CD pipelines.\n\n## secrets push\n\nUploads a local env file to the `\u003Cproject-name>-secrets` vault via `UpdateSecretCommand`, then writes the pushed key names to `terraform/secret_keys.json` so Terraform and CI redeploy know which variables exist.\n\n```bash\nnpx deploy-stack secrets push\nnpx deploy-stack secrets push .env.production\n```\n\nThe optional positional argument is the path of the env file to push (resolved relative to the project root). It defaults to `.env` when omitted or blank.\n\n**Smart follow-up based on what changed:**\n\n- **Key names changed** (added/removed variables) — commit `terraform/secret_keys.json` and push to GitHub to trigger a deployment with the new variables. The ECS task definition is rebuilt from the updated key map.\n- **Only values changed** (same key set) — the CLI offers a rolling ECS restart (`forceNewDeployment`) so running tasks pick up the new values immediately, no redeploy required.\n\nThe AWS region is resolved from the `region` setting in `terraform/main.tf`, falling back to `AWS_REGION` or your AWS profile default. Emits a `secrets_pushed` telemetry event. Exits non-zero on failure.\n\n## secrets pull\n\nFetches the remote JSON payload from the `\u003Cproject-name>-secrets` vault and merges it into your local env file — useful for onboarding a new machine or recovering after losing `.env`.\n\n```bash\nnpx deploy-stack secrets pull\nnpx deploy-stack secrets pull .env\n```\n\n**Merge behavior:**\n\n- Remote keys are appended after your existing local keys; existing local order is preserved.\n- Local-only variables are kept — pull never deletes them.\n- If a key exists locally and remotely with different values, the CLI asks `Conflicting variables found. Overwrite local values with remote?` In `--headless` mode it overwrites automatically.\n\nValues are written in standard `KEY=\"value\"` format. Emits a `secrets_pull` telemetry event. Exits non-zero on failure (e.g. no remote vault yet — run `secrets push` first).\n\n## secrets audit\n\nCompares your local env file against the remote vault and prints a colored drift report — no files are modified.\n\n```bash\nnpx deploy-stack secrets audit\n```\n\n- `+ KEY (Missing locally)` in green — exists in AWS but not in your `.env`.\n- `~ KEY (Mismatched value)` in yellow — exists in both with different values.\n- `- KEY (Not tracked in AWS)` in dim — exists locally but was never pushed.\n\nEnds with `Audit complete. N drifted variable(s) found.` Emits a `secrets_audit` telemetry event.\n\n## Prerequisites\n\n- Run `npx deploy-stack apply` first: the `\u003Cproject-name>-secrets` vault is created during provisioning. If it does not exist yet, each command points you back to `apply`.\n- Valid AWS credentials. On expired credentials, refresh with `aws sso login` or `aws configure`. See the [AWS credentials guide](/deploy-stack/guides/aws-credentials/).\n\n## A note on `terraform/secret_keys.json`\n\nThis file contains **key names only** (e.g. `[\"API_KEY\"]`), never values — it is safe to commit, and it **must** be committed: Terraform reads it during deployment to map each key into your ECS task definition.\n\n## See also\n\n- [Secrets management guide](/deploy-stack/guides/secrets-management/)\n- [apply](/deploy-stack/cli/apply/)","src/content/docs/cli/secrets.md","6bbce89b7b21be47",{"html":509,"metadata":510},"\u003Cp>Sync your local \u003Ccode dir=\"auto\">.env\u003C/code> file with the Secrets Manager vault provisioned for this project, so your deployed app reads the values at runtime without plaintext secrets ever touching the repo or CI/CD pipelines.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"secrets-push\">secrets push\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#secrets-push\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “secrets push”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>Uploads a local env file to the \u003Ccode dir=\"auto\"><project-name>-secrets\u003C/code> vault via \u003Ccode dir=\"auto\">UpdateSecretCommand\u003C/code>, then writes the pushed key names to \u003Ccode dir=\"auto\">terraform/secret_keys.json\u003C/code> so Terraform and CI redeploy know which variables exist.\u003C/p>\n\u003Cdiv class=\"expressive-code\">\u003Clink rel=\"stylesheet\" href=\"/deploy-stack/_astro/ec.w36nc.css\">\u003Cscript type=\"module\" src=\"/deploy-stack/_astro/ec.0vx5m.js\">\u003C/script>\u003Cfigure class=\"frame is-terminal not-content\">\u003Cfigcaption class=\"header\">\u003Cspan class=\"title\">\u003C/span>\u003Cspan class=\"sr-only\">Terminal window\u003C/span>\u003C/figcaption>\u003Cpre data-language=\"bash\">\u003Ccode>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">npx\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">deploy-stack\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">secrets\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">push\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">npx\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">deploy-stack\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">secrets\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">push\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">.env.production\u003C/span>\u003C/div>\u003C/div>\u003C/code>\u003C/pre>\u003Cdiv class=\"copy\">\u003Cdiv aria-live=\"polite\">\u003C/div>\u003Cbutton title=\"Copy to clipboard\" data-copied=\"Copied!\" data-code=\"npx deploy-stack secrets pushnpx deploy-stack secrets push .env.production\">\u003Cdiv>\u003C/div>\u003C/button>\u003C/div>\u003C/figure>\u003C/div>\n\u003Cp>The optional positional argument is the path of the env file to push (resolved relative to the project root). It defaults to \u003Ccode dir=\"auto\">.env\u003C/code> when omitted or blank.\u003C/p>\n\u003Cp>\u003Cstrong>Smart follow-up based on what changed:\u003C/strong>\u003C/p>\n\u003Cul>\n\u003Cli>\u003Cstrong>Key names changed\u003C/strong> (added/removed variables) — commit \u003Ccode dir=\"auto\">terraform/secret_keys.json\u003C/code> and push to GitHub to trigger a deployment with the new variables. The ECS task definition is rebuilt from the updated key map.\u003C/li>\n\u003Cli>\u003Cstrong>Only values changed\u003C/strong> (same key set) — the CLI offers a rolling ECS restart (\u003Ccode dir=\"auto\">forceNewDeployment\u003C/code>) so running tasks pick up the new values immediately, no redeploy required.\u003C/li>\n\u003C/ul>\n\u003Cp>The AWS region is resolved from the \u003Ccode dir=\"auto\">region\u003C/code> setting in \u003Ccode dir=\"auto\">terraform/main.tf\u003C/code>, falling back to \u003Ccode dir=\"auto\">AWS_REGION\u003C/code> or your AWS profile default. Emits a \u003Ccode dir=\"auto\">secrets_pushed\u003C/code> telemetry event. Exits non-zero on failure.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"secrets-pull\">secrets pull\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#secrets-pull\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “secrets pull”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>Fetches the remote JSON payload from the \u003Ccode dir=\"auto\"><project-name>-secrets\u003C/code> vault and merges it into your local env file — useful for onboarding a new machine or recovering after losing \u003Ccode dir=\"auto\">.env\u003C/code>.\u003C/p>\n\u003Cdiv class=\"expressive-code\">\u003Cfigure class=\"frame is-terminal not-content\">\u003Cfigcaption class=\"header\">\u003Cspan class=\"title\">\u003C/span>\u003Cspan class=\"sr-only\">Terminal window\u003C/span>\u003C/figcaption>\u003Cpre data-language=\"bash\">\u003Ccode>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">npx\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">deploy-stack\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">secrets\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">pull\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">npx\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">deploy-stack\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">secrets\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">pull\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">.env\u003C/span>\u003C/div>\u003C/div>\u003C/code>\u003C/pre>\u003Cdiv class=\"copy\">\u003Cdiv aria-live=\"polite\">\u003C/div>\u003Cbutton title=\"Copy to clipboard\" data-copied=\"Copied!\" data-code=\"npx deploy-stack secrets pullnpx deploy-stack secrets pull .env\">\u003Cdiv>\u003C/div>\u003C/button>\u003C/div>\u003C/figure>\u003C/div>\n\u003Cp>\u003Cstrong>Merge behavior:\u003C/strong>\u003C/p>\n\u003Cul>\n\u003Cli>Remote keys are appended after your existing local keys; existing local order is preserved.\u003C/li>\n\u003Cli>Local-only variables are kept — pull never deletes them.\u003C/li>\n\u003Cli>If a key exists locally and remotely with different values, the CLI asks \u003Ccode dir=\"auto\">Conflicting variables found. Overwrite local values with remote?\u003C/code> In \u003Ccode dir=\"auto\">--headless\u003C/code> mode it overwrites automatically.\u003C/li>\n\u003C/ul>\n\u003Cp>Values are written in standard \u003Ccode dir=\"auto\">KEY=\"value\"\u003C/code> format. Emits a \u003Ccode dir=\"auto\">secrets_pull\u003C/code> telemetry event. Exits non-zero on failure (e.g. no remote vault yet — run \u003Ccode dir=\"auto\">secrets push\u003C/code> first).\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"secrets-audit\">secrets audit\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#secrets-audit\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “secrets audit”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>Compares your local env file against the remote vault and prints a colored drift report — no files are modified.\u003C/p>\n\u003Cdiv class=\"expressive-code\">\u003Cfigure class=\"frame is-terminal not-content\">\u003Cfigcaption class=\"header\">\u003Cspan class=\"title\">\u003C/span>\u003Cspan class=\"sr-only\">Terminal window\u003C/span>\u003C/figcaption>\u003Cpre data-language=\"bash\">\u003Ccode>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">npx\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">deploy-stack\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">secrets\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">audit\u003C/span>\u003C/div>\u003C/div>\u003C/code>\u003C/pre>\u003Cdiv class=\"copy\">\u003Cdiv aria-live=\"polite\">\u003C/div>\u003Cbutton title=\"Copy to clipboard\" data-copied=\"Copied!\" data-code=\"npx deploy-stack secrets audit\">\u003Cdiv>\u003C/div>\u003C/button>\u003C/div>\u003C/figure>\u003C/div>\n\u003Cul>\n\u003Cli>\u003Ccode dir=\"auto\">+ KEY (Missing locally)\u003C/code> in green — exists in AWS but not in your \u003Ccode dir=\"auto\">.env\u003C/code>.\u003C/li>\n\u003Cli>\u003Ccode dir=\"auto\">~ KEY (Mismatched value)\u003C/code> in yellow — exists in both with different values.\u003C/li>\n\u003Cli>\u003Ccode dir=\"auto\">- KEY (Not tracked in AWS)\u003C/code> in dim — exists locally but was never pushed.\u003C/li>\n\u003C/ul>\n\u003Cp>Ends with \u003Ccode dir=\"auto\">Audit complete. N drifted variable(s) found.\u003C/code> Emits a \u003Ccode dir=\"auto\">secrets_audit\u003C/code> telemetry event.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"prerequisites\">Prerequisites\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#prerequisites\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Prerequisites”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>Run \u003Ccode dir=\"auto\">npx deploy-stack apply\u003C/code> first: the \u003Ccode dir=\"auto\"><project-name>-secrets\u003C/code> vault is created during provisioning. If it does not exist yet, each command points you back to \u003Ccode dir=\"auto\">apply\u003C/code>.\u003C/li>\n\u003Cli>Valid AWS credentials. On expired credentials, refresh with \u003Ccode dir=\"auto\">aws sso login\u003C/code> or \u003Ccode dir=\"auto\">aws configure\u003C/code>. See the \u003Ca href=\"/deploy-stack/guides/aws-credentials/\">AWS credentials guide\u003C/a>.\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"a-note-on-terraformsecret_keysjson\">A note on \u003Ccode dir=\"auto\">terraform/secret_keys.json\u003C/code>\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#a-note-on-terraformsecret_keysjson\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “A note on terraform/secret_keys.json”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>This file contains \u003Cstrong>key names only\u003C/strong> (e.g. \u003Ccode dir=\"auto\">[\"API_KEY\"]\u003C/code>), never values — it is safe to commit, and it \u003Cstrong>must\u003C/strong> be committed: Terraform reads it during deployment to map each key into your ECS task definition.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"see-also\">See also\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#see-also\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “See also”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>\u003Ca href=\"/deploy-stack/guides/secrets-management/\">Secrets management guide\u003C/a>\u003C/li>\n\u003Cli>\u003Ca href=\"/deploy-stack/cli/apply/\">apply\u003C/a>\u003C/li>\n\u003C/ul>\n",{"headings":511,"localImagePaths":526,"remoteImagePaths":527,"frontmatter":528,"imagePaths":529},[512,515,518,521,522,525],{"depth":22,"slug":513,"text":514},"secrets-push","secrets push",{"depth":22,"slug":516,"text":517},"secrets-pull","secrets pull",{"depth":22,"slug":519,"text":520},"secrets-audit","secrets audit",{"depth":22,"slug":417,"text":418},{"depth":22,"slug":523,"text":524},"a-note-on-terraformsecret_keysjson","A note on terraform/secret_keys.json",{"depth":22,"slug":323,"text":324},[],[],{"title":500,"description":501},[],"cli/status",{"id":530,"data":532,"body":538,"filePath":539,"digest":540,"rendered":541},{"title":533,"description":534,"editUrl":8,"head":535,"template":10,"sidebar":536,"pagefind":8,"draft":12},"status","Check ECS service health and CloudWatch alarms.",[],{"hidden":12,"attrs":537},{},"Instant health dashboard for your deployment. Exits cleanly when healthy; hands off to `diagnose` automatically when degraded.\n\n## What it does\n\n- Queries ECS (`desiredCount` vs `runningCount`/`pendingCount`) and project-prefixed CloudWatch alarms.\n- Prints a color-coded dashboard: service status, replicas (green/yellow/red), and alarm states.\n- If degraded (`runningCount \u003C desiredCount` or any alarm firing), prints the degraded notice, invokes `diagnose`, and exits 1.\n- Resolves region like `logs` (`--region` → `AWS_REGION` → `terraform/main.tf` → `us-east-2`); cluster, service, and log group default to `\u003Cproject-name>-cluster`, `\u003Cproject-name>-service`, `/ecs/\u003Cproject-name>` (overridable via `ECS_CLUSTER` / `ECS_SERVICE` / `ECS_LOG_GROUP`).\n- Emits a `status_run` telemetry event recording health and outcome.\n\n## Usage\n\n```bash\nnpx deploy-stack status\nnpx deploy-stack status --json\n```\n\n## Flags\n\n| Flag | Description |\n| ---- | ----------- |\n| `--json` | Output the raw status payload as JSON; disables auto-diagnose. |\n| `--region \u003Cregion>` | Explicit AWS region override. |\n\n## See also\n\n- [exec](/deploy-stack/cli/exec/)\n- [diagnose](/deploy-stack/cli/diagnose/)","src/content/docs/cli/status.md","dad9e4da8a252dd8",{"html":542,"metadata":543},"\u003Cp>Instant health dashboard for your deployment. Exits cleanly when healthy; hands off to \u003Ccode dir=\"auto\">diagnose\u003C/code> automatically when degraded.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"what-it-does\">What it does\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#what-it-does\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “What it does”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>Queries ECS (\u003Ccode dir=\"auto\">desiredCount\u003C/code> vs \u003Ccode dir=\"auto\">runningCount\u003C/code>/\u003Ccode dir=\"auto\">pendingCount\u003C/code>) and project-prefixed CloudWatch alarms.\u003C/li>\n\u003Cli>Prints a color-coded dashboard: service status, replicas (green/yellow/red), and alarm states.\u003C/li>\n\u003Cli>If degraded (\u003Ccode dir=\"auto\">runningCount < desiredCount\u003C/code> or any alarm firing), prints the degraded notice, invokes \u003Ccode dir=\"auto\">diagnose\u003C/code>, and exits 1.\u003C/li>\n\u003Cli>Resolves region like \u003Ccode dir=\"auto\">logs\u003C/code> (\u003Ccode dir=\"auto\">--region\u003C/code> → \u003Ccode dir=\"auto\">AWS_REGION\u003C/code> → \u003Ccode dir=\"auto\">terraform/main.tf\u003C/code> → \u003Ccode dir=\"auto\">us-east-2\u003C/code>); cluster, service, and log group default to \u003Ccode dir=\"auto\"><project-name>-cluster\u003C/code>, \u003Ccode dir=\"auto\"><project-name>-service\u003C/code>, \u003Ccode dir=\"auto\">/ecs/<project-name>\u003C/code> (overridable via \u003Ccode dir=\"auto\">ECS_CLUSTER\u003C/code> / \u003Ccode dir=\"auto\">ECS_SERVICE\u003C/code> / \u003Ccode dir=\"auto\">ECS_LOG_GROUP\u003C/code>).\u003C/li>\n\u003Cli>Emits a \u003Ccode dir=\"auto\">status_run\u003C/code> telemetry event recording health and outcome.\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"usage\">Usage\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#usage\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Usage”\u003C/span>\u003C/a>\u003C/div>\n\u003Cdiv class=\"expressive-code\">\u003Clink rel=\"stylesheet\" href=\"/deploy-stack/_astro/ec.w36nc.css\">\u003Cscript type=\"module\" src=\"/deploy-stack/_astro/ec.0vx5m.js\">\u003C/script>\u003Cfigure class=\"frame is-terminal not-content\">\u003Cfigcaption class=\"header\">\u003Cspan class=\"title\">\u003C/span>\u003Cspan class=\"sr-only\">Terminal window\u003C/span>\u003C/figcaption>\u003Cpre data-language=\"bash\">\u003Ccode>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">npx\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">deploy-stack\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">status\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">npx\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">deploy-stack\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">status\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">--json\u003C/span>\u003C/div>\u003C/div>\u003C/code>\u003C/pre>\u003Cdiv class=\"copy\">\u003Cdiv aria-live=\"polite\">\u003C/div>\u003Cbutton title=\"Copy to clipboard\" data-copied=\"Copied!\" data-code=\"npx deploy-stack statusnpx deploy-stack status --json\">\u003Cdiv>\u003C/div>\u003C/button>\u003C/div>\u003C/figure>\u003C/div>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"flags\">Flags\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#flags\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Flags”\u003C/span>\u003C/a>\u003C/div>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>Flag\u003C/th>\n\u003Cth>Description\u003C/th>\n\u003C/tr>\n\u003C/thead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Ccode dir=\"auto\">--json\u003C/code>\u003C/td>\n\u003Ctd>Output the raw status payload as JSON; disables auto-diagnose.\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode dir=\"auto\">--region <region>\u003C/code>\u003C/td>\n\u003Ctd>Explicit AWS region override.\u003C/td>\n\u003C/tr>\n\u003C/tbody>\n\u003C/table>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"see-also\">See also\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#see-also\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “See also”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>\u003Ca href=\"/deploy-stack/cli/exec/\">exec\u003C/a>\u003C/li>\n\u003Cli>\u003Ca href=\"/deploy-stack/cli/diagnose/\">diagnose\u003C/a>\u003C/li>\n\u003C/ul>\n",{"headings":544,"localImagePaths":549,"remoteImagePaths":550,"frontmatter":551,"imagePaths":552},[545,546,547,548],{"depth":22,"slug":292,"text":293},{"depth":22,"slug":295,"text":296},{"depth":22,"slug":298,"text":299},{"depth":22,"slug":323,"text":324},[],[],{"title":533,"description":534},[],"cli/sync-ai",{"id":553,"data":555,"body":561,"filePath":562,"digest":563,"rendered":564},{"title":556,"description":557,"editUrl":8,"head":558,"template":10,"sidebar":559,"pagefind":8,"draft":12},"sync-ai","Regenerate AI assistant rules for an existing project.",[],{"hidden":12,"attrs":560},{},"Give your AI coding assistants up-to-date deploy-stack context after project settings change, so they generate correct Terraform and deployment instructions instead of hallucinating them.\n\n## What it does\n\n- Prompts you to choose which AI assistants to configure, then writes the matching rule files with your project's region (from `region` in `terraform/main.tf`) and container port (from `containerPort` in `terraform/main.tf`).\n- Writes a full rule file for Cursor (`.cursor/rules/deploy-stack.mdc`), Roo (`.roo/rules/deploy-stack.md`), Trae (`.trae/rules/project_rules.md`), and Continue (`.prompts/deploy-stack.prompt`); injects a managed block into the existing config for Windsurf (`.windsurfrules`), Copilot (`.github/copilot-instructions.md`), Claude (`CLAUDE.md`), Goose (`.goosehints`), and Aider (`.aider.conf.yml`).\n- Exits without writing anything when no assistants are selected.\n- Emits a `sync_ai_executed` telemetry event listing the selected assistants.\n\n## Usage\n\n```bash\nnpx deploy-stack sync-ai\n```\n\n## Flags\n\nThis command accepts no CLI flags. Assistant selection is interactive.\n\n## See also\n\n- [npx deploy-stack (init)](/deploy-stack/cli/init/)","src/content/docs/cli/sync-ai.md","637a7ac9d1ce6a0e",{"html":565,"metadata":566},"\u003Cp>Give your AI coding assistants up-to-date deploy-stack context after project settings change, so they generate correct Terraform and deployment instructions instead of hallucinating them.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"what-it-does\">What it does\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#what-it-does\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “What it does”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>Prompts you to choose which AI assistants to configure, then writes the matching rule files with your project’s region (from \u003Ccode dir=\"auto\">region\u003C/code> in \u003Ccode dir=\"auto\">terraform/main.tf\u003C/code>) and container port (from \u003Ccode dir=\"auto\">containerPort\u003C/code> in \u003Ccode dir=\"auto\">terraform/main.tf\u003C/code>).\u003C/li>\n\u003Cli>Writes a full rule file for Cursor (\u003Ccode dir=\"auto\">.cursor/rules/deploy-stack.mdc\u003C/code>), Roo (\u003Ccode dir=\"auto\">.roo/rules/deploy-stack.md\u003C/code>), Trae (\u003Ccode dir=\"auto\">.trae/rules/project_rules.md\u003C/code>), and Continue (\u003Ccode dir=\"auto\">.prompts/deploy-stack.prompt\u003C/code>); injects a managed block into the existing config for Windsurf (\u003Ccode dir=\"auto\">.windsurfrules\u003C/code>), Copilot (\u003Ccode dir=\"auto\">.github/copilot-instructions.md\u003C/code>), Claude (\u003Ccode dir=\"auto\">CLAUDE.md\u003C/code>), Goose (\u003Ccode dir=\"auto\">.goosehints\u003C/code>), and Aider (\u003Ccode dir=\"auto\">.aider.conf.yml\u003C/code>).\u003C/li>\n\u003Cli>Exits without writing anything when no assistants are selected.\u003C/li>\n\u003Cli>Emits a \u003Ccode dir=\"auto\">sync_ai_executed\u003C/code> telemetry event listing the selected assistants.\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"usage\">Usage\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#usage\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Usage”\u003C/span>\u003C/a>\u003C/div>\n\u003Cdiv class=\"expressive-code\">\u003Clink rel=\"stylesheet\" href=\"/deploy-stack/_astro/ec.w36nc.css\">\u003Cscript type=\"module\" src=\"/deploy-stack/_astro/ec.0vx5m.js\">\u003C/script>\u003Cfigure class=\"frame is-terminal not-content\">\u003Cfigcaption class=\"header\">\u003Cspan class=\"title\">\u003C/span>\u003Cspan class=\"sr-only\">Terminal window\u003C/span>\u003C/figcaption>\u003Cpre data-language=\"bash\">\u003Ccode>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">npx\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">deploy-stack\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">sync-ai\u003C/span>\u003C/div>\u003C/div>\u003C/code>\u003C/pre>\u003Cdiv class=\"copy\">\u003Cdiv aria-live=\"polite\">\u003C/div>\u003Cbutton title=\"Copy to clipboard\" data-copied=\"Copied!\" data-code=\"npx deploy-stack sync-ai\">\u003Cdiv>\u003C/div>\u003C/button>\u003C/div>\u003C/figure>\u003C/div>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"flags\">Flags\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#flags\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Flags”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>This command accepts no CLI flags. Assistant selection is interactive.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"see-also\">See also\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#see-also\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “See also”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>\u003Ca href=\"/deploy-stack/cli/init/\">npx deploy-stack (init)\u003C/a>\u003C/li>\n\u003C/ul>\n",{"headings":567,"localImagePaths":572,"remoteImagePaths":573,"frontmatter":574,"imagePaths":575},[568,569,570,571],{"depth":22,"slug":292,"text":293},{"depth":22,"slug":295,"text":296},{"depth":22,"slug":298,"text":299},{"depth":22,"slug":323,"text":324},[],[],{"title":556,"description":557},[],"guides/aws-credentials",{"id":576,"data":578,"body":585,"filePath":586,"digest":587,"rendered":588},{"title":579,"description":580,"editUrl":8,"head":581,"template":10,"sidebar":582,"pagefind":8,"draft":12},"Troubleshooting AWS Credentials & Authentication","Troubleshooting AWS authentication, expired tokens, and SSO logins.",[],{"order":583,"hidden":12,"attrs":584},9,{},"`deploy-stack` interacts directly with AWS APIs (Secrets Manager, ECS, CloudWatch, S3) using the official AWS SDK v3 default credential provider chain.\n\nWhen you encounter an `UnrecognizedClientException` or `ExpiredTokenException`, your local AWS authentication state has lapsed.\n\n---\n\n## 1. Quick Refresh by Setup Type\n\n### A. AWS IAM Identity Center (AWS SSO)\nIf your organization or personal account uses IAM Identity Center / SSO:\n\n```bash\n# Log in to refresh your active session token\naws sso login\n```\n\nIf you use named profiles:\n```bash\naws sso login --profile your-profile-name\nexport AWS_PROFILE=your-profile-name\n```\n\n---\n\n### B. Standard Long-Lived Access Keys (`~/.aws/credentials`)\nIf you use long-lived `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` pairs:\n\n1. Verify credentials configured:\n ```bash\n aws sts get-caller-identity\n ```\n2. If invalid or missing:\n ```bash\n aws configure\n ```\n\n---\n\n### C. Temporary Session Tokens (`AWS_SESSION_TOKEN`)\nIf you assumed an IAM role or exported manual session tokens in your terminal:\n\nCheck if stale environment variables are overriding your global credentials:\n```bash\necho $AWS_SESSION_TOKEN\n```\nIf expired, clear them:\n```bash\nunset AWS_ACCESS_KEY_ID\nunset AWS_SECRET_ACCESS_KEY\nunset AWS_SESSION_TOKEN\n```\nThen re-authenticate via `aws configure` or `aws sso login`.\n\n---\n\n## 2. Common Error References\n\n| Error Name | Root Cause | Solution |\n| :--- | :--- | :--- |\n| `UnrecognizedClientException` | The security token is unrecognized, mistyped, or expired. | Run `aws sso login` or re-run `aws configure`. |\n| `ExpiredTokenException` | Temporary STS credentials passed their validity window (typically 1–12 hrs). | Refresh STS credentials or log into SSO again. |\n| `AccessDeniedException` | User or role lacks IAM permissions for ECS, Secrets Manager, or S3. | Ensure your IAM user has adequate deployment permissions. |\n| `ResourceNotFoundException` | Target cluster, secret, or log group does not exist in target region. | Verify `AWS_REGION` and ensure infrastructure was provisioned via `deploy-stack apply`. |","src/content/docs/guides/aws-credentials.md","5f42e951a38ac46d",{"html":589,"metadata":590},"\u003Cp>\u003Ccode dir=\"auto\">deploy-stack\u003C/code> interacts directly with AWS APIs (Secrets Manager, ECS, CloudWatch, S3) using the official AWS SDK v3 default credential provider chain.\u003C/p>\n\u003Cp>When you encounter an \u003Ccode dir=\"auto\">UnrecognizedClientException\u003C/code> or \u003Ccode dir=\"auto\">ExpiredTokenException\u003C/code>, your local AWS authentication state has lapsed.\u003C/p>\n\u003Chr>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"1-quick-refresh-by-setup-type\">1. Quick Refresh by Setup Type\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#1-quick-refresh-by-setup-type\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “1. Quick Refresh by Setup Type”\u003C/span>\u003C/a>\u003C/div>\n\u003Cdiv class=\"sl-heading-wrapper level-h3\">\u003Ch3 id=\"a-aws-iam-identity-center-aws-sso\">A. AWS IAM Identity Center (AWS SSO)\u003C/h3>\u003Ca class=\"sl-anchor-link\" href=\"#a-aws-iam-identity-center-aws-sso\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “A. AWS IAM Identity Center (AWS SSO)”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>If your organization or personal account uses IAM Identity Center / SSO:\u003C/p>\n\u003Cdiv class=\"expressive-code\">\u003Clink rel=\"stylesheet\" href=\"/deploy-stack/_astro/ec.w36nc.css\">\u003Cscript type=\"module\" src=\"/deploy-stack/_astro/ec.0vx5m.js\">\u003C/script>\u003Cfigure class=\"frame is-terminal not-content\">\u003Cfigcaption class=\"header\">\u003Cspan class=\"title\">\u003C/span>\u003Cspan class=\"sr-only\">Terminal window\u003C/span>\u003C/figcaption>\u003Cpre data-language=\"bash\">\u003Ccode>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#919F9F;--1:#5F636F\"># Log in to refresh your active session token\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">aws\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">sso\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">login\u003C/span>\u003C/div>\u003C/div>\u003C/code>\u003C/pre>\u003Cdiv class=\"copy\">\u003Cdiv aria-live=\"polite\">\u003C/div>\u003Cbutton title=\"Copy to clipboard\" data-copied=\"Copied!\" data-code=\"aws sso login\">\u003Cdiv>\u003C/div>\u003C/button>\u003C/div>\u003C/figure>\u003C/div>\n\u003Cp>If you use named profiles:\u003C/p>\n\u003Cdiv class=\"expressive-code\">\u003Cfigure class=\"frame is-terminal not-content\">\u003Cfigcaption class=\"header\">\u003Cspan class=\"title\">\u003C/span>\u003Cspan class=\"sr-only\">Terminal window\u003C/span>\u003C/figcaption>\u003Cpre data-language=\"bash\">\u003Ccode>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">aws\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">sso\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">login\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">--profile\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">your-profile-name\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#C792EA;--1:#8844AE\">export\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#C5E478;--1:#3B61B0\">AWS_PROFILE\u003C/span>\u003Cspan style=\"--0:#C792EA;--1:#8844AE\">=\u003C/span>\u003Cspan style=\"--0:#C5E478;--1:#3B61B0\">your-profile-name\u003C/span>\u003C/div>\u003C/div>\u003C/code>\u003C/pre>\u003Cdiv class=\"copy\">\u003Cdiv aria-live=\"polite\">\u003C/div>\u003Cbutton title=\"Copy to clipboard\" data-copied=\"Copied!\" data-code=\"aws sso login --profile your-profile-nameexport AWS_PROFILE=your-profile-name\">\u003Cdiv>\u003C/div>\u003C/button>\u003C/div>\u003C/figure>\u003C/div>\n\u003Chr>\n\u003Cdiv class=\"sl-heading-wrapper level-h3\">\u003Ch3 id=\"b-standard-long-lived-access-keys-awscredentials\">B. Standard Long-Lived Access Keys (\u003Ccode dir=\"auto\">~/.aws/credentials\u003C/code>)\u003C/h3>\u003Ca class=\"sl-anchor-link\" href=\"#b-standard-long-lived-access-keys-awscredentials\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “B. Standard Long-Lived Access Keys (~/.aws/credentials)”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>If you use long-lived \u003Ccode dir=\"auto\">AWS_ACCESS_KEY_ID\u003C/code> and \u003Ccode dir=\"auto\">AWS_SECRET_ACCESS_KEY\u003C/code> pairs:\u003C/p>\n\u003Col>\n\u003Cli>Verify credentials configured:\n\u003Cdiv class=\"expressive-code\">\u003Cfigure class=\"frame is-terminal not-content\">\u003Cfigcaption class=\"header\">\u003Cspan class=\"title\">\u003C/span>\u003Cspan class=\"sr-only\">Terminal window\u003C/span>\u003C/figcaption>\u003Cpre data-language=\"bash\">\u003Ccode>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">aws\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">sts\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">get-caller-identity\u003C/span>\u003C/div>\u003C/div>\u003C/code>\u003C/pre>\u003Cdiv class=\"copy\">\u003Cdiv aria-live=\"polite\">\u003C/div>\u003Cbutton title=\"Copy to clipboard\" data-copied=\"Copied!\" data-code=\"aws sts get-caller-identity\">\u003Cdiv>\u003C/div>\u003C/button>\u003C/div>\u003C/figure>\u003C/div>\n\u003C/li>\n\u003Cli>If invalid or missing:\n\u003Cdiv class=\"expressive-code\">\u003Cfigure class=\"frame is-terminal not-content\">\u003Cfigcaption class=\"header\">\u003Cspan class=\"title\">\u003C/span>\u003Cspan class=\"sr-only\">Terminal window\u003C/span>\u003C/figcaption>\u003Cpre data-language=\"bash\">\u003Ccode>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">aws\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">configure\u003C/span>\u003C/div>\u003C/div>\u003C/code>\u003C/pre>\u003Cdiv class=\"copy\">\u003Cdiv aria-live=\"polite\">\u003C/div>\u003Cbutton title=\"Copy to clipboard\" data-copied=\"Copied!\" data-code=\"aws configure\">\u003Cdiv>\u003C/div>\u003C/button>\u003C/div>\u003C/figure>\u003C/div>\n\u003C/li>\n\u003C/ol>\n\u003Chr>\n\u003Cdiv class=\"sl-heading-wrapper level-h3\">\u003Ch3 id=\"c-temporary-session-tokens-aws_session_token\">C. Temporary Session Tokens (\u003Ccode dir=\"auto\">AWS_SESSION_TOKEN\u003C/code>)\u003C/h3>\u003Ca class=\"sl-anchor-link\" href=\"#c-temporary-session-tokens-aws_session_token\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “C. Temporary Session Tokens (AWS_SESSION_TOKEN)”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>If you assumed an IAM role or exported manual session tokens in your terminal:\u003C/p>\n\u003Cp>Check if stale environment variables are overriding your global credentials:\u003C/p>\n\u003Cdiv class=\"expressive-code\">\u003Cfigure class=\"frame is-terminal not-content\">\u003Cfigcaption class=\"header\">\u003Cspan class=\"title\">\u003C/span>\u003Cspan class=\"sr-only\">Terminal window\u003C/span>\u003C/figcaption>\u003Cpre data-language=\"bash\">\u003Ccode>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#C5E478;--1:#3B61B0\">echo\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#C5E478;--1:#3B61B0\">$AWS_SESSION_TOKEN\u003C/span>\u003C/div>\u003C/div>\u003C/code>\u003C/pre>\u003Cdiv class=\"copy\">\u003Cdiv aria-live=\"polite\">\u003C/div>\u003Cbutton title=\"Copy to clipboard\" data-copied=\"Copied!\" data-code=\"echo $AWS_SESSION_TOKEN\">\u003Cdiv>\u003C/div>\u003C/button>\u003C/div>\u003C/figure>\u003C/div>\n\u003Cp>If expired, clear them:\u003C/p>\n\u003Cdiv class=\"expressive-code\">\u003Cfigure class=\"frame is-terminal not-content\">\u003Cfigcaption class=\"header\">\u003Cspan class=\"title\">\u003C/span>\u003Cspan class=\"sr-only\">Terminal window\u003C/span>\u003C/figcaption>\u003Cpre data-language=\"bash\">\u003Ccode>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#C5E478;--1:#3B61B0\">unset\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">AWS_ACCESS_KEY_ID\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#C5E478;--1:#3B61B0\">unset\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">AWS_SECRET_ACCESS_KEY\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#C5E478;--1:#3B61B0\">unset\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">AWS_SESSION_TOKEN\u003C/span>\u003C/div>\u003C/div>\u003C/code>\u003C/pre>\u003Cdiv class=\"copy\">\u003Cdiv aria-live=\"polite\">\u003C/div>\u003Cbutton title=\"Copy to clipboard\" data-copied=\"Copied!\" data-code=\"unset AWS_ACCESS_KEY_IDunset AWS_SECRET_ACCESS_KEYunset AWS_SESSION_TOKEN\">\u003Cdiv>\u003C/div>\u003C/button>\u003C/div>\u003C/figure>\u003C/div>\n\u003Cp>Then re-authenticate via \u003Ccode dir=\"auto\">aws configure\u003C/code> or \u003Ccode dir=\"auto\">aws sso login\u003C/code>.\u003C/p>\n\u003Chr>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"2-common-error-references\">2. Common Error References\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#2-common-error-references\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “2. Common Error References”\u003C/span>\u003C/a>\u003C/div>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth style=\"text-align: left\">Error Name\u003C/th>\n\u003Cth style=\"text-align: left\">Root Cause\u003C/th>\n\u003Cth style=\"text-align: left\">Solution\u003C/th>\n\u003C/tr>\n\u003C/thead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd style=\"text-align: left\">\u003Ccode dir=\"auto\">UnrecognizedClientException\u003C/code>\u003C/td>\n\u003Ctd style=\"text-align: left\">The security token is unrecognized, mistyped, or expired.\u003C/td>\n\u003Ctd style=\"text-align: left\">Run \u003Ccode dir=\"auto\">aws sso login\u003C/code> or re-run \u003Ccode dir=\"auto\">aws configure\u003C/code>.\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd style=\"text-align: left\">\u003Ccode dir=\"auto\">ExpiredTokenException\u003C/code>\u003C/td>\n\u003Ctd style=\"text-align: left\">Temporary STS credentials passed their validity window (typically 1–12 hrs).\u003C/td>\n\u003Ctd style=\"text-align: left\">Refresh STS credentials or log into SSO again.\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd style=\"text-align: left\">\u003Ccode dir=\"auto\">AccessDeniedException\u003C/code>\u003C/td>\n\u003Ctd style=\"text-align: left\">User or role lacks IAM permissions for ECS, Secrets Manager, or S3.\u003C/td>\n\u003Ctd style=\"text-align: left\">Ensure your IAM user has adequate deployment permissions.\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd style=\"text-align: left\">\u003Ccode dir=\"auto\">ResourceNotFoundException\u003C/code>\u003C/td>\n\u003Ctd style=\"text-align: left\">Target cluster, secret, or log group does not exist in target region.\u003C/td>\n\u003Ctd style=\"text-align: left\">Verify \u003Ccode dir=\"auto\">AWS_REGION\u003C/code> and ensure infrastructure was provisioned via \u003Ccode dir=\"auto\">deploy-stack apply\u003C/code>.\u003C/td>\n\u003C/tr>\n\u003C/tbody>\n\u003C/table>\n",{"headings":591,"localImagePaths":607,"remoteImagePaths":608,"frontmatter":609,"imagePaths":611},[592,595,598,601,604],{"depth":22,"slug":593,"text":594},"1-quick-refresh-by-setup-type","1. Quick Refresh by Setup Type",{"depth":35,"slug":596,"text":597},"a-aws-iam-identity-center-aws-sso","A. AWS IAM Identity Center (AWS SSO)",{"depth":35,"slug":599,"text":600},"b-standard-long-lived-access-keys-awscredentials","B. Standard Long-Lived Access Keys (~/.aws/credentials)",{"depth":35,"slug":602,"text":603},"c-temporary-session-tokens-aws_session_token","C. Temporary Session Tokens (AWS_SESSION_TOKEN)",{"depth":22,"slug":605,"text":606},"2-common-error-references","2. Common Error References",[],[],{"title":579,"description":580,"sidebar":610},{"order":583},[],"guides/cicd-pipeline",{"id":612,"data":614,"body":621,"filePath":622,"digest":623,"rendered":624},{"title":615,"description":616,"editUrl":8,"head":617,"template":10,"sidebar":618,"pagefind":8,"draft":12},"CI/CD Pipeline & First Deploy","How the generated GitHub Actions workflow builds, scans, and deploys your app with zero stored AWS keys.",[],{"order":619,"hidden":12,"attrs":620},1,{},"Every `npx deploy-stack` run generates `.github/workflows/deploy.yml`. This page explains what that pipeline does, when it runs, and why your site returns `503` until the first push completes.\n\n## When it runs\n\nThe workflow triggers on two events (`templates/github/deploy.yml`):\n\n- A `push` to your deploy branch (`{{DEPLOY_BRANCH}}`, chosen during setup).\n- A weekly Sunday cron (`0 0 * * 0`) that re-applies the Terraform configuration, so drift and base-image updates converge automatically.\n\nThe region, ECR repository, ECS cluster, and ECS service names are baked in at generation time as `\u003Cproject>-repo`, `\u003Cproject>-cluster`, and `\u003Cproject>-service`.\n\n## No stored AWS keys\n\nAuthentication uses GitHub OIDC, not long-lived credentials. The workflow declares:\n\n```yaml\npermissions:\n id-token: write\n contents: read\n```\n\nand assumes the `{{PROJECT_NAME}}-github-actions-role` IAM role created by `terraform/oidc.tf`. There is nothing to rotate and no secret to leak. (If your AWS account already has a GitHub OIDC provider, set `create_oidc_provider = false` in `terraform/oidc.tf` — see [apply](/deploy-stack/cli/apply/).)\n\n## The five stages\n\n1. **IaC security scan.** Trivy scans `terraform/` for vulnerabilities, secrets, and misconfigurations (`CRITICAL,HIGH`). It is informational only (`exit-code: '0'`), so it never blocks the build; results land in the GitHub step summary.\n2. **Infrastructure sync.** The `anton-codes-iac/deploy-stack-action@v1` step runs Terraform against `terraform/`, so infrastructure changes committed alongside code are applied before the new image rolls out.\n3. **Build & push.** The workflow logs in to Amazon ECR, runs `docker build` on your generated `Dockerfile`, and tags the result `latest`.\n4. **Container scan.** Trivy scans the built image (`os,library`, `ignore-unfixed: true`), again informational only with results in the step summary.\n5. **Deploy.** The image is pushed to ECR and the workflow forces a new ECS deployment (`aws ecs update-service --force-new-deployment`), which rolls the new image across your tasks behind the ALB.\n\n## Why you see a 503 first\n\n`npx deploy-stack apply` provisions the ALB, cluster, and service, but no container image exists until this workflow runs once. Pushing to your deploy branch (`git add . && git commit -m \"ci: infra\" && git push`) builds and deploys the first image, clearing the `503`. If the service stays unhealthy after that, run `npx deploy-stack diagnose` — usually the container failed its ALB health check (see [Dockerfiles](/deploy-stack/guides/dockerfiles/)).\n\n## Related workflows\n\n- `preview.yml` / `teardown.yml` exist only when ephemeral PR previews are enabled. See [Ephemeral PR Previews](/deploy-stack/guides/ephemeral-pr-previews/).\n- Secrets are injected at deploy time from AWS Secrets Manager, never from the repo. See [Secrets Management](/deploy-stack/guides/secrets-management/).\n\n## See also\n\n- [Quickstart](/deploy-stack/guides/quickstart/) for the 5-minute path that ends here.\n- [Supported Frameworks](/deploy-stack/guides/frameworks/) for what the pipeline builds.\n- Migrating? See [Vercel (Next.js)](/deploy-stack/migrations/nextjs-vercel-to-aws/), [Vercel (Astro)](/deploy-stack/migrations/astro-vercel-to-aws/), [Vercel (SvelteKit)](/deploy-stack/migrations/sveltekit-vercel-to-aws/), and [Heroku (Procfile)](/deploy-stack/migrations/heroku-procfile-to-aws/).","src/content/docs/guides/cicd-pipeline.md","04178f9493449f9a",{"html":625,"metadata":626},"\u003Cp>Every \u003Ccode dir=\"auto\">npx deploy-stack\u003C/code> run generates \u003Ccode dir=\"auto\">.github/workflows/deploy.yml\u003C/code>. This page explains what that pipeline does, when it runs, and why your site returns \u003Ccode dir=\"auto\">503\u003C/code> until the first push completes.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"when-it-runs\">When it runs\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#when-it-runs\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “When it runs”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>The workflow triggers on two events (\u003Ccode dir=\"auto\">templates/github/deploy.yml\u003C/code>):\u003C/p>\n\u003Cul>\n\u003Cli>A \u003Ccode dir=\"auto\">push\u003C/code> to your deploy branch (\u003Ccode dir=\"auto\">{{DEPLOY_BRANCH}}\u003C/code>, chosen during setup).\u003C/li>\n\u003Cli>A weekly Sunday cron (\u003Ccode dir=\"auto\">0 0 * * 0\u003C/code>) that re-applies the Terraform configuration, so drift and base-image updates converge automatically.\u003C/li>\n\u003C/ul>\n\u003Cp>The region, ECR repository, ECS cluster, and ECS service names are baked in at generation time as \u003Ccode dir=\"auto\"><project>-repo\u003C/code>, \u003Ccode dir=\"auto\"><project>-cluster\u003C/code>, and \u003Ccode dir=\"auto\"><project>-service\u003C/code>.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"no-stored-aws-keys\">No stored AWS keys\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#no-stored-aws-keys\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “No stored AWS keys”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>Authentication uses GitHub OIDC, not long-lived credentials. The workflow declares:\u003C/p>\n\u003Cdiv class=\"expressive-code\">\u003Clink rel=\"stylesheet\" href=\"/deploy-stack/_astro/ec.w36nc.css\">\u003Cscript type=\"module\" src=\"/deploy-stack/_astro/ec.0vx5m.js\">\u003C/script>\u003Cfigure class=\"frame not-content\">\u003Cfigcaption class=\"header\">\u003C/figcaption>\u003Cpre data-language=\"yaml\">\u003Ccode>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#7FDBCA;--1:#111111\">permissions\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\">:\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan class=\"indent\"> \u003C/span>\u003Cspan style=\"--0:#7FDBCA;--1:#111111\">id-token\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\">: \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">write\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan class=\"indent\"> \u003C/span>\u003Cspan style=\"--0:#7FDBCA;--1:#111111\">contents\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\">: \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">read\u003C/span>\u003C/div>\u003C/div>\u003C/code>\u003C/pre>\u003Cdiv class=\"copy\">\u003Cdiv aria-live=\"polite\">\u003C/div>\u003Cbutton title=\"Copy to clipboard\" data-copied=\"Copied!\" data-code=\"permissions: id-token: write contents: read\">\u003Cdiv>\u003C/div>\u003C/button>\u003C/div>\u003C/figure>\u003C/div>\n\u003Cp>and assumes the \u003Ccode dir=\"auto\">{{PROJECT_NAME}}-github-actions-role\u003C/code> IAM role created by \u003Ccode dir=\"auto\">terraform/oidc.tf\u003C/code>. There is nothing to rotate and no secret to leak. (If your AWS account already has a GitHub OIDC provider, set \u003Ccode dir=\"auto\">create_oidc_provider = false\u003C/code> in \u003Ccode dir=\"auto\">terraform/oidc.tf\u003C/code> — see \u003Ca href=\"/deploy-stack/cli/apply/\">apply\u003C/a>.)\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"the-five-stages\">The five stages\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#the-five-stages\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “The five stages”\u003C/span>\u003C/a>\u003C/div>\n\u003Col>\n\u003Cli>\u003Cstrong>IaC security scan.\u003C/strong> Trivy scans \u003Ccode dir=\"auto\">terraform/\u003C/code> for vulnerabilities, secrets, and misconfigurations (\u003Ccode dir=\"auto\">CRITICAL,HIGH\u003C/code>). It is informational only (\u003Ccode dir=\"auto\">exit-code: '0'\u003C/code>), so it never blocks the build; results land in the GitHub step summary.\u003C/li>\n\u003Cli>\u003Cstrong>Infrastructure sync.\u003C/strong> The \u003Ccode dir=\"auto\">anton-codes-iac/deploy-stack-action@v1\u003C/code> step runs Terraform against \u003Ccode dir=\"auto\">terraform/\u003C/code>, so infrastructure changes committed alongside code are applied before the new image rolls out.\u003C/li>\n\u003Cli>\u003Cstrong>Build & push.\u003C/strong> The workflow logs in to Amazon ECR, runs \u003Ccode dir=\"auto\">docker build\u003C/code> on your generated \u003Ccode dir=\"auto\">Dockerfile\u003C/code>, and tags the result \u003Ccode dir=\"auto\">latest\u003C/code>.\u003C/li>\n\u003Cli>\u003Cstrong>Container scan.\u003C/strong> Trivy scans the built image (\u003Ccode dir=\"auto\">os,library\u003C/code>, \u003Ccode dir=\"auto\">ignore-unfixed: true\u003C/code>), again informational only with results in the step summary.\u003C/li>\n\u003Cli>\u003Cstrong>Deploy.\u003C/strong> The image is pushed to ECR and the workflow forces a new ECS deployment (\u003Ccode dir=\"auto\">aws ecs update-service --force-new-deployment\u003C/code>), which rolls the new image across your tasks behind the ALB.\u003C/li>\n\u003C/ol>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"why-you-see-a-503-first\">Why you see a 503 first\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#why-you-see-a-503-first\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Why you see a 503 first”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>\u003Ccode dir=\"auto\">npx deploy-stack apply\u003C/code> provisions the ALB, cluster, and service, but no container image exists until this workflow runs once. Pushing to your deploy branch (\u003Ccode dir=\"auto\">git add . && git commit -m \"ci: infra\" && git push\u003C/code>) builds and deploys the first image, clearing the \u003Ccode dir=\"auto\">503\u003C/code>. If the service stays unhealthy after that, run \u003Ccode dir=\"auto\">npx deploy-stack diagnose\u003C/code> — usually the container failed its ALB health check (see \u003Ca href=\"/deploy-stack/guides/dockerfiles/\">Dockerfiles\u003C/a>).\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"related-workflows\">Related workflows\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#related-workflows\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Related workflows”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>\u003Ccode dir=\"auto\">preview.yml\u003C/code> / \u003Ccode dir=\"auto\">teardown.yml\u003C/code> exist only when ephemeral PR previews are enabled. See \u003Ca href=\"/deploy-stack/guides/ephemeral-pr-previews/\">Ephemeral PR Previews\u003C/a>.\u003C/li>\n\u003Cli>Secrets are injected at deploy time from AWS Secrets Manager, never from the repo. See \u003Ca href=\"/deploy-stack/guides/secrets-management/\">Secrets Management\u003C/a>.\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"see-also\">See also\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#see-also\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “See also”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>\u003Ca href=\"/deploy-stack/guides/quickstart/\">Quickstart\u003C/a> for the 5-minute path that ends here.\u003C/li>\n\u003Cli>\u003Ca href=\"/deploy-stack/guides/frameworks/\">Supported Frameworks\u003C/a> for what the pipeline builds.\u003C/li>\n\u003Cli>Migrating? See \u003Ca href=\"/deploy-stack/migrations/nextjs-vercel-to-aws/\">Vercel (Next.js)\u003C/a>, \u003Ca href=\"/deploy-stack/migrations/astro-vercel-to-aws/\">Vercel (Astro)\u003C/a>, \u003Ca href=\"/deploy-stack/migrations/sveltekit-vercel-to-aws/\">Vercel (SvelteKit)\u003C/a>, and \u003Ca href=\"/deploy-stack/migrations/heroku-procfile-to-aws/\">Heroku (Procfile)\u003C/a>.\u003C/li>\n\u003C/ul>\n",{"headings":627,"localImagePaths":644,"remoteImagePaths":645,"frontmatter":646,"imagePaths":648},[628,631,634,637,640,643],{"depth":22,"slug":629,"text":630},"when-it-runs","When it runs",{"depth":22,"slug":632,"text":633},"no-stored-aws-keys","No stored AWS keys",{"depth":22,"slug":635,"text":636},"the-five-stages","The five stages",{"depth":22,"slug":638,"text":639},"why-you-see-a-503-first","Why you see a 503 first",{"depth":22,"slug":641,"text":642},"related-workflows","Related workflows",{"depth":22,"slug":323,"text":324},[],[],{"title":615,"description":616,"sidebar":647},{"order":619},[],"guides/database-connections",{"id":649,"data":651,"body":658,"filePath":659,"digest":660,"rendered":661},{"title":652,"description":653,"editUrl":8,"head":654,"template":10,"sidebar":655,"pagefind":8,"draft":12},"Managed Database Connections","Provision a managed AWS RDS PostgreSQL database for backend frameworks.",[],{"order":656,"hidden":12,"attrs":657},6,{},"When you run `npx deploy-stack` for a backend framework (Node, Django, Rails, Go, etc.), the CLI prompts you to automatically provision a managed AWS RDS PostgreSQL database.\n\n## Zero-Trust Architecture\n\nIf you select \"Yes\", `deploy-stack` builds a true zero-trust network topology:\n1. The PostgreSQL instance is deployed into heavily restricted **Isolated Subnets**.\n2. It is given a strict Security Group that *only* allows inbound traffic from your specific ECS Fargate containers on port `5432`.\n3. The database is completely inaccessible from the public internet.\n\n## Auto-Injected Environment Variables\n\nYou do not need to configure database connection strings manually. The generated Terraform automatically creates a secure, random master password in AWS Secrets Manager and injects the following environment variables directly into your running containers:\n\n* `DB_HOST` (The internal AWS DNS endpoint)\n* `DB_PORT` (5432)\n* `DB_NAME` (Your auto-generated database name)\n* `DB_USER` (Injected securely at runtime)\n* `DB_PASSWORD` (Injected securely at runtime)\n\nTo connect your application, simply configure your ORM (Prisma, Django, TypeORM, Active Record) to read from these standard environment variables.\n\n## Running Database Migrations\n\nBecause the database is in an isolated subnet, you cannot run schema migrations directly from your local laptop. \nThe best practice is to configure your CI/CD pipeline or your Docker container's startup script to run your migrations (e.g., `npx prisma deploy` or `python manage.py migrate`) before starting the main web process.","src/content/docs/guides/database-connections.md","ad8d77a67a0695fa",{"html":662,"metadata":663},"\u003Cp>When you run \u003Ccode dir=\"auto\">npx deploy-stack\u003C/code> for a backend framework (Node, Django, Rails, Go, etc.), the CLI prompts you to automatically provision a managed AWS RDS PostgreSQL database.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"zero-trust-architecture\">Zero-Trust Architecture\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#zero-trust-architecture\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Zero-Trust Architecture”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>If you select “Yes”, \u003Ccode dir=\"auto\">deploy-stack\u003C/code> builds a true zero-trust network topology:\u003C/p>\n\u003Col>\n\u003Cli>The PostgreSQL instance is deployed into heavily restricted \u003Cstrong>Isolated Subnets\u003C/strong>.\u003C/li>\n\u003Cli>It is given a strict Security Group that \u003Cem>only\u003C/em> allows inbound traffic from your specific ECS Fargate containers on port \u003Ccode dir=\"auto\">5432\u003C/code>.\u003C/li>\n\u003Cli>The database is completely inaccessible from the public internet.\u003C/li>\n\u003C/ol>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"auto-injected-environment-variables\">Auto-Injected Environment Variables\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#auto-injected-environment-variables\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Auto-Injected Environment Variables”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>You do not need to configure database connection strings manually. The generated Terraform automatically creates a secure, random master password in AWS Secrets Manager and injects the following environment variables directly into your running containers:\u003C/p>\n\u003Cul>\n\u003Cli>\u003Ccode dir=\"auto\">DB_HOST\u003C/code> (The internal AWS DNS endpoint)\u003C/li>\n\u003Cli>\u003Ccode dir=\"auto\">DB_PORT\u003C/code> (5432)\u003C/li>\n\u003Cli>\u003Ccode dir=\"auto\">DB_NAME\u003C/code> (Your auto-generated database name)\u003C/li>\n\u003Cli>\u003Ccode dir=\"auto\">DB_USER\u003C/code> (Injected securely at runtime)\u003C/li>\n\u003Cli>\u003Ccode dir=\"auto\">DB_PASSWORD\u003C/code> (Injected securely at runtime)\u003C/li>\n\u003C/ul>\n\u003Cp>To connect your application, simply configure your ORM (Prisma, Django, TypeORM, Active Record) to read from these standard environment variables.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"running-database-migrations\">Running Database Migrations\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#running-database-migrations\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Running Database Migrations”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>Because the database is in an isolated subnet, you cannot run schema migrations directly from your local laptop.\nThe best practice is to configure your CI/CD pipeline or your Docker container’s startup script to run your migrations (e.g., \u003Ccode dir=\"auto\">npx prisma deploy\u003C/code> or \u003Ccode dir=\"auto\">python manage.py migrate\u003C/code>) before starting the main web process.\u003C/p>\n",{"headings":664,"localImagePaths":674,"remoteImagePaths":675,"frontmatter":676,"imagePaths":678},[665,668,671],{"depth":22,"slug":666,"text":667},"zero-trust-architecture","Zero-Trust Architecture",{"depth":22,"slug":669,"text":670},"auto-injected-environment-variables","Auto-Injected Environment Variables",{"depth":22,"slug":672,"text":673},"running-database-migrations","Running Database Migrations",[],[],{"title":652,"description":653,"sidebar":677},{"order":656},[],"guides/docker-compose",{"id":679,"data":681,"body":688,"filePath":689,"digest":690,"rendered":691},{"title":682,"description":683,"editUrl":8,"head":684,"template":10,"sidebar":685,"pagefind":8,"draft":12},"Docker Compose Support","How deploy-stack maps docker-compose.yml services to ECS — web-service selection, ports, env vars, and sidecars.",[],{"order":686,"hidden":12,"attrs":687},5,{},"If your repo contains a `docker-compose.yml` (or `docker-compose.yaml`), setup parses it (`src/utils/dockerCompose.js`) and translates its services into the ECS task definition. Your Compose file keeps working locally, and these are the exact mapping rules that decide what runs in AWS.\n\n## File discovery\n\nOnly the repo root is checked, trying `docker-compose.yml` first and then `docker-compose.yaml`. A file with no `services` key — or one that fails YAML parsing — is treated as absent (a warning is printed, setup continues), so a stray or malformed file never blocks generation.\n\n## Which service is \"web\"\n\nThe service used as the ECS web service is the **first service with an exposed port**; if none exposes a port, the first service listed wins. Every other service becomes an ECS **sidecar** in the same task definition. Structure your file accordingly: the publicly reachable app must be the port-exposing service.\n\n## Port mapping\n\nThe container port is taken from the **last segment of the first port entry** — `\"8080:80\"` and `\"127.0.0.1:8001:8001\"` both resolve to the right-hand value (`80` and `8001`). Only the first entry is read, and any non-numeric characters are stripped. This port overrides the configured container port during setup, and the ALB health check targets it.\n\n## Environment and command injection\n\n- **Environment** supports both Compose styles: `environment:` as a mapping is used as-is; as a list (`- KEY=value`) each entry is split on the first `=`.\n- The web service's variables are injected directly into the ECS task definition, and its `command` overrides the container start command — but only when no `Procfile` `web:` process already set one (`Procfile` wins; see [Dockerfiles](/deploy-stack/guides/dockerfiles/)).\n- Sidecars get the same treatment: their environment is injected, their `command` is preserved, a missing `image` defaults to `alpine:latest`, and each sidecar logs to the shared CloudWatch log group under an `ecs-\u003Cservice>` stream prefix.\n\n## What this means in practice\n\n- Sidecars (Redis, Memcached, background helpers) run **in the same task** as the web container and share its lifecycle — this is co-location, not separate services.\n- Compose `build:` contexts are not used in AWS; the image is built from the generated `Dockerfile` by the [CI/CD pipeline](/deploy-stack/guides/cicd-pipeline/).\n- Runtime secrets still belong in AWS Secrets Manager, not in Compose `environment:`. See [Secrets Management](/deploy-stack/guides/secrets-management/).\n\n## See also\n\n- [Supported Frameworks](/deploy-stack/guides/frameworks/) for detection and defaults.\n- [Dockerfiles & the container contract](/deploy-stack/guides/dockerfiles/) for runtime requirements.","src/content/docs/guides/docker-compose.md","25870adcd27a31ff",{"html":692,"metadata":693},"\u003Cp>If your repo contains a \u003Ccode dir=\"auto\">docker-compose.yml\u003C/code> (or \u003Ccode dir=\"auto\">docker-compose.yaml\u003C/code>), setup parses it (\u003Ccode dir=\"auto\">src/utils/dockerCompose.js\u003C/code>) and translates its services into the ECS task definition. Your Compose file keeps working locally, and these are the exact mapping rules that decide what runs in AWS.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"file-discovery\">File discovery\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#file-discovery\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “File discovery”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>Only the repo root is checked, trying \u003Ccode dir=\"auto\">docker-compose.yml\u003C/code> first and then \u003Ccode dir=\"auto\">docker-compose.yaml\u003C/code>. A file with no \u003Ccode dir=\"auto\">services\u003C/code> key — or one that fails YAML parsing — is treated as absent (a warning is printed, setup continues), so a stray or malformed file never blocks generation.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"which-service-is-web\">Which service is “web”\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#which-service-is-web\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Which service is “web””\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>The service used as the ECS web service is the \u003Cstrong>first service with an exposed port\u003C/strong>; if none exposes a port, the first service listed wins. Every other service becomes an ECS \u003Cstrong>sidecar\u003C/strong> in the same task definition. Structure your file accordingly: the publicly reachable app must be the port-exposing service.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"port-mapping\">Port mapping\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#port-mapping\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Port mapping”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>The container port is taken from the \u003Cstrong>last segment of the first port entry\u003C/strong> — \u003Ccode dir=\"auto\">\"8080:80\"\u003C/code> and \u003Ccode dir=\"auto\">\"127.0.0.1:8001:8001\"\u003C/code> both resolve to the right-hand value (\u003Ccode dir=\"auto\">80\u003C/code> and \u003Ccode dir=\"auto\">8001\u003C/code>). Only the first entry is read, and any non-numeric characters are stripped. This port overrides the configured container port during setup, and the ALB health check targets it.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"environment-and-command-injection\">Environment and command injection\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#environment-and-command-injection\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Environment and command injection”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>\u003Cstrong>Environment\u003C/strong> supports both Compose styles: \u003Ccode dir=\"auto\">environment:\u003C/code> as a mapping is used as-is; as a list (\u003Ccode dir=\"auto\">- KEY=value\u003C/code>) each entry is split on the first \u003Ccode dir=\"auto\">=\u003C/code>.\u003C/li>\n\u003Cli>The web service’s variables are injected directly into the ECS task definition, and its \u003Ccode dir=\"auto\">command\u003C/code> overrides the container start command — but only when no \u003Ccode dir=\"auto\">Procfile\u003C/code> \u003Ccode dir=\"auto\">web:\u003C/code> process already set one (\u003Ccode dir=\"auto\">Procfile\u003C/code> wins; see \u003Ca href=\"/deploy-stack/guides/dockerfiles/\">Dockerfiles\u003C/a>).\u003C/li>\n\u003Cli>Sidecars get the same treatment: their environment is injected, their \u003Ccode dir=\"auto\">command\u003C/code> is preserved, a missing \u003Ccode dir=\"auto\">image\u003C/code> defaults to \u003Ccode dir=\"auto\">alpine:latest\u003C/code>, and each sidecar logs to the shared CloudWatch log group under an \u003Ccode dir=\"auto\">ecs-<service>\u003C/code> stream prefix.\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"what-this-means-in-practice\">What this means in practice\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#what-this-means-in-practice\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “What this means in practice”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>Sidecars (Redis, Memcached, background helpers) run \u003Cstrong>in the same task\u003C/strong> as the web container and share its lifecycle — this is co-location, not separate services.\u003C/li>\n\u003Cli>Compose \u003Ccode dir=\"auto\">build:\u003C/code> contexts are not used in AWS; the image is built from the generated \u003Ccode dir=\"auto\">Dockerfile\u003C/code> by the \u003Ca href=\"/deploy-stack/guides/cicd-pipeline/\">CI/CD pipeline\u003C/a>.\u003C/li>\n\u003Cli>Runtime secrets still belong in AWS Secrets Manager, not in Compose \u003Ccode dir=\"auto\">environment:\u003C/code>. See \u003Ca href=\"/deploy-stack/guides/secrets-management/\">Secrets Management\u003C/a>.\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"see-also\">See also\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#see-also\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “See also”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>\u003Ca href=\"/deploy-stack/guides/frameworks/\">Supported Frameworks\u003C/a> for detection and defaults.\u003C/li>\n\u003Cli>\u003Ca href=\"/deploy-stack/guides/dockerfiles/\">Dockerfiles & the container contract\u003C/a> for runtime requirements.\u003C/li>\n\u003C/ul>\n",{"headings":694,"localImagePaths":711,"remoteImagePaths":712,"frontmatter":713,"imagePaths":715},[695,698,701,704,707,710],{"depth":22,"slug":696,"text":697},"file-discovery","File discovery",{"depth":22,"slug":699,"text":700},"which-service-is-web","Which service is “web”",{"depth":22,"slug":702,"text":703},"port-mapping","Port mapping",{"depth":22,"slug":705,"text":706},"environment-and-command-injection","Environment and command injection",{"depth":22,"slug":708,"text":709},"what-this-means-in-practice","What this means in practice",{"depth":22,"slug":323,"text":324},[],[],{"title":682,"description":683,"sidebar":714},{"order":686},[],"guides/dockerfiles",{"id":716,"data":718,"body":725,"filePath":726,"digest":727,"rendered":728},{"title":719,"description":720,"editUrl":8,"head":721,"template":10,"sidebar":722,"pagefind":8,"draft":12},"Dockerfiles & the Container Contract","What your app must do at runtime (port, bind address, health check) and the per-framework prerequisites.",[],{"order":723,"hidden":12,"attrs":724},4,{},"Setup generates a framework-specific Alpine multi-stage `Dockerfile` engineered for zero Critical/High CVEs. Your app only needs to honor a small runtime contract — plus a few per-framework prerequisites printed as warnings (`src/utils/warnings.js`) at the end of setup.\n\n## The container contract\n\nEvery generated image assumes three things. Violating any of them is the most common cause of failing ALB health checks after an otherwise successful `apply`:\n\n1. **Listen on `$PORT`.** The container must serve traffic on the port baked in as `{{PORT}}` (default per framework — see [Supported Frameworks](/deploy-stack/guides/frameworks/)).\n2. **Bind `0.0.0.0`, not `localhost`.** Localhost-bound apps are unreachable inside ECS networking and Docker.\n3. **Answer the health check with `200 OK`.** The ALB polls your health-check path (default `/`); anything else marks the task unhealthy and the pipeline's new deployment never stabilizes.\n\n## Per-framework prerequisites\n\n| Framework | What setup warns you about |\n| --------- | -------------------------- |\n| NestJS | Bind `0.0.0.0` in `src/main.ts`: `await app.listen(process.env.PORT ?? 3000, '0.0.0.0')` |\n| Next.js | Set `output: 'standalone'` in your Next config and create a health-check route (copy-paste code is in the generated README's \"Critical Application Prerequisites\") |\n| Node.js / Express | A `start` script in `package.json` (e.g. `\"start\": \"node index.js\"`) and `0.0.0.0` binding |\n| Python (FastAPI) | Web framework in `requirements.txt`, `0.0.0.0` binding, and a health-check route returning `200 OK` |\n| Rails | Your default `Dockerfile` is backed up to `Dockerfile.bak` and replaced with the Alpine build; if you use SQLite locally but provisioned RDS, add the `pg` gem |\n| Static sites | Output folder defaults to `/app/dist` — if your framework emits `build/` or `out/`, update the `COPY` command; ensure a `build` script exists (e.g. `vite build`) |\n\nWarnings are skipped with `--preconfigured` and for `static` projects whose build directory was auto-detected.\n\n## What command runs your app\n\nThe container's start command is resolved in this order:\n\n1. `Procfile` `web:` command, if a `Procfile` exists (a `worker:` process additionally generates `worker.tf`, i.e. a second ECS service that doubles Fargate cost).\n2. Otherwise the `command` of the `docker-compose.yml` web service, if one exists.\n3. Otherwise the default `CMD` in the generated `Dockerfile`.\n\n## Keeping images lean\n\nSetup writes a `.dockerignore` excluding `.git/`, `terraform/`, state files, and `.env`, and appends Terraform entries to an existing `.gitignore`. Never commit `.env` — runtime secrets come from AWS Secrets Manager (see [Secrets Management](/deploy-stack/guides/secrets-management/)).\n\n## See also\n\n- [CI/CD Pipeline & First Deploy](/deploy-stack/guides/cicd-pipeline/) for how the image is built and rolled out.\n- [diagnose](/deploy-stack/cli/diagnose/) for reading ECS failure output when the contract is broken.","src/content/docs/guides/dockerfiles.md","d330e065371b1c95",{"html":729,"metadata":730},"\u003Cp>Setup generates a framework-specific Alpine multi-stage \u003Ccode dir=\"auto\">Dockerfile\u003C/code> engineered for zero Critical/High CVEs. Your app only needs to honor a small runtime contract — plus a few per-framework prerequisites printed as warnings (\u003Ccode dir=\"auto\">src/utils/warnings.js\u003C/code>) at the end of setup.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"the-container-contract\">The container contract\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#the-container-contract\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “The container contract”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>Every generated image assumes three things. Violating any of them is the most common cause of failing ALB health checks after an otherwise successful \u003Ccode dir=\"auto\">apply\u003C/code>:\u003C/p>\n\u003Col>\n\u003Cli>\u003Cstrong>Listen on \u003Ccode dir=\"auto\">$PORT\u003C/code>.\u003C/strong> The container must serve traffic on the port baked in as \u003Ccode dir=\"auto\">{{PORT}}\u003C/code> (default per framework — see \u003Ca href=\"/deploy-stack/guides/frameworks/\">Supported Frameworks\u003C/a>).\u003C/li>\n\u003Cli>\u003Cstrong>Bind \u003Ccode dir=\"auto\">0.0.0.0\u003C/code>, not \u003Ccode dir=\"auto\">localhost\u003C/code>.\u003C/strong> Localhost-bound apps are unreachable inside ECS networking and Docker.\u003C/li>\n\u003Cli>\u003Cstrong>Answer the health check with \u003Ccode dir=\"auto\">200 OK\u003C/code>.\u003C/strong> The ALB polls your health-check path (default \u003Ccode dir=\"auto\">/\u003C/code>); anything else marks the task unhealthy and the pipeline’s new deployment never stabilizes.\u003C/li>\n\u003C/ol>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"per-framework-prerequisites\">Per-framework prerequisites\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#per-framework-prerequisites\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Per-framework prerequisites”\u003C/span>\u003C/a>\u003C/div>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>Framework\u003C/th>\n\u003Cth>What setup warns you about\u003C/th>\n\u003C/tr>\n\u003C/thead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>NestJS\u003C/td>\n\u003Ctd>Bind \u003Ccode dir=\"auto\">0.0.0.0\u003C/code> in \u003Ccode dir=\"auto\">src/main.ts\u003C/code>: \u003Ccode dir=\"auto\">await app.listen(process.env.PORT ?? 3000, '0.0.0.0')\u003C/code>\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>Next.js\u003C/td>\n\u003Ctd>Set \u003Ccode dir=\"auto\">output: 'standalone'\u003C/code> in your Next config and create a health-check route (copy-paste code is in the generated README’s “Critical Application Prerequisites”)\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>Node.js / Express\u003C/td>\n\u003Ctd>A \u003Ccode dir=\"auto\">start\u003C/code> script in \u003Ccode dir=\"auto\">package.json\u003C/code> (e.g. \u003Ccode dir=\"auto\">\"start\": \"node index.js\"\u003C/code>) and \u003Ccode dir=\"auto\">0.0.0.0\u003C/code> binding\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>Python (FastAPI)\u003C/td>\n\u003Ctd>Web framework in \u003Ccode dir=\"auto\">requirements.txt\u003C/code>, \u003Ccode dir=\"auto\">0.0.0.0\u003C/code> binding, and a health-check route returning \u003Ccode dir=\"auto\">200 OK\u003C/code>\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>Rails\u003C/td>\n\u003Ctd>Your default \u003Ccode dir=\"auto\">Dockerfile\u003C/code> is backed up to \u003Ccode dir=\"auto\">Dockerfile.bak\u003C/code> and replaced with the Alpine build; if you use SQLite locally but provisioned RDS, add the \u003Ccode dir=\"auto\">pg\u003C/code> gem\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>Static sites\u003C/td>\n\u003Ctd>Output folder defaults to \u003Ccode dir=\"auto\">/app/dist\u003C/code> — if your framework emits \u003Ccode dir=\"auto\">build/\u003C/code> or \u003Ccode dir=\"auto\">out/\u003C/code>, update the \u003Ccode dir=\"auto\">COPY\u003C/code> command; ensure a \u003Ccode dir=\"auto\">build\u003C/code> script exists (e.g. \u003Ccode dir=\"auto\">vite build\u003C/code>)\u003C/td>\n\u003C/tr>\n\u003C/tbody>\n\u003C/table>\n\u003Cp>Warnings are skipped with \u003Ccode dir=\"auto\">--preconfigured\u003C/code> and for \u003Ccode dir=\"auto\">static\u003C/code> projects whose build directory was auto-detected.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"what-command-runs-your-app\">What command runs your app\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#what-command-runs-your-app\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “What command runs your app”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>The container’s start command is resolved in this order:\u003C/p>\n\u003Col>\n\u003Cli>\u003Ccode dir=\"auto\">Procfile\u003C/code> \u003Ccode dir=\"auto\">web:\u003C/code> command, if a \u003Ccode dir=\"auto\">Procfile\u003C/code> exists (a \u003Ccode dir=\"auto\">worker:\u003C/code> process additionally generates \u003Ccode dir=\"auto\">worker.tf\u003C/code>, i.e. a second ECS service that doubles Fargate cost).\u003C/li>\n\u003Cli>Otherwise the \u003Ccode dir=\"auto\">command\u003C/code> of the \u003Ccode dir=\"auto\">docker-compose.yml\u003C/code> web service, if one exists.\u003C/li>\n\u003Cli>Otherwise the default \u003Ccode dir=\"auto\">CMD\u003C/code> in the generated \u003Ccode dir=\"auto\">Dockerfile\u003C/code>.\u003C/li>\n\u003C/ol>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"keeping-images-lean\">Keeping images lean\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#keeping-images-lean\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Keeping images lean”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>Setup writes a \u003Ccode dir=\"auto\">.dockerignore\u003C/code> excluding \u003Ccode dir=\"auto\">.git/\u003C/code>, \u003Ccode dir=\"auto\">terraform/\u003C/code>, state files, and \u003Ccode dir=\"auto\">.env\u003C/code>, and appends Terraform entries to an existing \u003Ccode dir=\"auto\">.gitignore\u003C/code>. Never commit \u003Ccode dir=\"auto\">.env\u003C/code> — runtime secrets come from AWS Secrets Manager (see \u003Ca href=\"/deploy-stack/guides/secrets-management/\">Secrets Management\u003C/a>).\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"see-also\">See also\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#see-also\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “See also”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>\u003Ca href=\"/deploy-stack/guides/cicd-pipeline/\">CI/CD Pipeline & First Deploy\u003C/a> for how the image is built and rolled out.\u003C/li>\n\u003Cli>\u003Ca href=\"/deploy-stack/cli/diagnose/\">diagnose\u003C/a> for reading ECS failure output when the contract is broken.\u003C/li>\n\u003C/ul>\n",{"headings":731,"localImagePaths":745,"remoteImagePaths":746,"frontmatter":747,"imagePaths":749},[732,735,738,741,744],{"depth":22,"slug":733,"text":734},"the-container-contract","The container contract",{"depth":22,"slug":736,"text":737},"per-framework-prerequisites","Per-framework prerequisites",{"depth":22,"slug":739,"text":740},"what-command-runs-your-app","What command runs your app",{"depth":22,"slug":742,"text":743},"keeping-images-lean","Keeping images lean",{"depth":22,"slug":323,"text":324},[],[],{"title":719,"description":720,"sidebar":748},{"order":723},[],"guides/ephemeral-pr-previews",{"id":750,"data":752,"body":759,"filePath":760,"digest":761,"rendered":762},{"title":753,"description":754,"editUrl":8,"head":755,"template":10,"sidebar":756,"pagefind":8,"draft":12},"Ephemeral PR Previews","Spin up isolated temporary AWS environments for every pull request.",[],{"order":757,"hidden":12,"attrs":758},8,{},"When enabled, `deploy-stack` automatically configures your GitHub Actions pipeline to spin up isolated, temporary AWS environments every time a developer opens a Pull Request.\n\nA bot will comment on the PR with a live URL (e.g., `http://pr-123-your-app...`), allowing your team to test features, UI changes, and API updates before merging to `main`. When the PR is closed or merged, the environment is automatically destroyed.\n\n### 🏗️ How it Works\n\nUnder the hood, `deploy-stack` utilizes **Terraform Workspaces**. \n\nWhen a PR is opened, Terraform creates a new workspace (e.g., `pr-12`). It provisions a completely isolated Application Load Balancer and ECS Fargate Task using the exact same infrastructure definitions as your production environment, ensuring 100% parity.\n\nTo save time and simplify architecture, PR environments **share** your production AWS Secrets Manager vault and ECR Image Repository.\n\n### ⚖️ The Rule of Thumb: Should I enable this?\n\n**✅ Enable PR Previews if:**\n* You are working on a team of 2+ developers and require visual QA or UX sign-off before merging code.\n* You are building a frontend application or full-stack monolith where seeing the live UI is critical.\n\n**❌ Do NOT enable PR Previews if:**\n* You are a solo developer (you can just test locally).\n* You have a massive volume of PRs (e.g., automated Dependabot updates). Spinning up an AWS Load Balancer takes ~3 minutes, which will slow down rapid automated merges.\n\n### 💰 AWS Cost Implications\n\nBecause PR previews provision a real Application Load Balancer (ALB) and ECS Fargate compute tasks, **they are not free.**\n\n* **Compute:** You are charged standard AWS Fargate rates per minute while the PR environment is running.\n* **Load Balancing:** AWS charges ~$16/month per active Load Balancer. If a PR is open for 2 days, you pay the prorated ALB cost for those 48 hours (~$1.00).\n\nTo keep costs low, ensure your team closes or merges Pull Requests promptly so the `teardown.yml` workflow can destroy the resources and stop the billing clock!","src/content/docs/guides/ephemeral-pr-previews.md","d7eb8b9c19476171",{"html":763,"metadata":764},"\u003Cp>When enabled, \u003Ccode dir=\"auto\">deploy-stack\u003C/code> automatically configures your GitHub Actions pipeline to spin up isolated, temporary AWS environments every time a developer opens a Pull Request.\u003C/p>\n\u003Cp>A bot will comment on the PR with a live URL (e.g., \u003Ccode dir=\"auto\">http://pr-123-your-app...\u003C/code>), allowing your team to test features, UI changes, and API updates before merging to \u003Ccode dir=\"auto\">main\u003C/code>. When the PR is closed or merged, the environment is automatically destroyed.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h3\">\u003Ch3 id=\"️-how-it-works\">🏗️ How it Works\u003C/h3>\u003Ca class=\"sl-anchor-link\" href=\"#️-how-it-works\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “🏗️ How it Works”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>Under the hood, \u003Ccode dir=\"auto\">deploy-stack\u003C/code> utilizes \u003Cstrong>Terraform Workspaces\u003C/strong>.\u003C/p>\n\u003Cp>When a PR is opened, Terraform creates a new workspace (e.g., \u003Ccode dir=\"auto\">pr-12\u003C/code>). It provisions a completely isolated Application Load Balancer and ECS Fargate Task using the exact same infrastructure definitions as your production environment, ensuring 100% parity.\u003C/p>\n\u003Cp>To save time and simplify architecture, PR environments \u003Cstrong>share\u003C/strong> your production AWS Secrets Manager vault and ECR Image Repository.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h3\">\u003Ch3 id=\"️-the-rule-of-thumb-should-i-enable-this\">⚖️ The Rule of Thumb: Should I enable this?\u003C/h3>\u003Ca class=\"sl-anchor-link\" href=\"#️-the-rule-of-thumb-should-i-enable-this\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “⚖️ The Rule of Thumb: Should I enable this?”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>\u003Cstrong>✅ Enable PR Previews if:\u003C/strong>\u003C/p>\n\u003Cul>\n\u003Cli>You are working on a team of 2+ developers and require visual QA or UX sign-off before merging code.\u003C/li>\n\u003Cli>You are building a frontend application or full-stack monolith where seeing the live UI is critical.\u003C/li>\n\u003C/ul>\n\u003Cp>\u003Cstrong>❌ Do NOT enable PR Previews if:\u003C/strong>\u003C/p>\n\u003Cul>\n\u003Cli>You are a solo developer (you can just test locally).\u003C/li>\n\u003Cli>You have a massive volume of PRs (e.g., automated Dependabot updates). Spinning up an AWS Load Balancer takes ~3 minutes, which will slow down rapid automated merges.\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h3\">\u003Ch3 id=\"-aws-cost-implications\">💰 AWS Cost Implications\u003C/h3>\u003Ca class=\"sl-anchor-link\" href=\"#-aws-cost-implications\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “💰 AWS Cost Implications”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>Because PR previews provision a real Application Load Balancer (ALB) and ECS Fargate compute tasks, \u003Cstrong>they are not free.\u003C/strong>\u003C/p>\n\u003Cul>\n\u003Cli>\u003Cstrong>Compute:\u003C/strong> You are charged standard AWS Fargate rates per minute while the PR environment is running.\u003C/li>\n\u003Cli>\u003Cstrong>Load Balancing:\u003C/strong> AWS charges \u003Cdel>$16/month per active Load Balancer. If a PR is open for 2 days, you pay the prorated ALB cost for those 48 hours (\u003C/del>$1.00).\u003C/li>\n\u003C/ul>\n\u003Cp>To keep costs low, ensure your team closes or merges Pull Requests promptly so the \u003Ccode dir=\"auto\">teardown.yml\u003C/code> workflow can destroy the resources and stop the billing clock!\u003C/p>\n",{"headings":765,"localImagePaths":775,"remoteImagePaths":776,"frontmatter":777,"imagePaths":779},[766,769,772],{"depth":35,"slug":767,"text":768},"️-how-it-works","🏗️ How it Works",{"depth":35,"slug":770,"text":771},"️-the-rule-of-thumb-should-i-enable-this","⚖️ The Rule of Thumb: Should I enable this?",{"depth":35,"slug":773,"text":774},"-aws-cost-implications","💰 AWS Cost Implications",[],[],{"title":753,"description":754,"sidebar":778},{"order":757},[],"guides/examples",{"id":780,"data":782,"body":788,"filePath":789,"digest":790,"rendered":791},{"title":783,"description":784,"editUrl":8,"head":785,"template":10,"sidebar":786,"pagefind":8,"draft":12},"Reference Implementations & Examples","Example repositories demonstrating how deploy-stack handles frameworks and architectural patterns, plus ecosystem plugins and starters.",[],{"order":35,"hidden":12,"attrs":787},{},"These repositories demonstrate how `deploy-stack` handles various frameworks and architectural patterns. Each example includes the auto-generated Terraform, GitHub Actions, and container configurations.\n\n## Featured migrations\n\n* **[Heroku to AWS Migration (Django)](https://github.com/anton-codes-iac/deploy-stack-heroku-django-example):** A classic Heroku-style monolith migrated via the Procfile Importer, demonstrating a multi-container Web and Celery Worker architecture deployed from a single codebase.\n* **[Vercel to AWS Migration (Next.js)](https://github.com/anton-codes-iac/deploy-stack-vercel-nextjs-example):** Demonstrates automatic translation of Vercel edge routing (`vercel.json`) to native AWS Application Load Balancer rules.\n* **[Docker Compose to AWS Migration](https://github.com/anton-codes-iac/deploy-stack-docker-compose-example):** Demonstrates automatic translation of local `docker-compose.yml` sidecars (like Redis) into a multi-container AWS ECS Task Definition communicating over `localhost`.\n\n## DevSecOps & security architectures\n\n* **[Zero-Secret AWS Secrets Manager Injection](https://github.com/anton-codes-iac/deploy-stack-secrets-example):** A production-grade Node.js architecture demonstrating zero-plaintext secret injection. It pushes local `.env` variables directly to AWS and maps them into ECS memory at runtime, exposing a live endpoint querying GitHub's API.\n\n## Frontend & fullstack frameworks\n\n* **[Next.js Fullstack App](https://github.com/anton-codes-iac/deploy-stack-nextjs-example):** A complete Next.js deployment showcasing the generated Terraform, CloudFront setup, and automated OIDC workflow.\n* **[Vite / React SPA](https://github.com/anton-codes-iac/deploy-stack-vite-example):** Demonstrates SPA routing and `dist/` auto-detection.\n* **[Create React App](https://github.com/anton-codes-iac/deploy-stack-cra-example):** Validates backward compatibility with legacy Webpack pipelines and `build/` auto-detection.\n* **[Astro Static Site](https://github.com/anton-codes-iac/deploy-stack-astro-example):** Demonstrates modern static site generation (SSG).\n* **[SvelteKit Application](https://github.com/anton-codes-iac/deploy-stack-svelte-example):** Demonstrates static adapter integration and custom output folder detection.\n* **[Nuxt 3 (SSR)](https://github.com/anton-codes-iac/deploy-stack-nuxt-example):** Demonstrates a fully server-side rendered Nuxt application using Nitro's optimized Node output.\n\n## Backend APIs & monoliths\n\n* **[Express.js API](https://github.com/anton-codes-iac/deploy-stack-express-example):** A standard Node.js backend setup.\n* **[NestJS API](https://github.com/anton-codes-iac/deploy-stack-nest-example):** A robust NestJS architecture utilizing AST code-patching and highly optimized multi-stage TypeScript builds.\n* **[Python FastAPI](https://github.com/anton-codes-iac/deploy-stack-fastapi-example):** A Python API demonstrating unprivileged port mapping.\n* **[Ruby on Rails](https://github.com/anton-codes-iac/deploy-stack-rails-example):** A production Rails 7+ setup featuring an auto-provisioned PostgreSQL database and secure `RAILS_MASTER_KEY` string-literal injection into the initial Secrets Manager placeholder.\n* **[Django / Python](https://github.com/anton-codes-iac/deploy-stack-django-example):** A secure Gunicorn/WSGI implementation with PostgreSQL and unprivileged container adapters.\n* **[Go / Fiber](https://github.com/anton-codes-iac/deploy-stack-go-example):** A distroless, compiled Go binary deployment demonstrating ultra-low memory footprints and instant boot times.\n\n---\n\n## Ecosystem plugins & starters\n\nIn addition to standalone reference repositories, `deploy-stack` provides native integrations that hook directly into framework build pipelines and community template engines:\n\n* **[astro-deploy-stack](https://www.npmjs.com/package/astro-deploy-stack):** Push-button deployment plugin for Astro sites.\n* **[nuxt-deploy-stack](https://www.npmjs.com/package/nuxt-deploy-stack):** Nitro-optimized deployment integration for Nuxt 3 applications.\n* **[vite-plugin-deploy-stack](https://www.npmjs.com/package/vite-plugin-deploy-stack):** Zero-config Vite build plugin for single-page applications.\n* **[svelte-adapter-deploy-stack](https://www.npmjs.com/package/svelte-adapter-deploy-stack):** Native SvelteKit adapter producing optimized Fargate container builds.\n* **[nest-deploy-stack](https://www.npmjs.com/package/nest-deploy-stack):** Native Angular DevKit schematic for NestJS, installable via `nest add`.\n* **[cookiecutter-django-deploy-stack](https://github.com/anton-codes-iac/cookiecutter-django-deploy-stack):** Community Django starter listed on Django Packages.\n* **[cookiecutter-fastapi-deploy-stack](https://github.com/anton-codes-iac/cookiecutter-fastapi-deploy-stack):** Instant scaffolding for modern, async FastAPI deployments.","src/content/docs/guides/examples.md","1f51ea1a847f7f49",{"html":792,"metadata":793},"\u003Cp>These repositories demonstrate how \u003Ccode dir=\"auto\">deploy-stack\u003C/code> handles various frameworks and architectural patterns. Each example includes the auto-generated Terraform, GitHub Actions, and container configurations.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"featured-migrations\">Featured migrations\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#featured-migrations\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Featured migrations”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>\u003Cstrong>\u003Ca href=\"https://github.com/anton-codes-iac/deploy-stack-heroku-django-example\">Heroku to AWS Migration (Django)\u003C/a>:\u003C/strong> A classic Heroku-style monolith migrated via the Procfile Importer, demonstrating a multi-container Web and Celery Worker architecture deployed from a single codebase.\u003C/li>\n\u003Cli>\u003Cstrong>\u003Ca href=\"https://github.com/anton-codes-iac/deploy-stack-vercel-nextjs-example\">Vercel to AWS Migration (Next.js)\u003C/a>:\u003C/strong> Demonstrates automatic translation of Vercel edge routing (\u003Ccode dir=\"auto\">vercel.json\u003C/code>) to native AWS Application Load Balancer rules.\u003C/li>\n\u003Cli>\u003Cstrong>\u003Ca href=\"https://github.com/anton-codes-iac/deploy-stack-docker-compose-example\">Docker Compose to AWS Migration\u003C/a>:\u003C/strong> Demonstrates automatic translation of local \u003Ccode dir=\"auto\">docker-compose.yml\u003C/code> sidecars (like Redis) into a multi-container AWS ECS Task Definition communicating over \u003Ccode dir=\"auto\">localhost\u003C/code>.\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"devsecops--security-architectures\">DevSecOps & security architectures\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#devsecops--security-architectures\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “DevSecOps & security architectures”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>\u003Cstrong>\u003Ca href=\"https://github.com/anton-codes-iac/deploy-stack-secrets-example\">Zero-Secret AWS Secrets Manager Injection\u003C/a>:\u003C/strong> A production-grade Node.js architecture demonstrating zero-plaintext secret injection. It pushes local \u003Ccode dir=\"auto\">.env\u003C/code> variables directly to AWS and maps them into ECS memory at runtime, exposing a live endpoint querying GitHub’s API.\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"frontend--fullstack-frameworks\">Frontend & fullstack frameworks\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#frontend--fullstack-frameworks\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Frontend & fullstack frameworks”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>\u003Cstrong>\u003Ca href=\"https://github.com/anton-codes-iac/deploy-stack-nextjs-example\">Next.js Fullstack App\u003C/a>:\u003C/strong> A complete Next.js deployment showcasing the generated Terraform, CloudFront setup, and automated OIDC workflow.\u003C/li>\n\u003Cli>\u003Cstrong>\u003Ca href=\"https://github.com/anton-codes-iac/deploy-stack-vite-example\">Vite / React SPA\u003C/a>:\u003C/strong> Demonstrates SPA routing and \u003Ccode dir=\"auto\">dist/\u003C/code> auto-detection.\u003C/li>\n\u003Cli>\u003Cstrong>\u003Ca href=\"https://github.com/anton-codes-iac/deploy-stack-cra-example\">Create React App\u003C/a>:\u003C/strong> Validates backward compatibility with legacy Webpack pipelines and \u003Ccode dir=\"auto\">build/\u003C/code> auto-detection.\u003C/li>\n\u003Cli>\u003Cstrong>\u003Ca href=\"https://github.com/anton-codes-iac/deploy-stack-astro-example\">Astro Static Site\u003C/a>:\u003C/strong> Demonstrates modern static site generation (SSG).\u003C/li>\n\u003Cli>\u003Cstrong>\u003Ca href=\"https://github.com/anton-codes-iac/deploy-stack-svelte-example\">SvelteKit Application\u003C/a>:\u003C/strong> Demonstrates static adapter integration and custom output folder detection.\u003C/li>\n\u003Cli>\u003Cstrong>\u003Ca href=\"https://github.com/anton-codes-iac/deploy-stack-nuxt-example\">Nuxt 3 (SSR)\u003C/a>:\u003C/strong> Demonstrates a fully server-side rendered Nuxt application using Nitro’s optimized Node output.\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"backend-apis--monoliths\">Backend APIs & monoliths\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#backend-apis--monoliths\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Backend APIs & monoliths”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>\u003Cstrong>\u003Ca href=\"https://github.com/anton-codes-iac/deploy-stack-express-example\">Express.js API\u003C/a>:\u003C/strong> A standard Node.js backend setup.\u003C/li>\n\u003Cli>\u003Cstrong>\u003Ca href=\"https://github.com/anton-codes-iac/deploy-stack-nest-example\">NestJS API\u003C/a>:\u003C/strong> A robust NestJS architecture utilizing AST code-patching and highly optimized multi-stage TypeScript builds.\u003C/li>\n\u003Cli>\u003Cstrong>\u003Ca href=\"https://github.com/anton-codes-iac/deploy-stack-fastapi-example\">Python FastAPI\u003C/a>:\u003C/strong> A Python API demonstrating unprivileged port mapping.\u003C/li>\n\u003Cli>\u003Cstrong>\u003Ca href=\"https://github.com/anton-codes-iac/deploy-stack-rails-example\">Ruby on Rails\u003C/a>:\u003C/strong> A production Rails 7+ setup featuring an auto-provisioned PostgreSQL database and secure \u003Ccode dir=\"auto\">RAILS_MASTER_KEY\u003C/code> string-literal injection into the initial Secrets Manager placeholder.\u003C/li>\n\u003Cli>\u003Cstrong>\u003Ca href=\"https://github.com/anton-codes-iac/deploy-stack-django-example\">Django / Python\u003C/a>:\u003C/strong> A secure Gunicorn/WSGI implementation with PostgreSQL and unprivileged container adapters.\u003C/li>\n\u003Cli>\u003Cstrong>\u003Ca href=\"https://github.com/anton-codes-iac/deploy-stack-go-example\">Go / Fiber\u003C/a>:\u003C/strong> A distroless, compiled Go binary deployment demonstrating ultra-low memory footprints and instant boot times.\u003C/li>\n\u003C/ul>\n\u003Chr>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"ecosystem-plugins--starters\">Ecosystem plugins & starters\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#ecosystem-plugins--starters\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Ecosystem plugins & starters”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>In addition to standalone reference repositories, \u003Ccode dir=\"auto\">deploy-stack\u003C/code> provides native integrations that hook directly into framework build pipelines and community template engines:\u003C/p>\n\u003Cul>\n\u003Cli>\u003Cstrong>\u003Ca href=\"https://www.npmjs.com/package/astro-deploy-stack\">astro-deploy-stack\u003C/a>:\u003C/strong> Push-button deployment plugin for Astro sites.\u003C/li>\n\u003Cli>\u003Cstrong>\u003Ca href=\"https://www.npmjs.com/package/nuxt-deploy-stack\">nuxt-deploy-stack\u003C/a>:\u003C/strong> Nitro-optimized deployment integration for Nuxt 3 applications.\u003C/li>\n\u003Cli>\u003Cstrong>\u003Ca href=\"https://www.npmjs.com/package/vite-plugin-deploy-stack\">vite-plugin-deploy-stack\u003C/a>:\u003C/strong> Zero-config Vite build plugin for single-page applications.\u003C/li>\n\u003Cli>\u003Cstrong>\u003Ca href=\"https://www.npmjs.com/package/svelte-adapter-deploy-stack\">svelte-adapter-deploy-stack\u003C/a>:\u003C/strong> Native SvelteKit adapter producing optimized Fargate container builds.\u003C/li>\n\u003Cli>\u003Cstrong>\u003Ca href=\"https://www.npmjs.com/package/nest-deploy-stack\">nest-deploy-stack\u003C/a>:\u003C/strong> Native Angular DevKit schematic for NestJS, installable via \u003Ccode dir=\"auto\">nest add\u003C/code>.\u003C/li>\n\u003Cli>\u003Cstrong>\u003Ca href=\"https://github.com/anton-codes-iac/cookiecutter-django-deploy-stack\">cookiecutter-django-deploy-stack\u003C/a>:\u003C/strong> Community Django starter listed on Django Packages.\u003C/li>\n\u003Cli>\u003Cstrong>\u003Ca href=\"https://github.com/anton-codes-iac/cookiecutter-fastapi-deploy-stack\">cookiecutter-fastapi-deploy-stack\u003C/a>:\u003C/strong> Instant scaffolding for modern, async FastAPI deployments.\u003C/li>\n\u003C/ul>\n",{"headings":794,"localImagePaths":810,"remoteImagePaths":811,"frontmatter":812,"imagePaths":814},[795,798,801,804,807],{"depth":22,"slug":796,"text":797},"featured-migrations","Featured migrations",{"depth":22,"slug":799,"text":800},"devsecops--security-architectures","DevSecOps & security architectures",{"depth":22,"slug":802,"text":803},"frontend--fullstack-frameworks","Frontend & fullstack frameworks",{"depth":22,"slug":805,"text":806},"backend-apis--monoliths","Backend APIs & monoliths",{"depth":22,"slug":808,"text":809},"ecosystem-plugins--starters","Ecosystem plugins & starters",[],[],{"title":783,"description":784,"sidebar":813},{"order":35},[],"guides/frameworks",{"id":815,"data":817,"body":823,"filePath":824,"digest":825,"rendered":826},{"title":818,"description":819,"editUrl":8,"head":820,"template":10,"sidebar":821,"pagefind":8,"draft":12},"Supported Frameworks & Detection","Which frameworks deploy-stack detects, the signals it looks for, the valid --framework ids, and per-framework requirements.",[],{"order":22,"hidden":12,"attrs":822},{},"`deploy-stack` is designed to be as \"zero-config\" as possible. During setup, it inspects your repo (`src/utils/detector.js`) and preselects a framework preset. However, because different frameworks have unique internal architectures (especially around network binding and build outputs), a few frameworks require minor application-level tweaks to run securely in a Dockerized AWS Fargate environment.\n\n## The 3-tier support philosophy\n\nWe handle framework requirements using a 3-tier strategy so you are never left guessing why a deployment failed:\n\n1. **Zero-touch plugins (Tier 1):** If you use one of our ecosystem plugins (e.g., `nest add nest-deploy-stack` or `cookiecutter-django-deploy-stack`), your code is automatically patched and configured. Zero manual intervention required.\n2. **Intelligent CLI pre-flight (Tier 2):** If you run the standalone `deploy-stack` CLI against a raw repository, the CLI statically analyzes your code. If it detects a missing production requirement (like a localhost binding), it will flag it inline in your terminal with the exact copy-paste fix.\n3. **In-repo docs (Tier 3):** The generated `DEPLOYMENT.md` file always contains a framework-specific checklist before you push to CI/CD.\n\n## Detection precedence\n\nChecks run top-down; the first match wins.\n\n| # | Signal | Preset (`id` / name) |\n| - | ------ | -------------------- |\n| 1 | `package.json` depends on `@nestjs/core` | `nestjs` / NestJS |\n| 2 | `package.json` depends on `next` | `nextjs` / Next.js |\n| 3 | `package.json` depends on `nuxt` | `nuxt` / Nuxt 3 (SSR) |\n| 4 | `package.json` depends on `express` | `node` / Node.js / Express |\n| 5 | `package.json` depends on `@sveltejs/kit` | `svelte` / SvelteKit SSR |\n| 6 | `package.json` depends on `react-scripts`, `gatsby`, `astro`, `vite`, `@vue/cli-service`, or `@angular/cli` | `static` / (that generator) |\n| 7 | `requirements.txt` contains `fastapi` | `python` / Python FastAPI |\n| 8 | `requirements.txt` contains `django`, or `manage.py` exists | `django` / Django |\n| 9 | `Gemfile` contains a `rails` gem | `rails` / Ruby on Rails |\n| 10 | `go.mod` exists | `go` / Go |\n| 11 | No match | No preset — you pick from the interactive list |\n\nNotes from the actual code:\n\n- Both `dependencies` and `devDependencies` are searched, so a framework listed only under dev dependencies still matches.\n- A malformed `package.json` or `vercel.json` is silently ignored (no match), never fatal.\n- An empty `vercel.json` (no `redirects`, `headers`, or `rewrites`) is treated as absent.\n\n## Valid `--framework` ids\n\nThe interactive picker and the headless `--framework` flag accept: `node`, `nestjs`, `nextjs`, `nuxt`, `svelte`, `python`, `django`, `rails`, `go`, `static`. In headless mode with no `--framework`, detection applies and anything unmatched falls back to `static`.\n\n## Per-framework defaults\n\n- **Static build directory** (`buildDir`): SvelteKit `build`, Gatsby `public`, everything else (`astro`, `vite`, Vue, Angular) `dist`. This selects the folder the generated `Dockerfile` serves.\n- **Default container port**: `8080` for `static` and `go`, `8000` for `python` and `django`, `3000` for everything else (headless uses `8080` only when `--framework=static`, else `3000`).\n- **Database prompt**: offered only for backend presets (`node`, `nestjs`, `nextjs`, `nuxt`, `python`, `django`, `rails`, `go`).\n\n## Framework requirements cheat sheet\n\n| Framework | What `deploy-stack` automates | Application code requirement | Zero-click starter / plugin |\n|---|---|---|---|\n| **Next.js** | Multi-stage Dockerfile, CloudFront edge routing, `vercel.json` parsing | `output: 'standalone'` must be set in `next.config.js` | Built-in CLI detection |\n| **NestJS** | Multi-stage TypeScript build (`dist/`), unprivileged Node runtime | `await app.listen(port, '0.0.0.0')` in `src/main.ts` | `nest-deploy-stack` (`nest add`) |\n| **FastAPI** | Alpine Python container, Uvicorn CLI args, unprivileged port mapping | None (0.0.0.0 set via Docker CMD) | `cookiecutter-fastapi-deploy-stack` |\n| **Django** | Gunicorn WSGI adapter, Celery worker topologies, RDS bindings | None (0.0.0.0 set via Docker CMD) | `cookiecutter-django-deploy-stack` |\n| **Ruby on Rails** | Puma adapter, `RAILS_MASTER_KEY` injection into Secrets Manager placeholder, Kamal Dockerfile replaced with 0-CVE Alpine build | None (0.0.0.0 set via Docker CMD) | `rails-template-deploy-stack` |\n| **Nuxt 3** | Nitro-optimized Node output | None (`NITRO_HOST=0.0.0.0` injected automatically) | `nuxt-deploy-stack` |\n| **SvelteKit** | Node adapter conversion | None (`HOST=0.0.0.0` injected automatically) | `svelte-adapter-deploy-stack` |\n| **Static Sites** *(Vite, Astro, React)* | Output folder detection (`dist/`, `build/`), Nginx routing | None | `vite-plugin-deploy-stack` |\n\n## Post-detection checks\n\nAfter detection, setup validates framework-specific requirements and warns before generating:\n\n- **NestJS**: `src/main.ts` (or `main.js`) must bind `0.0.0.0`, e.g. `await app.listen(process.env.PORT ?? 3000, '0.0.0.0')`.\n- **Next.js**: config must set `output: 'standalone'` (`.js/.mjs/.cjs/.ts` checked).\n- **SvelteKit**: adapter must not be `@sveltejs/adapter-vercel` or `adapter-auto`.\n- **Astro**: adapter must not be `@astrojs/vercel`.\n\nAlongside detection, setup also auto-detects `Procfile` (web/worker commands), `vercel.json` edge rules (translated to ALB listener rules), and `docker-compose.yml` services (port override plus sidecars).\n\n## The golden rule: 0.0.0.0 vs localhost\n\nThe most common reason a newly deployed container fails its ALB health check is network binding.\n\nIn local development, frameworks bind to `localhost` (or `127.0.0.1`) for security. However, inside a Docker container on AWS ECS, binding to `localhost` means the web server only listens to internal container traffic. The AWS Application Load Balancer (ALB) trying to route traffic from the outside world will hit a closed port, resulting in a `502 Bad Gateway` or `503 Service Temporarily Unavailable`.\n\n**Always ensure your application explicitly binds to `0.0.0.0`.**\n\n## See also\n\n- [Dockerfiles & the container contract](/deploy-stack/guides/dockerfiles/) for what your app must do at runtime.\n- [Headless Mode](/deploy-stack/guides/headless/) for automating framework selection.\n- [Examples](/deploy-stack/guides/examples/) for reference repositories and ecosystem plugins per framework.","src/content/docs/guides/frameworks.md","fd4f31e20b265b25",{"html":827,"metadata":828},"\u003Cp>\u003Ccode dir=\"auto\">deploy-stack\u003C/code> is designed to be as “zero-config” as possible. During setup, it inspects your repo (\u003Ccode dir=\"auto\">src/utils/detector.js\u003C/code>) and preselects a framework preset. However, because different frameworks have unique internal architectures (especially around network binding and build outputs), a few frameworks require minor application-level tweaks to run securely in a Dockerized AWS Fargate environment.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"the-3-tier-support-philosophy\">The 3-tier support philosophy\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#the-3-tier-support-philosophy\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “The 3-tier support philosophy”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>We handle framework requirements using a 3-tier strategy so you are never left guessing why a deployment failed:\u003C/p>\n\u003Col>\n\u003Cli>\u003Cstrong>Zero-touch plugins (Tier 1):\u003C/strong> If you use one of our ecosystem plugins (e.g., \u003Ccode dir=\"auto\">nest add nest-deploy-stack\u003C/code> or \u003Ccode dir=\"auto\">cookiecutter-django-deploy-stack\u003C/code>), your code is automatically patched and configured. Zero manual intervention required.\u003C/li>\n\u003Cli>\u003Cstrong>Intelligent CLI pre-flight (Tier 2):\u003C/strong> If you run the standalone \u003Ccode dir=\"auto\">deploy-stack\u003C/code> CLI against a raw repository, the CLI statically analyzes your code. If it detects a missing production requirement (like a localhost binding), it will flag it inline in your terminal with the exact copy-paste fix.\u003C/li>\n\u003Cli>\u003Cstrong>In-repo docs (Tier 3):\u003C/strong> The generated \u003Ccode dir=\"auto\">DEPLOYMENT.md\u003C/code> file always contains a framework-specific checklist before you push to CI/CD.\u003C/li>\n\u003C/ol>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"detection-precedence\">Detection precedence\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#detection-precedence\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Detection precedence”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>Checks run top-down; the first match wins.\u003C/p>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>#\u003C/th>\n\u003Cth>Signal\u003C/th>\n\u003Cth>Preset (\u003Ccode dir=\"auto\">id\u003C/code> / name)\u003C/th>\n\u003C/tr>\n\u003C/thead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>1\u003C/td>\n\u003Ctd>\u003Ccode dir=\"auto\">package.json\u003C/code> depends on \u003Ccode dir=\"auto\">@nestjs/core\u003C/code>\u003C/td>\n\u003Ctd>\u003Ccode dir=\"auto\">nestjs\u003C/code> / NestJS\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>2\u003C/td>\n\u003Ctd>\u003Ccode dir=\"auto\">package.json\u003C/code> depends on \u003Ccode dir=\"auto\">next\u003C/code>\u003C/td>\n\u003Ctd>\u003Ccode dir=\"auto\">nextjs\u003C/code> / Next.js\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>3\u003C/td>\n\u003Ctd>\u003Ccode dir=\"auto\">package.json\u003C/code> depends on \u003Ccode dir=\"auto\">nuxt\u003C/code>\u003C/td>\n\u003Ctd>\u003Ccode dir=\"auto\">nuxt\u003C/code> / Nuxt 3 (SSR)\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>4\u003C/td>\n\u003Ctd>\u003Ccode dir=\"auto\">package.json\u003C/code> depends on \u003Ccode dir=\"auto\">express\u003C/code>\u003C/td>\n\u003Ctd>\u003Ccode dir=\"auto\">node\u003C/code> / Node.js / Express\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>5\u003C/td>\n\u003Ctd>\u003Ccode dir=\"auto\">package.json\u003C/code> depends on \u003Ccode dir=\"auto\">@sveltejs/kit\u003C/code>\u003C/td>\n\u003Ctd>\u003Ccode dir=\"auto\">svelte\u003C/code> / SvelteKit SSR\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>6\u003C/td>\n\u003Ctd>\u003Ccode dir=\"auto\">package.json\u003C/code> depends on \u003Ccode dir=\"auto\">react-scripts\u003C/code>, \u003Ccode dir=\"auto\">gatsby\u003C/code>, \u003Ccode dir=\"auto\">astro\u003C/code>, \u003Ccode dir=\"auto\">vite\u003C/code>, \u003Ccode dir=\"auto\">@vue/cli-service\u003C/code>, or \u003Ccode dir=\"auto\">@angular/cli\u003C/code>\u003C/td>\n\u003Ctd>\u003Ccode dir=\"auto\">static\u003C/code> / (that generator)\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>7\u003C/td>\n\u003Ctd>\u003Ccode dir=\"auto\">requirements.txt\u003C/code> contains \u003Ccode dir=\"auto\">fastapi\u003C/code>\u003C/td>\n\u003Ctd>\u003Ccode dir=\"auto\">python\u003C/code> / Python FastAPI\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>8\u003C/td>\n\u003Ctd>\u003Ccode dir=\"auto\">requirements.txt\u003C/code> contains \u003Ccode dir=\"auto\">django\u003C/code>, or \u003Ccode dir=\"auto\">manage.py\u003C/code> exists\u003C/td>\n\u003Ctd>\u003Ccode dir=\"auto\">django\u003C/code> / Django\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>9\u003C/td>\n\u003Ctd>\u003Ccode dir=\"auto\">Gemfile\u003C/code> contains a \u003Ccode dir=\"auto\">rails\u003C/code> gem\u003C/td>\n\u003Ctd>\u003Ccode dir=\"auto\">rails\u003C/code> / Ruby on Rails\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>10\u003C/td>\n\u003Ctd>\u003Ccode dir=\"auto\">go.mod\u003C/code> exists\u003C/td>\n\u003Ctd>\u003Ccode dir=\"auto\">go\u003C/code> / Go\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>11\u003C/td>\n\u003Ctd>No match\u003C/td>\n\u003Ctd>No preset — you pick from the interactive list\u003C/td>\n\u003C/tr>\n\u003C/tbody>\n\u003C/table>\n\u003Cp>Notes from the actual code:\u003C/p>\n\u003Cul>\n\u003Cli>Both \u003Ccode dir=\"auto\">dependencies\u003C/code> and \u003Ccode dir=\"auto\">devDependencies\u003C/code> are searched, so a framework listed only under dev dependencies still matches.\u003C/li>\n\u003Cli>A malformed \u003Ccode dir=\"auto\">package.json\u003C/code> or \u003Ccode dir=\"auto\">vercel.json\u003C/code> is silently ignored (no match), never fatal.\u003C/li>\n\u003Cli>An empty \u003Ccode dir=\"auto\">vercel.json\u003C/code> (no \u003Ccode dir=\"auto\">redirects\u003C/code>, \u003Ccode dir=\"auto\">headers\u003C/code>, or \u003Ccode dir=\"auto\">rewrites\u003C/code>) is treated as absent.\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"valid---framework-ids\">Valid \u003Ccode dir=\"auto\">--framework\u003C/code> ids\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#valid---framework-ids\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Valid --framework ids”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>The interactive picker and the headless \u003Ccode dir=\"auto\">--framework\u003C/code> flag accept: \u003Ccode dir=\"auto\">node\u003C/code>, \u003Ccode dir=\"auto\">nestjs\u003C/code>, \u003Ccode dir=\"auto\">nextjs\u003C/code>, \u003Ccode dir=\"auto\">nuxt\u003C/code>, \u003Ccode dir=\"auto\">svelte\u003C/code>, \u003Ccode dir=\"auto\">python\u003C/code>, \u003Ccode dir=\"auto\">django\u003C/code>, \u003Ccode dir=\"auto\">rails\u003C/code>, \u003Ccode dir=\"auto\">go\u003C/code>, \u003Ccode dir=\"auto\">static\u003C/code>. In headless mode with no \u003Ccode dir=\"auto\">--framework\u003C/code>, detection applies and anything unmatched falls back to \u003Ccode dir=\"auto\">static\u003C/code>.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"per-framework-defaults\">Per-framework defaults\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#per-framework-defaults\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Per-framework defaults”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>\u003Cstrong>Static build directory\u003C/strong> (\u003Ccode dir=\"auto\">buildDir\u003C/code>): SvelteKit \u003Ccode dir=\"auto\">build\u003C/code>, Gatsby \u003Ccode dir=\"auto\">public\u003C/code>, everything else (\u003Ccode dir=\"auto\">astro\u003C/code>, \u003Ccode dir=\"auto\">vite\u003C/code>, Vue, Angular) \u003Ccode dir=\"auto\">dist\u003C/code>. This selects the folder the generated \u003Ccode dir=\"auto\">Dockerfile\u003C/code> serves.\u003C/li>\n\u003Cli>\u003Cstrong>Default container port\u003C/strong>: \u003Ccode dir=\"auto\">8080\u003C/code> for \u003Ccode dir=\"auto\">static\u003C/code> and \u003Ccode dir=\"auto\">go\u003C/code>, \u003Ccode dir=\"auto\">8000\u003C/code> for \u003Ccode dir=\"auto\">python\u003C/code> and \u003Ccode dir=\"auto\">django\u003C/code>, \u003Ccode dir=\"auto\">3000\u003C/code> for everything else (headless uses \u003Ccode dir=\"auto\">8080\u003C/code> only when \u003Ccode dir=\"auto\">--framework=static\u003C/code>, else \u003Ccode dir=\"auto\">3000\u003C/code>).\u003C/li>\n\u003Cli>\u003Cstrong>Database prompt\u003C/strong>: offered only for backend presets (\u003Ccode dir=\"auto\">node\u003C/code>, \u003Ccode dir=\"auto\">nestjs\u003C/code>, \u003Ccode dir=\"auto\">nextjs\u003C/code>, \u003Ccode dir=\"auto\">nuxt\u003C/code>, \u003Ccode dir=\"auto\">python\u003C/code>, \u003Ccode dir=\"auto\">django\u003C/code>, \u003Ccode dir=\"auto\">rails\u003C/code>, \u003Ccode dir=\"auto\">go\u003C/code>).\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"framework-requirements-cheat-sheet\">Framework requirements cheat sheet\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#framework-requirements-cheat-sheet\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Framework requirements cheat sheet”\u003C/span>\u003C/a>\u003C/div>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>Framework\u003C/th>\n\u003Cth>What \u003Ccode dir=\"auto\">deploy-stack\u003C/code> automates\u003C/th>\n\u003Cth>Application code requirement\u003C/th>\n\u003Cth>Zero-click starter / plugin\u003C/th>\n\u003C/tr>\n\u003C/thead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Next.js\u003C/strong>\u003C/td>\n\u003Ctd>Multi-stage Dockerfile, CloudFront edge routing, \u003Ccode dir=\"auto\">vercel.json\u003C/code> parsing\u003C/td>\n\u003Ctd>\u003Ccode dir=\"auto\">output: 'standalone'\u003C/code> must be set in \u003Ccode dir=\"auto\">next.config.js\u003C/code>\u003C/td>\n\u003Ctd>Built-in CLI detection\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>NestJS\u003C/strong>\u003C/td>\n\u003Ctd>Multi-stage TypeScript build (\u003Ccode dir=\"auto\">dist/\u003C/code>), unprivileged Node runtime\u003C/td>\n\u003Ctd>\u003Ccode dir=\"auto\">await app.listen(port, '0.0.0.0')\u003C/code> in \u003Ccode dir=\"auto\">src/main.ts\u003C/code>\u003C/td>\n\u003Ctd>\u003Ccode dir=\"auto\">nest-deploy-stack\u003C/code> (\u003Ccode dir=\"auto\">nest add\u003C/code>)\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>FastAPI\u003C/strong>\u003C/td>\n\u003Ctd>Alpine Python container, Uvicorn CLI args, unprivileged port mapping\u003C/td>\n\u003Ctd>None (0.0.0.0 set via Docker CMD)\u003C/td>\n\u003Ctd>\u003Ccode dir=\"auto\">cookiecutter-fastapi-deploy-stack\u003C/code>\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Django\u003C/strong>\u003C/td>\n\u003Ctd>Gunicorn WSGI adapter, Celery worker topologies, RDS bindings\u003C/td>\n\u003Ctd>None (0.0.0.0 set via Docker CMD)\u003C/td>\n\u003Ctd>\u003Ccode dir=\"auto\">cookiecutter-django-deploy-stack\u003C/code>\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Ruby on Rails\u003C/strong>\u003C/td>\n\u003Ctd>Puma adapter, \u003Ccode dir=\"auto\">RAILS_MASTER_KEY\u003C/code> injection into Secrets Manager placeholder, Kamal Dockerfile replaced with 0-CVE Alpine build\u003C/td>\n\u003Ctd>None (0.0.0.0 set via Docker CMD)\u003C/td>\n\u003Ctd>\u003Ccode dir=\"auto\">rails-template-deploy-stack\u003C/code>\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Nuxt 3\u003C/strong>\u003C/td>\n\u003Ctd>Nitro-optimized Node output\u003C/td>\n\u003Ctd>None (\u003Ccode dir=\"auto\">NITRO_HOST=0.0.0.0\u003C/code> injected automatically)\u003C/td>\n\u003Ctd>\u003Ccode dir=\"auto\">nuxt-deploy-stack\u003C/code>\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>SvelteKit\u003C/strong>\u003C/td>\n\u003Ctd>Node adapter conversion\u003C/td>\n\u003Ctd>None (\u003Ccode dir=\"auto\">HOST=0.0.0.0\u003C/code> injected automatically)\u003C/td>\n\u003Ctd>\u003Ccode dir=\"auto\">svelte-adapter-deploy-stack\u003C/code>\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Static Sites\u003C/strong> \u003Cem>(Vite, Astro, React)\u003C/em>\u003C/td>\n\u003Ctd>Output folder detection (\u003Ccode dir=\"auto\">dist/\u003C/code>, \u003Ccode dir=\"auto\">build/\u003C/code>), Nginx routing\u003C/td>\n\u003Ctd>None\u003C/td>\n\u003Ctd>\u003Ccode dir=\"auto\">vite-plugin-deploy-stack\u003C/code>\u003C/td>\n\u003C/tr>\n\u003C/tbody>\n\u003C/table>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"post-detection-checks\">Post-detection checks\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#post-detection-checks\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Post-detection checks”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>After detection, setup validates framework-specific requirements and warns before generating:\u003C/p>\n\u003Cul>\n\u003Cli>\u003Cstrong>NestJS\u003C/strong>: \u003Ccode dir=\"auto\">src/main.ts\u003C/code> (or \u003Ccode dir=\"auto\">main.js\u003C/code>) must bind \u003Ccode dir=\"auto\">0.0.0.0\u003C/code>, e.g. \u003Ccode dir=\"auto\">await app.listen(process.env.PORT ?? 3000, '0.0.0.0')\u003C/code>.\u003C/li>\n\u003Cli>\u003Cstrong>Next.js\u003C/strong>: config must set \u003Ccode dir=\"auto\">output: 'standalone'\u003C/code> (\u003Ccode dir=\"auto\">.js/.mjs/.cjs/.ts\u003C/code> checked).\u003C/li>\n\u003Cli>\u003Cstrong>SvelteKit\u003C/strong>: adapter must not be \u003Ccode dir=\"auto\">@sveltejs/adapter-vercel\u003C/code> or \u003Ccode dir=\"auto\">adapter-auto\u003C/code>.\u003C/li>\n\u003Cli>\u003Cstrong>Astro\u003C/strong>: adapter must not be \u003Ccode dir=\"auto\">@astrojs/vercel\u003C/code>.\u003C/li>\n\u003C/ul>\n\u003Cp>Alongside detection, setup also auto-detects \u003Ccode dir=\"auto\">Procfile\u003C/code> (web/worker commands), \u003Ccode dir=\"auto\">vercel.json\u003C/code> edge rules (translated to ALB listener rules), and \u003Ccode dir=\"auto\">docker-compose.yml\u003C/code> services (port override plus sidecars).\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"the-golden-rule-0000-vs-localhost\">The golden rule: 0.0.0.0 vs localhost\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#the-golden-rule-0000-vs-localhost\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “The golden rule: 0.0.0.0 vs localhost”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>The most common reason a newly deployed container fails its ALB health check is network binding.\u003C/p>\n\u003Cp>In local development, frameworks bind to \u003Ccode dir=\"auto\">localhost\u003C/code> (or \u003Ccode dir=\"auto\">127.0.0.1\u003C/code>) for security. However, inside a Docker container on AWS ECS, binding to \u003Ccode dir=\"auto\">localhost\u003C/code> means the web server only listens to internal container traffic. The AWS Application Load Balancer (ALB) trying to route traffic from the outside world will hit a closed port, resulting in a \u003Ccode dir=\"auto\">502 Bad Gateway\u003C/code> or \u003Ccode dir=\"auto\">503 Service Temporarily Unavailable\u003C/code>.\u003C/p>\n\u003Cp>\u003Cstrong>Always ensure your application explicitly binds to \u003Ccode dir=\"auto\">0.0.0.0\u003C/code>.\u003C/strong>\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"see-also\">See also\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#see-also\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “See also”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>\u003Ca href=\"/deploy-stack/guides/dockerfiles/\">Dockerfiles & the container contract\u003C/a> for what your app must do at runtime.\u003C/li>\n\u003Cli>\u003Ca href=\"/deploy-stack/guides/headless/\">Headless Mode\u003C/a> for automating framework selection.\u003C/li>\n\u003Cli>\u003Ca href=\"/deploy-stack/guides/examples/\">Examples\u003C/a> for reference repositories and ecosystem plugins per framework.\u003C/li>\n\u003C/ul>\n",{"headings":829,"localImagePaths":852,"remoteImagePaths":853,"frontmatter":854,"imagePaths":856},[830,833,836,839,842,845,848,851],{"depth":22,"slug":831,"text":832},"the-3-tier-support-philosophy","The 3-tier support philosophy",{"depth":22,"slug":834,"text":835},"detection-precedence","Detection precedence",{"depth":22,"slug":837,"text":838},"valid---framework-ids","Valid --framework ids",{"depth":22,"slug":840,"text":841},"per-framework-defaults","Per-framework defaults",{"depth":22,"slug":843,"text":844},"framework-requirements-cheat-sheet","Framework requirements cheat sheet",{"depth":22,"slug":846,"text":847},"post-detection-checks","Post-detection checks",{"depth":22,"slug":849,"text":850},"the-golden-rule-0000-vs-localhost","The golden rule: 0.0.0.0 vs localhost",{"depth":22,"slug":323,"text":324},[],[],{"title":818,"description":819,"sidebar":855},{"order":22},[],"guides/headless",{"id":857,"data":859,"body":866,"filePath":867,"digest":868,"rendered":869},{"title":860,"description":861,"editUrl":8,"head":862,"template":10,"sidebar":863,"pagefind":8,"draft":12},"Headless Mode & Automation Guide","Run deploy-stack without prompts for CI/CD pipelines, scripts, and framework plugins.",[],{"order":864,"hidden":12,"attrs":865},11,{},"The `deploy-stack` CLI is designed to be fully automatable for CI/CD pipelines, custom scripts, Cookiecutters, and framework plugins (like `vite-plugin-deploy-stack`). \n\nBy passing the `--headless` flag, you bypass all interactive terminal prompts. This is a tested contract (`tests/headless.test.js`): with `--headless --preconfigured`, the CLI guarantees no interactive prompt ever fires, so external schematics and CI pipelines can invoke it without hanging.\n\n## Required Flags\nTo use headless mode, simply include the `--headless` flag. \n\nIf `deploy-stack` cannot auto-detect your framework, you should also provide the `--framework` flag to ensure the correct infrastructure is generated.\n\n* **Valid `--framework` options:** `node`, `nestjs`, `nextjs`, `nuxt`, `svelte`, `python`, `django`, `rails`, `go`, `static`\n\n## Optional Configuration Flags\n\nYou can append any of these flags to customize the generated architecture. These map exactly to the options available in the interactive setup:\n\n| Flag | Description | Default |\n|---|---|---|\n| `--region=\u003Cregion>` | The AWS region to deploy to (e.g., `us-east-1`, `eu-west-1`). | `us-east-2` |\n| `--size=\u003Csize>` | The Fargate compute size (`micro` or `small`). | `micro` |\n| `--port=\u003Cnumber>` | The internal port your container exposes. | Framework dependent (`3000`, `8080`, `8000`) |\n| `--healthCheckPath=\u003Cpath>`| The ALB health check endpoint path. | `/` |\n| `--desiredCount=\u003Cnumber>` | Number of container replicas to run (`1` or `2`). | `1` |\n| `--branch=\u003Cname>` | The primary Git deployment branch for CI/CD. | `main` |\n| `--dir=\u003Cpath>` | The directory to generate files into (use `.` for current).| `.` |\n| `--needsDatabase` | Provisions a managed AWS RDS PostgreSQL database alongside Fargate. | `false` |\n| `--enablePrPreviews` | Generates workflows for Ephemeral PR Previews. | `false` |\n| `--yes` | Automatically bypasses confirmation prompts during apply/destroy. | `false` |\n| `--no-telemetry` | Disables anonymous usage analytics. | `false` |\n| `--preconfigured` | Suppresses framework warnings for pre-validated configs from external schematics/integrations (e.g., `nest add`). | `false` |\n\n*(Note: Boolean flags like `--needsDatabase` and `--enablePrPreviews` can be passed alone or as `--flag=true`).*\n\n## Example Usage\n\n**Standard Static Site Automation (e.g., Vite/React):**\n```bash\nnpx deploy-stack --headless --framework=static --region=eu-west-1 --size=micro\n```\n\n**Next.js High-Availability CI/CD Generation:**\n```bash\nnpx deploy-stack --headless --framework=nextjs --size=small --desiredCount=2 --yes\n```\n\n**Django Setup with Managed RDS Database:**\n```bash\nnpx deploy-stack --headless --framework=django --needsDatabase\n```\n\n## See also\n\n- [`npx deploy-stack (init)`](/deploy-stack/cli/init/) for the full flag table.\n- [Supported Frameworks](/deploy-stack/guides/frameworks/) for valid `--framework` ids.\n- [CI/CD Pipeline & First Deploy](/deploy-stack/guides/cicd-pipeline/) for what runs after generation.","src/content/docs/guides/headless.md","0c7e629a093876bc",{"html":870,"metadata":871},"\u003Cp>The \u003Ccode dir=\"auto\">deploy-stack\u003C/code> CLI is designed to be fully automatable for CI/CD pipelines, custom scripts, Cookiecutters, and framework plugins (like \u003Ccode dir=\"auto\">vite-plugin-deploy-stack\u003C/code>).\u003C/p>\n\u003Cp>By passing the \u003Ccode dir=\"auto\">--headless\u003C/code> flag, you bypass all interactive terminal prompts. This is a tested contract (\u003Ccode dir=\"auto\">tests/headless.test.js\u003C/code>): with \u003Ccode dir=\"auto\">--headless --preconfigured\u003C/code>, the CLI guarantees no interactive prompt ever fires, so external schematics and CI pipelines can invoke it without hanging.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"required-flags\">Required Flags\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#required-flags\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Required Flags”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>To use headless mode, simply include the \u003Ccode dir=\"auto\">--headless\u003C/code> flag.\u003C/p>\n\u003Cp>If \u003Ccode dir=\"auto\">deploy-stack\u003C/code> cannot auto-detect your framework, you should also provide the \u003Ccode dir=\"auto\">--framework\u003C/code> flag to ensure the correct infrastructure is generated.\u003C/p>\n\u003Cul>\n\u003Cli>\u003Cstrong>Valid \u003Ccode dir=\"auto\">--framework\u003C/code> options:\u003C/strong> \u003Ccode dir=\"auto\">node\u003C/code>, \u003Ccode dir=\"auto\">nestjs\u003C/code>, \u003Ccode dir=\"auto\">nextjs\u003C/code>, \u003Ccode dir=\"auto\">nuxt\u003C/code>, \u003Ccode dir=\"auto\">svelte\u003C/code>, \u003Ccode dir=\"auto\">python\u003C/code>, \u003Ccode dir=\"auto\">django\u003C/code>, \u003Ccode dir=\"auto\">rails\u003C/code>, \u003Ccode dir=\"auto\">go\u003C/code>, \u003Ccode dir=\"auto\">static\u003C/code>\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"optional-configuration-flags\">Optional Configuration Flags\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#optional-configuration-flags\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Optional Configuration Flags”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>You can append any of these flags to customize the generated architecture. These map exactly to the options available in the interactive setup:\u003C/p>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>Flag\u003C/th>\n\u003Cth>Description\u003C/th>\n\u003Cth>Default\u003C/th>\n\u003C/tr>\n\u003C/thead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Ccode dir=\"auto\">--region=<region>\u003C/code>\u003C/td>\n\u003Ctd>The AWS region to deploy to (e.g., \u003Ccode dir=\"auto\">us-east-1\u003C/code>, \u003Ccode dir=\"auto\">eu-west-1\u003C/code>).\u003C/td>\n\u003Ctd>\u003Ccode dir=\"auto\">us-east-2\u003C/code>\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode dir=\"auto\">--size=<size>\u003C/code>\u003C/td>\n\u003Ctd>The Fargate compute size (\u003Ccode dir=\"auto\">micro\u003C/code> or \u003Ccode dir=\"auto\">small\u003C/code>).\u003C/td>\n\u003Ctd>\u003Ccode dir=\"auto\">micro\u003C/code>\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode dir=\"auto\">--port=<number>\u003C/code>\u003C/td>\n\u003Ctd>The internal port your container exposes.\u003C/td>\n\u003Ctd>Framework dependent (\u003Ccode dir=\"auto\">3000\u003C/code>, \u003Ccode dir=\"auto\">8080\u003C/code>, \u003Ccode dir=\"auto\">8000\u003C/code>)\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode dir=\"auto\">--healthCheckPath=<path>\u003C/code>\u003C/td>\n\u003Ctd>The ALB health check endpoint path.\u003C/td>\n\u003Ctd>\u003Ccode dir=\"auto\">/\u003C/code>\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode dir=\"auto\">--desiredCount=<number>\u003C/code>\u003C/td>\n\u003Ctd>Number of container replicas to run (\u003Ccode dir=\"auto\">1\u003C/code> or \u003Ccode dir=\"auto\">2\u003C/code>).\u003C/td>\n\u003Ctd>\u003Ccode dir=\"auto\">1\u003C/code>\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode dir=\"auto\">--branch=<name>\u003C/code>\u003C/td>\n\u003Ctd>The primary Git deployment branch for CI/CD.\u003C/td>\n\u003Ctd>\u003Ccode dir=\"auto\">main\u003C/code>\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode dir=\"auto\">--dir=<path>\u003C/code>\u003C/td>\n\u003Ctd>The directory to generate files into (use \u003Ccode dir=\"auto\">.\u003C/code> for current).\u003C/td>\n\u003Ctd>\u003Ccode dir=\"auto\">.\u003C/code>\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode dir=\"auto\">--needsDatabase\u003C/code>\u003C/td>\n\u003Ctd>Provisions a managed AWS RDS PostgreSQL database alongside Fargate.\u003C/td>\n\u003Ctd>\u003Ccode dir=\"auto\">false\u003C/code>\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode dir=\"auto\">--enablePrPreviews\u003C/code>\u003C/td>\n\u003Ctd>Generates workflows for Ephemeral PR Previews.\u003C/td>\n\u003Ctd>\u003Ccode dir=\"auto\">false\u003C/code>\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode dir=\"auto\">--yes\u003C/code>\u003C/td>\n\u003Ctd>Automatically bypasses confirmation prompts during apply/destroy.\u003C/td>\n\u003Ctd>\u003Ccode dir=\"auto\">false\u003C/code>\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode dir=\"auto\">--no-telemetry\u003C/code>\u003C/td>\n\u003Ctd>Disables anonymous usage analytics.\u003C/td>\n\u003Ctd>\u003Ccode dir=\"auto\">false\u003C/code>\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode dir=\"auto\">--preconfigured\u003C/code>\u003C/td>\n\u003Ctd>Suppresses framework warnings for pre-validated configs from external schematics/integrations (e.g., \u003Ccode dir=\"auto\">nest add\u003C/code>).\u003C/td>\n\u003Ctd>\u003Ccode dir=\"auto\">false\u003C/code>\u003C/td>\n\u003C/tr>\n\u003C/tbody>\n\u003C/table>\n\u003Cp>\u003Cem>(Note: Boolean flags like \u003Ccode dir=\"auto\">--needsDatabase\u003C/code> and \u003Ccode dir=\"auto\">--enablePrPreviews\u003C/code> can be passed alone or as \u003Ccode dir=\"auto\">--flag=true\u003C/code>).\u003C/em>\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"example-usage\">Example Usage\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#example-usage\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Example Usage”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>\u003Cstrong>Standard Static Site Automation (e.g., Vite/React):\u003C/strong>\u003C/p>\n\u003Cdiv class=\"expressive-code\">\u003Clink rel=\"stylesheet\" href=\"/deploy-stack/_astro/ec.w36nc.css\">\u003Cscript type=\"module\" src=\"/deploy-stack/_astro/ec.0vx5m.js\">\u003C/script>\u003Cfigure class=\"frame is-terminal not-content\">\u003Cfigcaption class=\"header\">\u003Cspan class=\"title\">\u003C/span>\u003Cspan class=\"sr-only\">Terminal window\u003C/span>\u003C/figcaption>\u003Cpre data-language=\"bash\">\u003Ccode>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">npx\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">deploy-stack\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">--headless\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">--framework=static\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">--region=eu-west-1\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">--size=micro\u003C/span>\u003C/div>\u003C/div>\u003C/code>\u003C/pre>\u003Cdiv class=\"copy\">\u003Cdiv aria-live=\"polite\">\u003C/div>\u003Cbutton title=\"Copy to clipboard\" data-copied=\"Copied!\" data-code=\"npx deploy-stack --headless --framework=static --region=eu-west-1 --size=micro\">\u003Cdiv>\u003C/div>\u003C/button>\u003C/div>\u003C/figure>\u003C/div>\n\u003Cp>\u003Cstrong>Next.js High-Availability CI/CD Generation:\u003C/strong>\u003C/p>\n\u003Cdiv class=\"expressive-code\">\u003Cfigure class=\"frame is-terminal not-content\">\u003Cfigcaption class=\"header\">\u003Cspan class=\"title\">\u003C/span>\u003Cspan class=\"sr-only\">Terminal window\u003C/span>\u003C/figcaption>\u003Cpre data-language=\"bash\">\u003Ccode>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">npx\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">deploy-stack\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">--headless\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">--framework=nextjs\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">--size=small\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">--desiredCount=2\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">--yes\u003C/span>\u003C/div>\u003C/div>\u003C/code>\u003C/pre>\u003Cdiv class=\"copy\">\u003Cdiv aria-live=\"polite\">\u003C/div>\u003Cbutton title=\"Copy to clipboard\" data-copied=\"Copied!\" data-code=\"npx deploy-stack --headless --framework=nextjs --size=small --desiredCount=2 --yes\">\u003Cdiv>\u003C/div>\u003C/button>\u003C/div>\u003C/figure>\u003C/div>\n\u003Cp>\u003Cstrong>Django Setup with Managed RDS Database:\u003C/strong>\u003C/p>\n\u003Cdiv class=\"expressive-code\">\u003Cfigure class=\"frame is-terminal not-content\">\u003Cfigcaption class=\"header\">\u003Cspan class=\"title\">\u003C/span>\u003Cspan class=\"sr-only\">Terminal window\u003C/span>\u003C/figcaption>\u003Cpre data-language=\"bash\">\u003Ccode>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">npx\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">deploy-stack\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">--headless\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">--framework=django\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">--needsDatabase\u003C/span>\u003C/div>\u003C/div>\u003C/code>\u003C/pre>\u003Cdiv class=\"copy\">\u003Cdiv aria-live=\"polite\">\u003C/div>\u003Cbutton title=\"Copy to clipboard\" data-copied=\"Copied!\" data-code=\"npx deploy-stack --headless --framework=django --needsDatabase\">\u003Cdiv>\u003C/div>\u003C/button>\u003C/div>\u003C/figure>\u003C/div>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"see-also\">See also\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#see-also\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “See also”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>\u003Ca href=\"/deploy-stack/cli/init/\">\u003Ccode dir=\"auto\">npx deploy-stack (init)\u003C/code>\u003C/a> for the full flag table.\u003C/li>\n\u003Cli>\u003Ca href=\"/deploy-stack/guides/frameworks/\">Supported Frameworks\u003C/a> for valid \u003Ccode dir=\"auto\">--framework\u003C/code> ids.\u003C/li>\n\u003Cli>\u003Ca href=\"/deploy-stack/guides/cicd-pipeline/\">CI/CD Pipeline & First Deploy\u003C/a> for what runs after generation.\u003C/li>\n\u003C/ul>\n",{"headings":872,"localImagePaths":883,"remoteImagePaths":884,"frontmatter":885,"imagePaths":887},[873,876,879,882],{"depth":22,"slug":874,"text":875},"required-flags","Required Flags",{"depth":22,"slug":877,"text":878},"optional-configuration-flags","Optional Configuration Flags",{"depth":22,"slug":880,"text":881},"example-usage","Example Usage",{"depth":22,"slug":323,"text":324},[],[],{"title":860,"description":861,"sidebar":886},{"order":864},[],"guides/quickstart",{"id":888,"data":890,"body":897,"filePath":898,"digest":899,"rendered":900},{"title":891,"description":892,"editUrl":8,"head":893,"template":10,"sidebar":894,"pagefind":8,"draft":12},"Quickstart (5 minutes)","Go from empty repo to live AWS deployment in five minutes with deploy-stack.",[],{"order":895,"hidden":12,"attrs":896},0,{},"Deploy your first app to AWS in about five minutes. This is the fastest path; follow the links for details at each step.\n\n## Prerequisites\n\n- Node.js 18+, an AWS account, and AWS credentials in your terminal (`aws sso login` or `aws configure`).\n- A git repository with your app. Stuck on auth? See [Troubleshooting AWS Credentials](/deploy-stack/guides/aws-credentials/).\n\n## Step 1 — Scaffold\n\n```bash\nnpx deploy-stack\n```\n\nThe wizard auto-detects your framework, `Procfile`, `vercel.json`, and `docker-compose.yml`, then writes Terraform, a `Dockerfile`, and `.github/workflows/deploy.yml`. Not sure your stack is supported? Check [Supported Frameworks](/deploy-stack/guides/frameworks/).\n\n## Step 2 — Provision\n\n```bash\nnpx deploy-stack apply\n```\n\nThis creates the ALB, ECS cluster, and service. Your URL returns `503` until the first image is pushed — that is expected.\n\n## Step 3 — Ship\n\n```bash\ngit add .\ngit commit -m \"ci: infra\"\ngit push\n```\n\nPushing to your deploy branch triggers the pipeline: Terraform sync, Docker build, image scan, ECS rollout. How it works is explained in [CI/CD Pipeline & First Deploy](/deploy-stack/guides/cicd-pipeline/).\n\n## Step 4 — Verify\n\n- Open the ALB URL from the `apply` output.\n- Still seeing `503` or `502` after the workflow finishes? Run `npx deploy-stack diagnose` — usually the container failed its health check. See [Dockerfiles & the Container Contract](/deploy-stack/guides/dockerfiles/).\n- Need env vars? Continue with [Secrets Management](/deploy-stack/guides/secrets-management/).\n\n## Next steps\n\n- [Supported Frameworks](/deploy-stack/guides/frameworks/) — framework requirements and detection rules.\n- [Reference Implementations & Examples](/deploy-stack/guides/examples/) — working repos per framework.\n- [Headless Mode & Automation](/deploy-stack/guides/headless/) — non-interactive `npx deploy-stack --headless` for CI.\n- Migrating? Start with [Vercel (Next.js)](/deploy-stack/migrations/nextjs-vercel-to-aws/), [Vercel (Astro)](/deploy-stack/migrations/astro-vercel-to-aws/), [Vercel (SvelteKit)](/deploy-stack/migrations/sveltekit-vercel-to-aws/), or [Heroku (Procfile)](/deploy-stack/migrations/heroku-procfile-to-aws/).","src/content/docs/guides/quickstart.md","2a30528eff2bf314",{"html":901,"metadata":902},"\u003Cp>Deploy your first app to AWS in about five minutes. This is the fastest path; follow the links for details at each step.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"prerequisites\">Prerequisites\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#prerequisites\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Prerequisites”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>Node.js 18+, an AWS account, and AWS credentials in your terminal (\u003Ccode dir=\"auto\">aws sso login\u003C/code> or \u003Ccode dir=\"auto\">aws configure\u003C/code>).\u003C/li>\n\u003Cli>A git repository with your app. Stuck on auth? See \u003Ca href=\"/deploy-stack/guides/aws-credentials/\">Troubleshooting AWS Credentials\u003C/a>.\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"step-1--scaffold\">Step 1 — Scaffold\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#step-1--scaffold\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Step 1 — Scaffold”\u003C/span>\u003C/a>\u003C/div>\n\u003Cdiv class=\"expressive-code\">\u003Clink rel=\"stylesheet\" href=\"/deploy-stack/_astro/ec.w36nc.css\">\u003Cscript type=\"module\" src=\"/deploy-stack/_astro/ec.0vx5m.js\">\u003C/script>\u003Cfigure class=\"frame is-terminal not-content\">\u003Cfigcaption class=\"header\">\u003Cspan class=\"title\">\u003C/span>\u003Cspan class=\"sr-only\">Terminal window\u003C/span>\u003C/figcaption>\u003Cpre data-language=\"bash\">\u003Ccode>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">npx\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">deploy-stack\u003C/span>\u003C/div>\u003C/div>\u003C/code>\u003C/pre>\u003Cdiv class=\"copy\">\u003Cdiv aria-live=\"polite\">\u003C/div>\u003Cbutton title=\"Copy to clipboard\" data-copied=\"Copied!\" data-code=\"npx deploy-stack\">\u003Cdiv>\u003C/div>\u003C/button>\u003C/div>\u003C/figure>\u003C/div>\n\u003Cp>The wizard auto-detects your framework, \u003Ccode dir=\"auto\">Procfile\u003C/code>, \u003Ccode dir=\"auto\">vercel.json\u003C/code>, and \u003Ccode dir=\"auto\">docker-compose.yml\u003C/code>, then writes Terraform, a \u003Ccode dir=\"auto\">Dockerfile\u003C/code>, and \u003Ccode dir=\"auto\">.github/workflows/deploy.yml\u003C/code>. Not sure your stack is supported? Check \u003Ca href=\"/deploy-stack/guides/frameworks/\">Supported Frameworks\u003C/a>.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"step-2--provision\">Step 2 — Provision\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#step-2--provision\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Step 2 — Provision”\u003C/span>\u003C/a>\u003C/div>\n\u003Cdiv class=\"expressive-code\">\u003Cfigure class=\"frame is-terminal not-content\">\u003Cfigcaption class=\"header\">\u003Cspan class=\"title\">\u003C/span>\u003Cspan class=\"sr-only\">Terminal window\u003C/span>\u003C/figcaption>\u003Cpre data-language=\"bash\">\u003Ccode>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">npx\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">deploy-stack\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">apply\u003C/span>\u003C/div>\u003C/div>\u003C/code>\u003C/pre>\u003Cdiv class=\"copy\">\u003Cdiv aria-live=\"polite\">\u003C/div>\u003Cbutton title=\"Copy to clipboard\" data-copied=\"Copied!\" data-code=\"npx deploy-stack apply\">\u003Cdiv>\u003C/div>\u003C/button>\u003C/div>\u003C/figure>\u003C/div>\n\u003Cp>This creates the ALB, ECS cluster, and service. Your URL returns \u003Ccode dir=\"auto\">503\u003C/code> until the first image is pushed — that is expected.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"step-3--ship\">Step 3 — Ship\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#step-3--ship\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Step 3 — Ship”\u003C/span>\u003C/a>\u003C/div>\n\u003Cdiv class=\"expressive-code\">\u003Cfigure class=\"frame is-terminal not-content\">\u003Cfigcaption class=\"header\">\u003Cspan class=\"title\">\u003C/span>\u003Cspan class=\"sr-only\">Terminal window\u003C/span>\u003C/figcaption>\u003Cpre data-language=\"bash\">\u003Ccode>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">git\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">add\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">.\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">git\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">commit\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">-m\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#D9F5DD;--1:#111111\">\"\u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#984E4D\">ci: infra\u003C/span>\u003Cspan style=\"--0:#D9F5DD;--1:#111111\">\"\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">git\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">push\u003C/span>\u003C/div>\u003C/div>\u003C/code>\u003C/pre>\u003Cdiv class=\"copy\">\u003Cdiv aria-live=\"polite\">\u003C/div>\u003Cbutton title=\"Copy to clipboard\" data-copied=\"Copied!\" data-code=\"git add .git commit -m "ci: infra"git push\">\u003Cdiv>\u003C/div>\u003C/button>\u003C/div>\u003C/figure>\u003C/div>\n\u003Cp>Pushing to your deploy branch triggers the pipeline: Terraform sync, Docker build, image scan, ECS rollout. How it works is explained in \u003Ca href=\"/deploy-stack/guides/cicd-pipeline/\">CI/CD Pipeline & First Deploy\u003C/a>.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"step-4--verify\">Step 4 — Verify\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#step-4--verify\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Step 4 — Verify”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>Open the ALB URL from the \u003Ccode dir=\"auto\">apply\u003C/code> output.\u003C/li>\n\u003Cli>Still seeing \u003Ccode dir=\"auto\">503\u003C/code> or \u003Ccode dir=\"auto\">502\u003C/code> after the workflow finishes? Run \u003Ccode dir=\"auto\">npx deploy-stack diagnose\u003C/code> — usually the container failed its health check. See \u003Ca href=\"/deploy-stack/guides/dockerfiles/\">Dockerfiles & the Container Contract\u003C/a>.\u003C/li>\n\u003Cli>Need env vars? Continue with \u003Ca href=\"/deploy-stack/guides/secrets-management/\">Secrets Management\u003C/a>.\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"next-steps\">Next steps\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#next-steps\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Next steps”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>\u003Ca href=\"/deploy-stack/guides/frameworks/\">Supported Frameworks\u003C/a> — framework requirements and detection rules.\u003C/li>\n\u003Cli>\u003Ca href=\"/deploy-stack/guides/examples/\">Reference Implementations & Examples\u003C/a> — working repos per framework.\u003C/li>\n\u003Cli>\u003Ca href=\"/deploy-stack/guides/headless/\">Headless Mode & Automation\u003C/a> — non-interactive \u003Ccode dir=\"auto\">npx deploy-stack --headless\u003C/code> for CI.\u003C/li>\n\u003Cli>Migrating? Start with \u003Ca href=\"/deploy-stack/migrations/nextjs-vercel-to-aws/\">Vercel (Next.js)\u003C/a>, \u003Ca href=\"/deploy-stack/migrations/astro-vercel-to-aws/\">Vercel (Astro)\u003C/a>, \u003Ca href=\"/deploy-stack/migrations/sveltekit-vercel-to-aws/\">Vercel (SvelteKit)\u003C/a>, or \u003Ca href=\"/deploy-stack/migrations/heroku-procfile-to-aws/\">Heroku (Procfile)\u003C/a>.\u003C/li>\n\u003C/ul>\n",{"headings":903,"localImagePaths":920,"remoteImagePaths":921,"frontmatter":922,"imagePaths":924},[904,905,908,911,914,917],{"depth":22,"slug":417,"text":418},{"depth":22,"slug":906,"text":907},"step-1--scaffold","Step 1 — Scaffold",{"depth":22,"slug":909,"text":910},"step-2--provision","Step 2 — Provision",{"depth":22,"slug":912,"text":913},"step-3--ship","Step 3 — Ship",{"depth":22,"slug":915,"text":916},"step-4--verify","Step 4 — Verify",{"depth":22,"slug":918,"text":919},"next-steps","Next steps",[],[],{"title":891,"description":892,"sidebar":923},{"order":895},[],"guides/rerun-init",{"id":925,"data":927,"body":934,"filePath":935,"digest":936,"rendered":937},{"title":928,"description":929,"editUrl":8,"head":930,"template":10,"sidebar":931,"pagefind":8,"draft":12},"Re-running Init Safely","What happens when setup finds existing files — backups, regeneration, and how to recover.",[],{"order":932,"hidden":12,"attrs":933},10,{},"Re-running `npx deploy-stack` to change region, size, or framework is safe and predictable: setup never merges with your existing generated files. It backs them up, regenerates from scratch, and tells you exactly what moved.\n\n## The conflict prompt\n\nWhen setup finds any of `terraform/`, `Dockerfile`, or `.github/workflows/deploy.yml` in the target directory (`src/utils/backup.js`), it lists the conflicts and offers two choices:\n\n- **Backup & Regenerate** — each conflicting path is renamed with a timestamp suffix (e.g. `terraform.bak.1726771200000`), then fresh files are generated.\n- **Cancel** — exits immediately with no changes.\n\nIn `--headless` mode there is no prompt: existing files are backed up automatically. Either way, nothing is ever merged or partially overwritten.\n\n## Backups stay local\n\nAfter backing up, setup appends a `# deploy-stack backups` block (`*.bak.*`) to `.gitignore` (creating the file if needed), so backup clutter never reaches GitHub. To recover a previous configuration, compare with `diff -r terraform.bak.\u003Ctimestamp> terraform/` and copy back what you need — then delete the `.bak.*` directory when you are satisfied. (`npx deploy-stack eject` removes all `*.bak.*` files as part of decoupling.)\n\n## What regeneration touches\n\n`src/utils/generator.js` writes a fixed file set and handles pre-existing files explicitly:\n\n- `terraform/*.tf`, `Dockerfile`, `.github/workflows/deploy.yml`, plus `preview.yml`/`teardown.yml` only when PR previews are enabled.\n- `terraform/secret_keys.json` is reset to `[]` — re-push secrets afterward with `npx deploy-stack secrets push`.\n- If your repo already has a `README.md`, it is kept and gets a short Deployment pointer appended; the generated guide goes to `DEPLOYMENT.md` instead.\n- Existing `.gitignore` / `.dockerignore` files are preserved with only the deploy-stack entries appended (Terraform state paths, `.env`); missing ones are created with framework-appropriate presets.\n- **Rails only:** if `ci.yml` or `dependabot.yml` exist, setup asks whether to disable them by renaming to `.bak` (default CI usually crashes without a database service); in headless mode they are disabled automatically.\n\n## Suggested workflow\n\n1. Commit your work before re-running, so `git status` shows exactly what regeneration changed.\n2. Re-run, review the diff (`git diff`, plus `diff -r` against the `.bak` copies for untracked files like `terraform/` internals).\n3. Run `npx deploy-stack apply` to converge AWS with the new configuration.\n4. Delete the `.bak.\u003Ctimestamp>` copies once the new infrastructure is verified.\n\n## See also\n\n- [apply](/deploy-stack/cli/apply/) for converging AWS after regeneration.\n- [eject](/deploy-stack/cli/eject/) for what happens to backups on decoupling.","src/content/docs/guides/rerun-init.md","4896157f2e426802",{"html":938,"metadata":939},"\u003Cp>Re-running \u003Ccode dir=\"auto\">npx deploy-stack\u003C/code> to change region, size, or framework is safe and predictable: setup never merges with your existing generated files. It backs them up, regenerates from scratch, and tells you exactly what moved.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"the-conflict-prompt\">The conflict prompt\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#the-conflict-prompt\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “The conflict prompt”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>When setup finds any of \u003Ccode dir=\"auto\">terraform/\u003C/code>, \u003Ccode dir=\"auto\">Dockerfile\u003C/code>, or \u003Ccode dir=\"auto\">.github/workflows/deploy.yml\u003C/code> in the target directory (\u003Ccode dir=\"auto\">src/utils/backup.js\u003C/code>), it lists the conflicts and offers two choices:\u003C/p>\n\u003Cul>\n\u003Cli>\u003Cstrong>Backup & Regenerate\u003C/strong> — each conflicting path is renamed with a timestamp suffix (e.g. \u003Ccode dir=\"auto\">terraform.bak.1726771200000\u003C/code>), then fresh files are generated.\u003C/li>\n\u003Cli>\u003Cstrong>Cancel\u003C/strong> — exits immediately with no changes.\u003C/li>\n\u003C/ul>\n\u003Cp>In \u003Ccode dir=\"auto\">--headless\u003C/code> mode there is no prompt: existing files are backed up automatically. Either way, nothing is ever merged or partially overwritten.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"backups-stay-local\">Backups stay local\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#backups-stay-local\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Backups stay local”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>After backing up, setup appends a \u003Ccode dir=\"auto\"># deploy-stack backups\u003C/code> block (\u003Ccode dir=\"auto\">*.bak.*\u003C/code>) to \u003Ccode dir=\"auto\">.gitignore\u003C/code> (creating the file if needed), so backup clutter never reaches GitHub. To recover a previous configuration, compare with \u003Ccode dir=\"auto\">diff -r terraform.bak.<timestamp> terraform/\u003C/code> and copy back what you need — then delete the \u003Ccode dir=\"auto\">.bak.*\u003C/code> directory when you are satisfied. (\u003Ccode dir=\"auto\">npx deploy-stack eject\u003C/code> removes all \u003Ccode dir=\"auto\">*.bak.*\u003C/code> files as part of decoupling.)\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"what-regeneration-touches\">What regeneration touches\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#what-regeneration-touches\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “What regeneration touches”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>\u003Ccode dir=\"auto\">src/utils/generator.js\u003C/code> writes a fixed file set and handles pre-existing files explicitly:\u003C/p>\n\u003Cul>\n\u003Cli>\u003Ccode dir=\"auto\">terraform/*.tf\u003C/code>, \u003Ccode dir=\"auto\">Dockerfile\u003C/code>, \u003Ccode dir=\"auto\">.github/workflows/deploy.yml\u003C/code>, plus \u003Ccode dir=\"auto\">preview.yml\u003C/code>/\u003Ccode dir=\"auto\">teardown.yml\u003C/code> only when PR previews are enabled.\u003C/li>\n\u003Cli>\u003Ccode dir=\"auto\">terraform/secret_keys.json\u003C/code> is reset to \u003Ccode dir=\"auto\">[]\u003C/code> — re-push secrets afterward with \u003Ccode dir=\"auto\">npx deploy-stack secrets push\u003C/code>.\u003C/li>\n\u003Cli>If your repo already has a \u003Ccode dir=\"auto\">README.md\u003C/code>, it is kept and gets a short Deployment pointer appended; the generated guide goes to \u003Ccode dir=\"auto\">DEPLOYMENT.md\u003C/code> instead.\u003C/li>\n\u003Cli>Existing \u003Ccode dir=\"auto\">.gitignore\u003C/code> / \u003Ccode dir=\"auto\">.dockerignore\u003C/code> files are preserved with only the deploy-stack entries appended (Terraform state paths, \u003Ccode dir=\"auto\">.env\u003C/code>); missing ones are created with framework-appropriate presets.\u003C/li>\n\u003Cli>\u003Cstrong>Rails only:\u003C/strong> if \u003Ccode dir=\"auto\">ci.yml\u003C/code> or \u003Ccode dir=\"auto\">dependabot.yml\u003C/code> exist, setup asks whether to disable them by renaming to \u003Ccode dir=\"auto\">.bak\u003C/code> (default CI usually crashes without a database service); in headless mode they are disabled automatically.\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"suggested-workflow\">Suggested workflow\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#suggested-workflow\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Suggested workflow”\u003C/span>\u003C/a>\u003C/div>\n\u003Col>\n\u003Cli>Commit your work before re-running, so \u003Ccode dir=\"auto\">git status\u003C/code> shows exactly what regeneration changed.\u003C/li>\n\u003Cli>Re-run, review the diff (\u003Ccode dir=\"auto\">git diff\u003C/code>, plus \u003Ccode dir=\"auto\">diff -r\u003C/code> against the \u003Ccode dir=\"auto\">.bak\u003C/code> copies for untracked files like \u003Ccode dir=\"auto\">terraform/\u003C/code> internals).\u003C/li>\n\u003Cli>Run \u003Ccode dir=\"auto\">npx deploy-stack apply\u003C/code> to converge AWS with the new configuration.\u003C/li>\n\u003Cli>Delete the \u003Ccode dir=\"auto\">.bak.<timestamp>\u003C/code> copies once the new infrastructure is verified.\u003C/li>\n\u003C/ol>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"see-also\">See also\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#see-also\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “See also”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>\u003Ca href=\"/deploy-stack/cli/apply/\">apply\u003C/a> for converging AWS after regeneration.\u003C/li>\n\u003Cli>\u003Ca href=\"/deploy-stack/cli/eject/\">eject\u003C/a> for what happens to backups on decoupling.\u003C/li>\n\u003C/ul>\n",{"headings":940,"localImagePaths":954,"remoteImagePaths":955,"frontmatter":956,"imagePaths":958},[941,944,947,950,953],{"depth":22,"slug":942,"text":943},"the-conflict-prompt","The conflict prompt",{"depth":22,"slug":945,"text":946},"backups-stay-local","Backups stay local",{"depth":22,"slug":948,"text":949},"what-regeneration-touches","What regeneration touches",{"depth":22,"slug":951,"text":952},"suggested-workflow","Suggested workflow",{"depth":22,"slug":323,"text":324},[],[],{"title":928,"description":929,"sidebar":957},{"order":932},[],"guides/secrets-management",{"id":959,"data":961,"body":968,"filePath":969,"digest":970,"rendered":971},{"title":962,"description":963,"editUrl":8,"head":964,"template":10,"sidebar":965,"pagefind":8,"draft":12},"Secrets Management in deploy-stack","Sync .env files to AWS Secrets Manager without committing plaintext secrets.",[],{"order":966,"hidden":12,"attrs":967},7,{},"Managing `.env` files across a team and syncing them to the cloud is a notorious pain point. `deploy-stack` solves this by natively integrating with **AWS Secrets Manager**, ensuring zero plaintext secrets ever touch your GitHub repository or CI/CD pipelines.\n\n## The Secrets Lifecycle\n\nTo maintain zero-secret Git repositories and safe infrastructure provisioning, secrets follow a strict 4-step lifecycle:\n\n```text\n1. Scaffold ───▶ 2. Provision Vault ───▶ 3. Push Secrets ───▶ 4. Deploy to App\n(deploy-stack) (deploy-stack apply) (secrets push .env) (git push)\nGenerates Terraform Creates empty vault Uploads encrypted keys ECS container boots\n& secret_keys.json in AWS Secrets Mgr & updates secret_keys with injected env\n```\n\n---\n\n### Step 1: Provision the Vault (Day 1)\nYour Secrets Manager vault is declared in `terraform/secrets.tf`. Provision the base infrastructure first:\n\n```bash\nnpx deploy-stack apply\n```\n*This creates an empty, secure secret vault named `\u003Cproject-name>-secrets` in your AWS account.*\n\n### Step 2: Push Secrets to AWS\nOnce the vault exists, push your local `.env` values directly to AWS:\n\n```bash\nnpx deploy-stack secrets push .env\n```\n\n**What happens under the hood?**\n1. The CLI reads your local `.env` file.\n2. It encrypts the key-value pairs and pushes them securely into AWS Secrets Manager under your project's namespace (e.g., `my-project-secrets`).\n3. It generates a local `terraform/secret_keys.json` file containing *only the names* of your keys (e.g., `[\"API_KEY\", \"STRIPE_SECRET\"]`), **not the values**.\n\n> 💡 **Tip:** The `secrets push` command takes the file path as the first argument. If you need to use other flags, ensure they are appended at the end of the command:\n> `npx deploy-stack secrets push .env --any-other-flags`\n\n### Step 3: Map Secrets into the Container\nCommit the updated `terraform/secret_keys.json` and push to GitHub:\n\n```bash\ngit add terraform/secret_keys.json\ngit commit -m \"chore: map new secrets to ECS\"\ngit push origin main\n```\n\nTerraform reads `secret_keys.json` during the GitHub Actions deployment and maps each key directly into your ECS Task Definition. When your Fargate container boots up, AWS injects the secret values into `process.env` (Node) or `os.environ` (Python) in memory.\n\n> ⚠️ **Commit this file.** `secret_keys.json` holds key *names* only — never values — so it is safe for version control, and deployment depends on it.\n\n---\n\n### Day-2: Pull, Audit, and Rotate (no redeploy)\n\nSecrets don't stand still — teammates join, keys rotate, local `.env` files get lost. Two commands close the loop:\n\n```bash\nnpx deploy-stack secrets pull # merge remote values into local .env\nnpx deploy-stack secrets audit # diff local .env vs AWS, change nothing\n```\n\n`pull` appends missing remote keys after your existing entries, keeps local-only variables, and asks before overwriting conflicting values (automatic in `--headless` mode). `audit` prints a colored drift report: `+` missing locally, `~` mismatched values, `-` never pushed to AWS.\n\n**Which flow do I need?**\n\n| Situation | Command |\n|---|---|\n| New variable name added/removed | `secrets push`, then commit `secret_keys.json` + `git push` (task definition must be rebuilt) |\n| Only a value changed (same keys) | `secrets push`, then accept the rolling ECS restart prompt — live in seconds, no redeploy |\n| New machine / lost `.env` | `secrets pull` |\n| \"Why doesn't my app see the new value?\" | `secrets audit` first, then push or restart accordingly |\n\nSee the [secrets CLI reference](/deploy-stack/cli/secrets/) for flags, merge rules, and prerequisites.","src/content/docs/guides/secrets-management.md","5a25ea5efa6c7fc3",{"html":972,"metadata":973},"\u003Cp>Managing \u003Ccode dir=\"auto\">.env\u003C/code> files across a team and syncing them to the cloud is a notorious pain point. \u003Ccode dir=\"auto\">deploy-stack\u003C/code> solves this by natively integrating with \u003Cstrong>AWS Secrets Manager\u003C/strong>, ensuring zero plaintext secrets ever touch your GitHub repository or CI/CD pipelines.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"the-secrets-lifecycle\">The Secrets Lifecycle\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#the-secrets-lifecycle\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “The Secrets Lifecycle”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>To maintain zero-secret Git repositories and safe infrastructure provisioning, secrets follow a strict 4-step lifecycle:\u003C/p>\n\u003Cdiv class=\"expressive-code\">\u003Clink rel=\"stylesheet\" href=\"/deploy-stack/_astro/ec.w36nc.css\">\u003Cscript type=\"module\" src=\"/deploy-stack/_astro/ec.0vx5m.js\">\u003C/script>\u003Cfigure class=\"frame not-content\">\u003Cfigcaption class=\"header\">\u003C/figcaption>\u003Cpre data-language=\"text\">\u003Ccode>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#d6deeb;--1:#403f53\">1. Scaffold ───▶ 2. Provision Vault ───▶ 3. Push Secrets ───▶ 4. Deploy to App\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#d6deeb;--1:#403f53\">(deploy-stack) (deploy-stack apply) (secrets push .env) (git push)\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#d6deeb;--1:#403f53\">Generates Terraform Creates empty vault Uploads encrypted keys ECS container boots\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#d6deeb;--1:#403f53\">& secret_keys.json in AWS Secrets Mgr & updates secret_keys with injected env\u003C/span>\u003C/div>\u003C/div>\u003C/code>\u003C/pre>\u003Cdiv class=\"copy\">\u003Cdiv aria-live=\"polite\">\u003C/div>\u003Cbutton title=\"Copy to clipboard\" data-copied=\"Copied!\" data-code=\"1. Scaffold ───▶ 2. Provision Vault ───▶ 3. Push Secrets ───▶ 4. Deploy to App(deploy-stack) (deploy-stack apply) (secrets push .env) (git push)Generates Terraform Creates empty vault Uploads encrypted keys ECS container boots& secret_keys.json in AWS Secrets Mgr & updates secret_keys with injected env\">\u003Cdiv>\u003C/div>\u003C/button>\u003C/div>\u003C/figure>\u003C/div>\n\u003Chr>\n\u003Cdiv class=\"sl-heading-wrapper level-h3\">\u003Ch3 id=\"step-1-provision-the-vault-day-1\">Step 1: Provision the Vault (Day 1)\u003C/h3>\u003Ca class=\"sl-anchor-link\" href=\"#step-1-provision-the-vault-day-1\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Step 1: Provision the Vault (Day 1)”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>Your Secrets Manager vault is declared in \u003Ccode dir=\"auto\">terraform/secrets.tf\u003C/code>. Provision the base infrastructure first:\u003C/p>\n\u003Cdiv class=\"expressive-code\">\u003Cfigure class=\"frame is-terminal not-content\">\u003Cfigcaption class=\"header\">\u003Cspan class=\"title\">\u003C/span>\u003Cspan class=\"sr-only\">Terminal window\u003C/span>\u003C/figcaption>\u003Cpre data-language=\"bash\">\u003Ccode>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">npx\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">deploy-stack\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">apply\u003C/span>\u003C/div>\u003C/div>\u003C/code>\u003C/pre>\u003Cdiv class=\"copy\">\u003Cdiv aria-live=\"polite\">\u003C/div>\u003Cbutton title=\"Copy to clipboard\" data-copied=\"Copied!\" data-code=\"npx deploy-stack apply\">\u003Cdiv>\u003C/div>\u003C/button>\u003C/div>\u003C/figure>\u003C/div>\n\u003Cp>\u003Cem>This creates an empty, secure secret vault named \u003Ccode dir=\"auto\"><project-name>-secrets\u003C/code> in your AWS account.\u003C/em>\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h3\">\u003Ch3 id=\"step-2-push-secrets-to-aws\">Step 2: Push Secrets to AWS\u003C/h3>\u003Ca class=\"sl-anchor-link\" href=\"#step-2-push-secrets-to-aws\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Step 2: Push Secrets to AWS”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>Once the vault exists, push your local \u003Ccode dir=\"auto\">.env\u003C/code> values directly to AWS:\u003C/p>\n\u003Cdiv class=\"expressive-code\">\u003Cfigure class=\"frame is-terminal not-content\">\u003Cfigcaption class=\"header\">\u003Cspan class=\"title\">\u003C/span>\u003Cspan class=\"sr-only\">Terminal window\u003C/span>\u003C/figcaption>\u003Cpre data-language=\"bash\">\u003Ccode>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">npx\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">deploy-stack\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">secrets\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">push\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">.env\u003C/span>\u003C/div>\u003C/div>\u003C/code>\u003C/pre>\u003Cdiv class=\"copy\">\u003Cdiv aria-live=\"polite\">\u003C/div>\u003Cbutton title=\"Copy to clipboard\" data-copied=\"Copied!\" data-code=\"npx deploy-stack secrets push .env\">\u003Cdiv>\u003C/div>\u003C/button>\u003C/div>\u003C/figure>\u003C/div>\n\u003Cp>\u003Cstrong>What happens under the hood?\u003C/strong>\u003C/p>\n\u003Col>\n\u003Cli>The CLI reads your local \u003Ccode dir=\"auto\">.env\u003C/code> file.\u003C/li>\n\u003Cli>It encrypts the key-value pairs and pushes them securely into AWS Secrets Manager under your project’s namespace (e.g., \u003Ccode dir=\"auto\">my-project-secrets\u003C/code>).\u003C/li>\n\u003Cli>It generates a local \u003Ccode dir=\"auto\">terraform/secret_keys.json\u003C/code> file containing \u003Cem>only the names\u003C/em> of your keys (e.g., \u003Ccode dir=\"auto\">[\"API_KEY\", \"STRIPE_SECRET\"]\u003C/code>), \u003Cstrong>not the values\u003C/strong>.\u003C/li>\n\u003C/ol>\n\u003Cblockquote>\n\u003Cp>💡 \u003Cstrong>Tip:\u003C/strong> The \u003Ccode dir=\"auto\">secrets push\u003C/code> command takes the file path as the first argument. If you need to use other flags, ensure they are appended at the end of the command:\n\u003Ccode dir=\"auto\">npx deploy-stack secrets push .env --any-other-flags\u003C/code>\u003C/p>\n\u003C/blockquote>\n\u003Cdiv class=\"sl-heading-wrapper level-h3\">\u003Ch3 id=\"step-3-map-secrets-into-the-container\">Step 3: Map Secrets into the Container\u003C/h3>\u003Ca class=\"sl-anchor-link\" href=\"#step-3-map-secrets-into-the-container\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Step 3: Map Secrets into the Container”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>Commit the updated \u003Ccode dir=\"auto\">terraform/secret_keys.json\u003C/code> and push to GitHub:\u003C/p>\n\u003Cdiv class=\"expressive-code\">\u003Cfigure class=\"frame is-terminal not-content\">\u003Cfigcaption class=\"header\">\u003Cspan class=\"title\">\u003C/span>\u003Cspan class=\"sr-only\">Terminal window\u003C/span>\u003C/figcaption>\u003Cpre data-language=\"bash\">\u003Ccode>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">git\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">add\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">terraform/secret_keys.json\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">git\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">commit\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">-m\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#D9F5DD;--1:#111111\">\"\u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#984E4D\">chore: map new secrets to ECS\u003C/span>\u003Cspan style=\"--0:#D9F5DD;--1:#111111\">\"\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">git\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">push\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">origin\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">main\u003C/span>\u003C/div>\u003C/div>\u003C/code>\u003C/pre>\u003Cdiv class=\"copy\">\u003Cdiv aria-live=\"polite\">\u003C/div>\u003Cbutton title=\"Copy to clipboard\" data-copied=\"Copied!\" data-code=\"git add terraform/secret_keys.jsongit commit -m "chore: map new secrets to ECS"git push origin main\">\u003Cdiv>\u003C/div>\u003C/button>\u003C/div>\u003C/figure>\u003C/div>\n\u003Cp>Terraform reads \u003Ccode dir=\"auto\">secret_keys.json\u003C/code> during the GitHub Actions deployment and maps each key directly into your ECS Task Definition. When your Fargate container boots up, AWS injects the secret values into \u003Ccode dir=\"auto\">process.env\u003C/code> (Node) or \u003Ccode dir=\"auto\">os.environ\u003C/code> (Python) in memory.\u003C/p>\n\u003Cblockquote>\n\u003Cp>⚠️ \u003Cstrong>Commit this file.\u003C/strong> \u003Ccode dir=\"auto\">secret_keys.json\u003C/code> holds key \u003Cem>names\u003C/em> only — never values — so it is safe for version control, and deployment depends on it.\u003C/p>\n\u003C/blockquote>\n\u003Chr>\n\u003Cdiv class=\"sl-heading-wrapper level-h3\">\u003Ch3 id=\"day-2-pull-audit-and-rotate-no-redeploy\">Day-2: Pull, Audit, and Rotate (no redeploy)\u003C/h3>\u003Ca class=\"sl-anchor-link\" href=\"#day-2-pull-audit-and-rotate-no-redeploy\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Day-2: Pull, Audit, and Rotate (no redeploy)”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>Secrets don’t stand still — teammates join, keys rotate, local \u003Ccode dir=\"auto\">.env\u003C/code> files get lost. Two commands close the loop:\u003C/p>\n\u003Cdiv class=\"expressive-code\">\u003Cfigure class=\"frame is-terminal not-content\">\u003Cfigcaption class=\"header\">\u003Cspan class=\"title\">\u003C/span>\u003Cspan class=\"sr-only\">Terminal window\u003C/span>\u003C/figcaption>\u003Cpre data-language=\"bash\">\u003Ccode>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">npx\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">deploy-stack\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">secrets\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">pull\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#919F9F;--1:#5F636F\"># merge remote values into local .env\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">npx\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">deploy-stack\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">secrets\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">audit\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#919F9F;--1:#5F636F\"># diff local .env vs AWS, change nothing\u003C/span>\u003C/div>\u003C/div>\u003C/code>\u003C/pre>\u003Cdiv class=\"copy\">\u003Cdiv aria-live=\"polite\">\u003C/div>\u003Cbutton title=\"Copy to clipboard\" data-copied=\"Copied!\" data-code=\"npx deploy-stack secrets pull # merge remote values into local .envnpx deploy-stack secrets audit # diff local .env vs AWS, change nothing\">\u003Cdiv>\u003C/div>\u003C/button>\u003C/div>\u003C/figure>\u003C/div>\n\u003Cp>\u003Ccode dir=\"auto\">pull\u003C/code> appends missing remote keys after your existing entries, keeps local-only variables, and asks before overwriting conflicting values (automatic in \u003Ccode dir=\"auto\">--headless\u003C/code> mode). \u003Ccode dir=\"auto\">audit\u003C/code> prints a colored drift report: \u003Ccode dir=\"auto\">+\u003C/code> missing locally, \u003Ccode dir=\"auto\">~\u003C/code> mismatched values, \u003Ccode dir=\"auto\">-\u003C/code> never pushed to AWS.\u003C/p>\n\u003Cp>\u003Cstrong>Which flow do I need?\u003C/strong>\u003C/p>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>Situation\u003C/th>\n\u003Cth>Command\u003C/th>\n\u003C/tr>\n\u003C/thead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>New variable name added/removed\u003C/td>\n\u003Ctd>\u003Ccode dir=\"auto\">secrets push\u003C/code>, then commit \u003Ccode dir=\"auto\">secret_keys.json\u003C/code> + \u003Ccode dir=\"auto\">git push\u003C/code> (task definition must be rebuilt)\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>Only a value changed (same keys)\u003C/td>\n\u003Ctd>\u003Ccode dir=\"auto\">secrets push\u003C/code>, then accept the rolling ECS restart prompt — live in seconds, no redeploy\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>New machine / lost \u003Ccode dir=\"auto\">.env\u003C/code>\u003C/td>\n\u003Ctd>\u003Ccode dir=\"auto\">secrets pull\u003C/code>\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>“Why doesn’t my app see the new value?”\u003C/td>\n\u003Ctd>\u003Ccode dir=\"auto\">secrets audit\u003C/code> first, then push or restart accordingly\u003C/td>\n\u003C/tr>\n\u003C/tbody>\n\u003C/table>\n\u003Cp>See the \u003Ca href=\"/deploy-stack/cli/secrets/\">secrets CLI reference\u003C/a> for flags, merge rules, and prerequisites.\u003C/p>\n",{"headings":974,"localImagePaths":990,"remoteImagePaths":991,"frontmatter":992,"imagePaths":994},[975,978,981,984,987],{"depth":22,"slug":976,"text":977},"the-secrets-lifecycle","The Secrets Lifecycle",{"depth":35,"slug":979,"text":980},"step-1-provision-the-vault-day-1","Step 1: Provision the Vault (Day 1)",{"depth":35,"slug":982,"text":983},"step-2-push-secrets-to-aws","Step 2: Push Secrets to AWS",{"depth":35,"slug":985,"text":986},"step-3-map-secrets-into-the-container","Step 3: Map Secrets into the Container",{"depth":35,"slug":988,"text":989},"day-2-pull-audit-and-rotate-no-redeploy","Day-2: Pull, Audit, and Rotate (no redeploy)",[],[],{"title":962,"description":963,"sidebar":993},{"order":966},[],"index",{"id":995,"data":997,"body":1007,"filePath":1008,"digest":1009,"deferredRender":8},{"title":998,"description":999,"editUrl":8,"head":1000,"tableOfContents":12,"template":1004,"sidebar":1005,"pagefind":8,"draft":12},"deploy-stack docs","Official developer portal for deploy-stack — concepts, guides, and CLI reference.",[1001],{"tag":1002,"content":1003},"style","/* Landing page: compact two-column hero, everything above the fold. */\n#_top { display: none; }\n.landing-hero { padding: 1.5rem 0; }\n\n.sl-markdown-content .landing-grid {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 2rem;\n align-items: start;\n margin: 0;\n padding: 0;\n}\n\n/* Brute-force the top alignment to ignore Starlight's markdown margins */\n.sl-markdown-content h1.landing-title,\n.sl-markdown-content p.landing-intro {\n margin-top: 0 !important;\n}\n\n.sl-markdown-content h1.landing-title { line-height: 1.1; }\n.sl-markdown-content .landing-tagline { margin-top: 1rem !important; }\n.landing-actions { display: flex; gap: 0.75rem; flex-wrap: wrap; margin-top: 1.5rem; }\n\n.sl-markdown-content .landing-cards {\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n gap: 1.5rem;\n align-items: stretch;\n margin-top: 2rem;\n padding: 0;\n}\n\n/* Force height propagation through any hidden MDX wrappers */\n.sl-markdown-content .landing-cards > * {\n margin: 0 !important;\n height: 100% !important;\n}\n\n.sl-markdown-content .landing-cards .sl-link-card,\n.sl-markdown-content .landing-cards .sl-link-card a {\n height: 100% !important;\n display: flex;\n flex-direction: column;\n}\n\n.sl-markdown-content .landing-footnote {\n margin-top: 2rem;\n font-size: var(--sl-text-sm);\n color: var(--sl-color-gray-2);\n}\n\n@media (max-width: 50rem) {\n .sl-markdown-content .landing-grid { grid-template-columns: 1fr; }\n .sl-markdown-content .landing-cards { grid-template-columns: 1fr; }\n}\n","splash",{"hidden":12,"attrs":1006},{},"import { LinkCard, LinkButton, Code } from '@astrojs/starlight/components';\n\n\u003Cdiv class=\"landing-hero\">\n \u003Cdiv class=\"landing-grid\">\n \u003Cdiv>\n \u003Ch1 class=\"landing-title\">deploy-stack docs\u003C/h1>\n \u003Cp class=\"landing-tagline\">Provision production-ready AWS infrastructure and CI/CD pipelines in seconds.\u003C/p>\n \u003Cdiv class=\"landing-actions\">\n \u003CLinkButton href=\"/deploy-stack/guides/quickstart/\">Get started in 5 minutes\u003C/LinkButton>\n \u003CLinkButton href=\"/deploy-stack/cli/init/\" variant=\"minimal\">CLI reference\u003C/LinkButton>\n \u003C/div>\n \u003C/div>\n \u003Cdiv>\n \u003Cp class=\"landing-intro\">`deploy-stack` turns your repo into a production-ready AWS deployment — Terraform, Docker, and CI/CD generated from your code:\u003C/p>\n \u003CCode code={`npx deploy-stack # scaffold Terraform, Dockerfile, workflows\\nnpx deploy-stack apply # provision the ALB, cluster, and service\\ngit push # build, scan, and roll out your image`} lang=\"bash\" />\n \u003C/div>\n \u003C/div>\n\u003C/div>\n\n\u003Cdiv class=\"landing-cards\">\n \u003CLinkCard\n title=\"Deploying for the first time\"\n description=\"Follow the 5-minute Quickstart, then the CI/CD pipeline and first deploy.\"\n href=\"/deploy-stack/guides/quickstart/\"\n />\n \u003CLinkCard\n title=\"Migrating from Vercel or Heroku\"\n description=\"Next.js, Astro, SvelteKit, and Procfile guides for moving to AWS Fargate.\"\n href=\"/deploy-stack/migrations/nextjs-vercel-to-aws/\"\n />\n \u003CLinkCard\n title=\"Looking up a command\"\n description=\"Full CLI reference starting with npx deploy-stack (init).\"\n href=\"/deploy-stack/cli/init/\"\n />\n\u003C/div>\n\n\u003Cp class=\"landing-footnote\">Supports NestJS, Next.js, Nuxt, Express, SvelteKit, Astro, FastAPI, Django, Rails, and Go — see \u003Ca href=\"/deploy-stack/guides/frameworks/\">Supported Frameworks\u003C/a> · \u003Ca href=\"/deploy-stack/guides/examples/\">Examples\u003C/a> · \u003Ca href=\"/deploy-stack/adrs/0001-s3-native-state-locking/\">Architecture Decisions\u003C/a>\u003C/p>","src/content/docs/index.mdx","0dff69969de3e5d4","migrations/astro-vercel-to-aws",{"id":1010,"data":1012,"body":1018,"filePath":1019,"digest":1020,"rendered":1021},{"title":1013,"description":1014,"editUrl":8,"head":1015,"template":10,"sidebar":1016,"pagefind":8,"draft":12},"Migrating Astro from Vercel to AWS Fargate","Switch the Astro adapter to Node standalone to leave Vercel for AWS Fargate.",[],{"hidden":12,"attrs":1017},{},"If you are seeing a warning from `deploy-stack` about your Astro adapter, it means your project is currently configured to build specifically for Vercel's proprietary serverless network. \n\nTo deploy Astro as a containerized application on standard AWS infrastructure, you simply need to switch to Astro's official Node.js adapter.\n\n## How to Fix\n\n### 1. Install the Node adapter\nRun the following command in your terminal to swap out the Vercel adapter for the Node adapter:\n\n\\`\\`\\`bash\nnpm install @astrojs/node\nnpm uninstall @astrojs/vercel\n\\`\\`\\`\n\n### 2. Update `astro.config.mjs`\nOpen your Astro configuration file and replace the Vercel import with the Node import.\n\n**Before (Vercel Lock-in):**\n\\`\\`\\`javascript\nimport { defineConfig } from 'astro/config';\nimport vercel from '@astrojs/vercel/serverless';\n\nexport default defineConfig({\n output: 'server',\n adapter: vercel(),\n});\n\\`\\`\\`\n\n**After (AWS Ready):**\n\\`\\`\\`javascript\nimport { defineConfig } from 'astro/config';\nimport node from '@astrojs/node';\n\nexport default defineConfig({\n output: 'server',\n adapter: node({\n mode: 'standalone'\n }),\n});\n\\`\\`\\`\n\n### 3. Deploy\nThat's it! Your Astro app is now decoupled from Vercel. \n\nRun `npx deploy-stack apply` and the CLI will automatically package this standalone Node server into a hardened Docker container and deploy it to your AWS cluster.\n\n## Next steps\n\n- [CI/CD Pipeline & First Deploy](/deploy-stack/guides/cicd-pipeline/) for what happens on `git push`.\n- [Supported Frameworks](/deploy-stack/guides/frameworks/) for Astro build requirements.","src/content/docs/migrations/astro-vercel-to-aws.md","c2e127d9cadfcb82",{"html":1022,"metadata":1023},"\u003Cp>If you are seeing a warning from \u003Ccode dir=\"auto\">deploy-stack\u003C/code> about your Astro adapter, it means your project is currently configured to build specifically for Vercel’s proprietary serverless network.\u003C/p>\n\u003Cp>To deploy Astro as a containerized application on standard AWS infrastructure, you simply need to switch to Astro’s official Node.js adapter.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"how-to-fix\">How to Fix\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#how-to-fix\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “How to Fix”\u003C/span>\u003C/a>\u003C/div>\n\u003Cdiv class=\"sl-heading-wrapper level-h3\">\u003Ch3 id=\"1-install-the-node-adapter\">1. Install the Node adapter\u003C/h3>\u003Ca class=\"sl-anchor-link\" href=\"#1-install-the-node-adapter\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “1. Install the Node adapter”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>Run the following command in your terminal to swap out the Vercel adapter for the Node adapter:\u003C/p>\n\u003Cp>```bash\nnpm install @astrojs/node\nnpm uninstall @astrojs/vercel\n```\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h3\">\u003Ch3 id=\"2-update-astroconfigmjs\">2. Update \u003Ccode dir=\"auto\">astro.config.mjs\u003C/code>\u003C/h3>\u003Ca class=\"sl-anchor-link\" href=\"#2-update-astroconfigmjs\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “2. Update astro.config.mjs”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>Open your Astro configuration file and replace the Vercel import with the Node import.\u003C/p>\n\u003Cp>\u003Cstrong>Before (Vercel Lock-in):\u003C/strong>\n```javascript\nimport { defineConfig } from ‘astro/config’;\nimport vercel from ‘@astrojs/vercel/serverless’;\u003C/p>\n\u003Cp>export default defineConfig({\noutput: ‘server’,\nadapter: vercel(),\n});\n```\u003C/p>\n\u003Cp>\u003Cstrong>After (AWS Ready):\u003C/strong>\n```javascript\nimport { defineConfig } from ‘astro/config’;\nimport node from ‘@astrojs/node’;\u003C/p>\n\u003Cp>export default defineConfig({\noutput: ‘server’,\nadapter: node({\nmode: ‘standalone’\n}),\n});\n```\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h3\">\u003Ch3 id=\"3-deploy\">3. Deploy\u003C/h3>\u003Ca class=\"sl-anchor-link\" href=\"#3-deploy\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “3. Deploy”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>That’s it! Your Astro app is now decoupled from Vercel.\u003C/p>\n\u003Cp>Run \u003Ccode dir=\"auto\">npx deploy-stack apply\u003C/code> and the CLI will automatically package this standalone Node server into a hardened Docker container and deploy it to your AWS cluster.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"next-steps\">Next steps\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#next-steps\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Next steps”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>\u003Ca href=\"/deploy-stack/guides/cicd-pipeline/\">CI/CD Pipeline & First Deploy\u003C/a> for what happens on \u003Ccode dir=\"auto\">git push\u003C/code>.\u003C/li>\n\u003Cli>\u003Ca href=\"/deploy-stack/guides/frameworks/\">Supported Frameworks\u003C/a> for Astro build requirements.\u003C/li>\n\u003C/ul>\n",{"headings":1024,"localImagePaths":1038,"remoteImagePaths":1039,"frontmatter":1040,"imagePaths":1041},[1025,1028,1031,1034,1037],{"depth":22,"slug":1026,"text":1027},"how-to-fix","How to Fix",{"depth":35,"slug":1029,"text":1030},"1-install-the-node-adapter","1. Install the Node adapter",{"depth":35,"slug":1032,"text":1033},"2-update-astroconfigmjs","2. Update astro.config.mjs",{"depth":35,"slug":1035,"text":1036},"3-deploy","3. Deploy",{"depth":22,"slug":918,"text":919},[],[],{"title":1013,"description":1014},[],"migrations/heroku-procfile-to-aws",{"id":1042,"data":1044,"body":1050,"filePath":1051,"digest":1052,"rendered":1053},{"title":1045,"description":1046,"editUrl":8,"head":1047,"template":10,"sidebar":1048,"pagefind":8,"draft":12},"Migrating from Heroku to AWS (Procfile Support)","Map Heroku Procfile web and worker processes to AWS ECS Fargate services.",[],{"hidden":12,"attrs":1049},{},"When migrating from Heroku or Render, you likely rely on a `Procfile` to define your application's architecture (e.g., a web server and a background worker like Celery or Sidekiq). \n\n`deploy-stack` natively understands Heroku `Procfile` syntax and automatically translates it into a production-grade, multi-container AWS architecture.\n\n## How it Works\n\nWhen you run `npx deploy-stack`, the CLI scans your root directory for a `Procfile`. \n\n### The `web` Process\nIf the CLI detects a `web:` declaration:\n1. It overrides the default Docker `CMD`.\n2. It provisions an AWS ECS Fargate service for this process.\n3. It automatically wires this specific container to your public-facing Application Load Balancer (ALB) so it can receive internet traffic.\n\n### The `worker` Process\nIf the CLI detects a `worker:` declaration:\n1. It generates a completely separate ECS Fargate task definition (`worker.tf`).\n2. It spins up the worker in a **fully isolated private subnet**.\n3. It intentionally strips all public ingress, ensuring your background workers are secure and can only communicate with your database or message brokers internally.\n\n## Example\n\n**Your `Procfile`:**\n```text\nweb: gunicorn myapp.wsgi\nworker: celery -A myapp worker -l info\n```\n\n**The Result:**\nRunning `deploy-stack` will automatically generate the Terraform required to spin up both containers simultaneously from the exact same Docker image, scaling them independently based on your needs.\n\n## Next steps\n\n- [CI/CD Pipeline & First Deploy](/deploy-stack/guides/cicd-pipeline/) for what happens on `git push`.\n- [Supported Frameworks](/deploy-stack/guides/frameworks/) for Procfile and framework detection.","src/content/docs/migrations/heroku-procfile-to-aws.md","e7287c93423a8a0b",{"html":1054,"metadata":1055},"\u003Cp>When migrating from Heroku or Render, you likely rely on a \u003Ccode dir=\"auto\">Procfile\u003C/code> to define your application’s architecture (e.g., a web server and a background worker like Celery or Sidekiq).\u003C/p>\n\u003Cp>\u003Ccode dir=\"auto\">deploy-stack\u003C/code> natively understands Heroku \u003Ccode dir=\"auto\">Procfile\u003C/code> syntax and automatically translates it into a production-grade, multi-container AWS architecture.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"how-it-works\">How it Works\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#how-it-works\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “How it Works”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>When you run \u003Ccode dir=\"auto\">npx deploy-stack\u003C/code>, the CLI scans your root directory for a \u003Ccode dir=\"auto\">Procfile\u003C/code>.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h3\">\u003Ch3 id=\"the-web-process\">The \u003Ccode dir=\"auto\">web\u003C/code> Process\u003C/h3>\u003Ca class=\"sl-anchor-link\" href=\"#the-web-process\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “The web Process”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>If the CLI detects a \u003Ccode dir=\"auto\">web:\u003C/code> declaration:\u003C/p>\n\u003Col>\n\u003Cli>It overrides the default Docker \u003Ccode dir=\"auto\">CMD\u003C/code>.\u003C/li>\n\u003Cli>It provisions an AWS ECS Fargate service for this process.\u003C/li>\n\u003Cli>It automatically wires this specific container to your public-facing Application Load Balancer (ALB) so it can receive internet traffic.\u003C/li>\n\u003C/ol>\n\u003Cdiv class=\"sl-heading-wrapper level-h3\">\u003Ch3 id=\"the-worker-process\">The \u003Ccode dir=\"auto\">worker\u003C/code> Process\u003C/h3>\u003Ca class=\"sl-anchor-link\" href=\"#the-worker-process\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “The worker Process”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>If the CLI detects a \u003Ccode dir=\"auto\">worker:\u003C/code> declaration:\u003C/p>\n\u003Col>\n\u003Cli>It generates a completely separate ECS Fargate task definition (\u003Ccode dir=\"auto\">worker.tf\u003C/code>).\u003C/li>\n\u003Cli>It spins up the worker in a \u003Cstrong>fully isolated private subnet\u003C/strong>.\u003C/li>\n\u003Cli>It intentionally strips all public ingress, ensuring your background workers are secure and can only communicate with your database or message brokers internally.\u003C/li>\n\u003C/ol>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"example\">Example\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#example\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Example”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>\u003Cstrong>Your \u003Ccode dir=\"auto\">Procfile\u003C/code>:\u003C/strong>\u003C/p>\n\u003Cdiv class=\"expressive-code\">\u003Clink rel=\"stylesheet\" href=\"/deploy-stack/_astro/ec.w36nc.css\">\u003Cscript type=\"module\" src=\"/deploy-stack/_astro/ec.0vx5m.js\">\u003C/script>\u003Cfigure class=\"frame not-content\">\u003Cfigcaption class=\"header\">\u003C/figcaption>\u003Cpre data-language=\"text\">\u003Ccode>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#d6deeb;--1:#403f53\">web: gunicorn myapp.wsgi\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#d6deeb;--1:#403f53\">worker: celery -A myapp worker -l info\u003C/span>\u003C/div>\u003C/div>\u003C/code>\u003C/pre>\u003Cdiv class=\"copy\">\u003Cdiv aria-live=\"polite\">\u003C/div>\u003Cbutton title=\"Copy to clipboard\" data-copied=\"Copied!\" data-code=\"web: gunicorn myapp.wsgiworker: celery -A myapp worker -l info\">\u003Cdiv>\u003C/div>\u003C/button>\u003C/div>\u003C/figure>\u003C/div>\n\u003Cp>\u003Cstrong>The Result:\u003C/strong>\nRunning \u003Ccode dir=\"auto\">deploy-stack\u003C/code> will automatically generate the Terraform required to spin up both containers simultaneously from the exact same Docker image, scaling them independently based on your needs.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"next-steps\">Next steps\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#next-steps\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Next steps”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>\u003Ca href=\"/deploy-stack/guides/cicd-pipeline/\">CI/CD Pipeline & First Deploy\u003C/a> for what happens on \u003Ccode dir=\"auto\">git push\u003C/code>.\u003C/li>\n\u003Cli>\u003Ca href=\"/deploy-stack/guides/frameworks/\">Supported Frameworks\u003C/a> for Procfile and framework detection.\u003C/li>\n\u003C/ul>\n",{"headings":1056,"localImagePaths":1070,"remoteImagePaths":1071,"frontmatter":1072,"imagePaths":1073},[1057,1060,1063,1066,1069],{"depth":22,"slug":1058,"text":1059},"how-it-works","How it Works",{"depth":35,"slug":1061,"text":1062},"the-web-process","The web Process",{"depth":35,"slug":1064,"text":1065},"the-worker-process","The worker Process",{"depth":22,"slug":1067,"text":1068},"example","Example",{"depth":22,"slug":918,"text":919},[],[],{"title":1045,"description":1046},[],"migrations/nextjs-vercel-to-aws",{"id":1074,"data":1076,"body":1082,"filePath":1083,"digest":1084,"rendered":1085},{"title":1077,"description":1078,"editUrl":8,"head":1079,"template":10,"sidebar":1080,"pagefind":8,"draft":12},"Migrating Next.js from Vercel to AWS Fargate","Add output: 'standalone' to run Next.js in a lean AWS Fargate container.",[],{"hidden":12,"attrs":1081},{},"If you are seeing a warning from `deploy-stack` about `output: 'standalone'`, your Next.js configuration is missing a crucial setting required for containerized environments.\n\nBy default, Next.js requires your entire `node_modules` folder to run the production server. This creates massive, bloated Docker containers that boot slowly and cost more to host. The `standalone` output mode tells Next.js to trace your code and bundle *only* the specific files and dependencies actually used in production, creating an ultra-lean deployment artifact.\n\n## How to Fix\n\n### 1. Update `next.config.js` (or `.mjs` / `.cjs`)\nOpen your Next.js configuration file in the root of your project and add `output: 'standalone'` to the configuration object.\n\n**Before (Vercel Default):**\n```javascript\n/** @type {import('next').NextConfig} */\nconst nextConfig = {\n reactStrictMode: true,\n // Other existing config...\n};\n\nexport default nextConfig;\n```\n\n**After (AWS Ready):**\n```javascript\n/** @type {import('next').NextConfig} */\nconst nextConfig = {\n reactStrictMode: true,\n output: 'standalone', // \u003C-- Add this line\n // Other existing config...\n};\n\nexport default nextConfig;\n```\n\n### 2. (Optional) Define a Health Check Route\nAWS Application Load Balancers require a route to ping to ensure your app is healthy. If you don't already have one, create a simple API route in your app (e.g., `app/api/health/route.ts` for App Router, or `pages/api/health.ts` for Pages Router) that returns a `200 OK` status.\n\nWhen running `deploy-stack`, choose **Advanced Configuration** and set your ALB Health Check Path to this route (e.g., `/api/health`).\n\n### 3. Deploy\nYour Next.js app is now perfectly optimized for AWS ECS Fargate! \n\nRun `npx deploy-stack apply`. The CLI's generated `Dockerfile` will automatically target your new `.next/standalone` directory and deploy the optimized build to the cloud.\n\n## Next steps\n\n- [CI/CD Pipeline & First Deploy](/deploy-stack/guides/cicd-pipeline/) for what happens on `git push`.\n- [Supported Frameworks](/deploy-stack/guides/frameworks/) for Next.js requirements.","src/content/docs/migrations/nextjs-vercel-to-aws.md","2810e90374a6f154",{"html":1086,"metadata":1087},"\u003Cp>If you are seeing a warning from \u003Ccode dir=\"auto\">deploy-stack\u003C/code> about \u003Ccode dir=\"auto\">output: 'standalone'\u003C/code>, your Next.js configuration is missing a crucial setting required for containerized environments.\u003C/p>\n\u003Cp>By default, Next.js requires your entire \u003Ccode dir=\"auto\">node_modules\u003C/code> folder to run the production server. This creates massive, bloated Docker containers that boot slowly and cost more to host. The \u003Ccode dir=\"auto\">standalone\u003C/code> output mode tells Next.js to trace your code and bundle \u003Cem>only\u003C/em> the specific files and dependencies actually used in production, creating an ultra-lean deployment artifact.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"how-to-fix\">How to Fix\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#how-to-fix\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “How to Fix”\u003C/span>\u003C/a>\u003C/div>\n\u003Cdiv class=\"sl-heading-wrapper level-h3\">\u003Ch3 id=\"1-update-nextconfigjs-or-mjs--cjs\">1. Update \u003Ccode dir=\"auto\">next.config.js\u003C/code> (or \u003Ccode dir=\"auto\">.mjs\u003C/code> / \u003Ccode dir=\"auto\">.cjs\u003C/code>)\u003C/h3>\u003Ca class=\"sl-anchor-link\" href=\"#1-update-nextconfigjs-or-mjs--cjs\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “1. Update next.config.js (or .mjs / .cjs)”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>Open your Next.js configuration file in the root of your project and add \u003Ccode dir=\"auto\">output: 'standalone'\u003C/code> to the configuration object.\u003C/p>\n\u003Cp>\u003Cstrong>Before (Vercel Default):\u003C/strong>\u003C/p>\n\u003Cdiv class=\"expressive-code\">\u003Clink rel=\"stylesheet\" href=\"/deploy-stack/_astro/ec.w36nc.css\">\u003Cscript type=\"module\" src=\"/deploy-stack/_astro/ec.0vx5m.js\">\u003C/script>\u003Cfigure class=\"frame not-content\">\u003Cfigcaption class=\"header\">\u003C/figcaption>\u003Cpre data-language=\"javascript\">\u003Ccode>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#919F9F;--1:#5F636F\">/** \u003C/span>\u003Cspan style=\"--0:#C792EA;--1:#8844AE\">@type\u003C/span>\u003Cspan style=\"--0:#919F9F;--1:#5F636F\"> \u003C/span>\u003Cspan style=\"--0:#889FB2;--1:#4D667B\">{import('next').NextConfig}\u003C/span>\u003Cspan style=\"--0:#919F9F;--1:#5F636F\"> */\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#C792EA;--1:#8844AE\">const \u003C/span>\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">nextConfig\u003C/span>\u003Cspan style=\"--0:#C792EA;--1:#8844AE\"> = {\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan class=\"indent\">\u003Cspan style=\"--0:#C792EA;--1:#8844AE\"> \u003C/span>\u003C/span>\u003Cspan style=\"--0:#C792EA;--1:#8844AE\">reactStrictMode: \u003C/span>\u003Cspan style=\"--0:#FF6A83;--1:#A24848\">true\u003C/span>\u003Cspan style=\"--0:#C792EA;--1:#8844AE\">,\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan class=\"indent\"> \u003C/span>\u003Cspan style=\"--0:#919F9F;--1:#5D6376\">// Other existing config...\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#C792EA;--1:#8844AE\">}\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\">;\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\n\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#C792EA;--1:#8844AE\">export\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#C792EA;--1:#8844AE\">default\u003C/span>\u003Cspan style=\"--1:#403F53\">\u003Cspan style=\"--0:#D6DEEB\"> \u003C/span>\u003Cspan style=\"--0:#D7DBE0\">nextConfig\u003C/span>\u003Cspan style=\"--0:#D6DEEB\">;\u003C/span>\u003C/span>\u003C/div>\u003C/div>\u003C/code>\u003C/pre>\u003Cdiv class=\"copy\">\u003Cdiv aria-live=\"polite\">\u003C/div>\u003Cbutton title=\"Copy to clipboard\" data-copied=\"Copied!\" data-code=\"/** @type {import('next').NextConfig} */const nextConfig = { reactStrictMode: true, // Other existing config...};export default nextConfig;\">\u003Cdiv>\u003C/div>\u003C/button>\u003C/div>\u003C/figure>\u003C/div>\n\u003Cp>\u003Cstrong>After (AWS Ready):\u003C/strong>\u003C/p>\n\u003Cdiv class=\"expressive-code\">\u003Cfigure class=\"frame not-content\">\u003Cfigcaption class=\"header\">\u003C/figcaption>\u003Cpre data-language=\"javascript\">\u003Ccode>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#919F9F;--1:#5F636F\">/** \u003C/span>\u003Cspan style=\"--0:#C792EA;--1:#8844AE\">@type\u003C/span>\u003Cspan style=\"--0:#919F9F;--1:#5F636F\"> \u003C/span>\u003Cspan style=\"--0:#889FB2;--1:#4D667B\">{import('next').NextConfig}\u003C/span>\u003Cspan style=\"--0:#919F9F;--1:#5F636F\"> */\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#C792EA;--1:#8844AE\">const \u003C/span>\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">nextConfig\u003C/span>\u003Cspan style=\"--0:#C792EA;--1:#8844AE\"> = {\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan class=\"indent\">\u003Cspan style=\"--0:#C792EA;--1:#8844AE\"> \u003C/span>\u003C/span>\u003Cspan style=\"--0:#C792EA;--1:#8844AE\">reactStrictMode: \u003C/span>\u003Cspan style=\"--0:#FF6A83;--1:#A24848\">true\u003C/span>\u003Cspan style=\"--0:#C792EA;--1:#8844AE\">,\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan class=\"indent\">\u003Cspan style=\"--0:#C792EA;--1:#8844AE\"> \u003C/span>\u003C/span>\u003Cspan style=\"--0:#C792EA;--1:#8844AE\">output: \u003C/span>\u003Cspan style=\"--0:#D9F5DD;--1:#111111\">'\u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#984E4D\">standalone\u003C/span>\u003Cspan style=\"--0:#D9F5DD;--1:#111111\">'\u003C/span>\u003Cspan style=\"--0:#C792EA;--1:#8844AE\">, \u003C/span>\u003Cspan style=\"--0:#919F9F;--1:#5D6376\">// <-- Add this line\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan class=\"indent\"> \u003C/span>\u003Cspan style=\"--0:#919F9F;--1:#5D6376\">// Other existing config...\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#C792EA;--1:#8844AE\">}\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\">;\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\n\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#C792EA;--1:#8844AE\">export\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#C792EA;--1:#8844AE\">default\u003C/span>\u003Cspan style=\"--1:#403F53\">\u003Cspan style=\"--0:#D6DEEB\"> \u003C/span>\u003Cspan style=\"--0:#D7DBE0\">nextConfig\u003C/span>\u003Cspan style=\"--0:#D6DEEB\">;\u003C/span>\u003C/span>\u003C/div>\u003C/div>\u003C/code>\u003C/pre>\u003Cdiv class=\"copy\">\u003Cdiv aria-live=\"polite\">\u003C/div>\u003Cbutton title=\"Copy to clipboard\" data-copied=\"Copied!\" data-code=\"/** @type {import('next').NextConfig} */const nextConfig = { reactStrictMode: true, output: 'standalone', // \u003C-- Add this line // Other existing config...};export default nextConfig;\">\u003Cdiv>\u003C/div>\u003C/button>\u003C/div>\u003C/figure>\u003C/div>\n\u003Cdiv class=\"sl-heading-wrapper level-h3\">\u003Ch3 id=\"2-optional-define-a-health-check-route\">2. (Optional) Define a Health Check Route\u003C/h3>\u003Ca class=\"sl-anchor-link\" href=\"#2-optional-define-a-health-check-route\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “2. (Optional) Define a Health Check Route”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>AWS Application Load Balancers require a route to ping to ensure your app is healthy. If you don’t already have one, create a simple API route in your app (e.g., \u003Ccode dir=\"auto\">app/api/health/route.ts\u003C/code> for App Router, or \u003Ccode dir=\"auto\">pages/api/health.ts\u003C/code> for Pages Router) that returns a \u003Ccode dir=\"auto\">200 OK\u003C/code> status.\u003C/p>\n\u003Cp>When running \u003Ccode dir=\"auto\">deploy-stack\u003C/code>, choose \u003Cstrong>Advanced Configuration\u003C/strong> and set your ALB Health Check Path to this route (e.g., \u003Ccode dir=\"auto\">/api/health\u003C/code>).\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h3\">\u003Ch3 id=\"3-deploy\">3. Deploy\u003C/h3>\u003Ca class=\"sl-anchor-link\" href=\"#3-deploy\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “3. Deploy”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>Your Next.js app is now perfectly optimized for AWS ECS Fargate!\u003C/p>\n\u003Cp>Run \u003Ccode dir=\"auto\">npx deploy-stack apply\u003C/code>. The CLI’s generated \u003Ccode dir=\"auto\">Dockerfile\u003C/code> will automatically target your new \u003Ccode dir=\"auto\">.next/standalone\u003C/code> directory and deploy the optimized build to the cloud.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"next-steps\">Next steps\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#next-steps\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Next steps”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>\u003Ca href=\"/deploy-stack/guides/cicd-pipeline/\">CI/CD Pipeline & First Deploy\u003C/a> for what happens on \u003Ccode dir=\"auto\">git push\u003C/code>.\u003C/li>\n\u003Cli>\u003Ca href=\"/deploy-stack/guides/frameworks/\">Supported Frameworks\u003C/a> for Next.js requirements.\u003C/li>\n\u003C/ul>\n",{"headings":1088,"localImagePaths":1098,"remoteImagePaths":1099,"frontmatter":1100,"imagePaths":1101},[1089,1090,1093,1096,1097],{"depth":22,"slug":1026,"text":1027},{"depth":35,"slug":1091,"text":1092},"1-update-nextconfigjs-or-mjs--cjs","1. Update next.config.js (or .mjs / .cjs)",{"depth":35,"slug":1094,"text":1095},"2-optional-define-a-health-check-route","2. (Optional) Define a Health Check Route",{"depth":35,"slug":1035,"text":1036},{"depth":22,"slug":918,"text":919},[],[],{"title":1077,"description":1078},[],"migrations/sveltekit-vercel-to-aws",{"id":1102,"data":1104,"body":1110,"filePath":1111,"digest":1112,"rendered":1113},{"title":1105,"description":1106,"editUrl":8,"head":1107,"template":10,"sidebar":1108,"pagefind":8,"draft":12},"Migrating SvelteKit from Vercel to AWS Fargate","Switch SvelteKit to adapter-node to leave Vercel for AWS Fargate.",[],{"hidden":12,"attrs":1109},{},"If you are seeing a warning from `deploy-stack` about your SvelteKit adapter, your project is currently using `@sveltejs/adapter-auto` (which often defaults to Vercel) or the explicit `@sveltejs/adapter-vercel`.\n\nThese adapters are designed specifically for proprietary serverless edge networks. To run your SvelteKit app in a scalable, standard Docker container on AWS Fargate, you need to switch to Svelte's official Node adapter.\n\n## How to Fix\n\n### 1. Install the Node Adapter\nRun the following command in your terminal to install the Node adapter and remove the Vercel/Auto adapter:\n\n```bash\nnpm install -D @sveltejs/adapter-node\nnpm uninstall @sveltejs/adapter-auto @sveltejs/adapter-vercel\n```\n\n### 2. Update `svelte.config.js`\nOpen your `svelte.config.js` file and change the adapter import at the top of the file.\n\n**Before (Locked into Vercel/Auto):**\n```javascript\nimport adapter from '@sveltejs/adapter-auto'; // or '@sveltejs/adapter-vercel'\nimport { vitePreprocess } from '@sveltejs/vite-plugin-svelte';\n\n/** @type {import('@sveltejs/kit').Config} */\nconst config = {\n\tpreprocess: vitePreprocess(),\n\tkit: {\n\t\tadapter: adapter()\n\t}\n};\n\nexport default config;\n```\n\n**After (AWS Ready):**\n```javascript\nimport adapter from '@sveltejs/adapter-node';\nimport { vitePreprocess } from '@sveltejs/vite-plugin-svelte';\n\n/** @type {import('@sveltejs/kit').Config} */\nconst config = {\n\tpreprocess: vitePreprocess(),\n\tkit: {\n\t\tadapter: adapter()\n\t}\n};\n\nexport default config;\n```\n\n### 3. Deploy\nYour SvelteKit app is now decoupled! \n\nRun `npx deploy-stack apply`. The CLI will automatically detect the standard Node build output, package it into a hardened Docker container, and deploy it to your AWS cluster.\n\n## Next steps\n\n- [CI/CD Pipeline & First Deploy](/deploy-stack/guides/cicd-pipeline/) for what happens on `git push`.\n- [Supported Frameworks](/deploy-stack/guides/frameworks/) for SvelteKit adapter requirements.","src/content/docs/migrations/sveltekit-vercel-to-aws.md","d0a85d02b3ebd345",{"html":1114,"metadata":1115},"\u003Cp>If you are seeing a warning from \u003Ccode dir=\"auto\">deploy-stack\u003C/code> about your SvelteKit adapter, your project is currently using \u003Ccode dir=\"auto\">@sveltejs/adapter-auto\u003C/code> (which often defaults to Vercel) or the explicit \u003Ccode dir=\"auto\">@sveltejs/adapter-vercel\u003C/code>.\u003C/p>\n\u003Cp>These adapters are designed specifically for proprietary serverless edge networks. To run your SvelteKit app in a scalable, standard Docker container on AWS Fargate, you need to switch to Svelte’s official Node adapter.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"how-to-fix\">How to Fix\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#how-to-fix\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “How to Fix”\u003C/span>\u003C/a>\u003C/div>\n\u003Cdiv class=\"sl-heading-wrapper level-h3\">\u003Ch3 id=\"1-install-the-node-adapter\">1. Install the Node Adapter\u003C/h3>\u003Ca class=\"sl-anchor-link\" href=\"#1-install-the-node-adapter\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “1. Install the Node Adapter”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>Run the following command in your terminal to install the Node adapter and remove the Vercel/Auto adapter:\u003C/p>\n\u003Cdiv class=\"expressive-code\">\u003Clink rel=\"stylesheet\" href=\"/deploy-stack/_astro/ec.w36nc.css\">\u003Cscript type=\"module\" src=\"/deploy-stack/_astro/ec.0vx5m.js\">\u003C/script>\u003Cfigure class=\"frame is-terminal not-content\">\u003Cfigcaption class=\"header\">\u003Cspan class=\"title\">\u003C/span>\u003Cspan class=\"sr-only\">Terminal window\u003C/span>\u003C/figcaption>\u003Cpre data-language=\"bash\">\u003Ccode>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">npm\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">install\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">-D\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">@sveltejs/adapter-node\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">npm\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">uninstall\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">@sveltejs/adapter-auto\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#3B61B0\">@sveltejs/adapter-vercel\u003C/span>\u003C/div>\u003C/div>\u003C/code>\u003C/pre>\u003Cdiv class=\"copy\">\u003Cdiv aria-live=\"polite\">\u003C/div>\u003Cbutton title=\"Copy to clipboard\" data-copied=\"Copied!\" data-code=\"npm install -D @sveltejs/adapter-nodenpm uninstall @sveltejs/adapter-auto @sveltejs/adapter-vercel\">\u003Cdiv>\u003C/div>\u003C/button>\u003C/div>\u003C/figure>\u003C/div>\n\u003Cdiv class=\"sl-heading-wrapper level-h3\">\u003Ch3 id=\"2-update-svelteconfigjs\">2. Update \u003Ccode dir=\"auto\">svelte.config.js\u003C/code>\u003C/h3>\u003Ca class=\"sl-anchor-link\" href=\"#2-update-svelteconfigjs\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “2. Update svelte.config.js”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>Open your \u003Ccode dir=\"auto\">svelte.config.js\u003C/code> file and change the adapter import at the top of the file.\u003C/p>\n\u003Cp>\u003Cstrong>Before (Locked into Vercel/Auto):\u003C/strong>\u003C/p>\n\u003Cdiv class=\"expressive-code\">\u003Cfigure class=\"frame not-content\">\u003Cfigcaption class=\"header\">\u003C/figcaption>\u003Cpre data-language=\"javascript\">\u003Ccode>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#C792EA;--1:#8844AE\">import\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> adapter \u003C/span>\u003Cspan style=\"--0:#C792EA;--1:#8844AE\">from\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#D9F5DD;--1:#111111\">'\u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#984E4D\">@sveltejs/adapter-auto\u003C/span>\u003Cspan style=\"--0:#D9F5DD;--1:#111111\">'\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\">; \u003C/span>\u003Cspan style=\"--0:#919F9F;--1:#5D6376\">// or '@sveltejs/adapter-vercel'\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#C792EA;--1:#8844AE\">import\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> { vitePreprocess } \u003C/span>\u003Cspan style=\"--0:#C792EA;--1:#8844AE\">from\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#D9F5DD;--1:#111111\">'\u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#984E4D\">@sveltejs/vite-plugin-svelte\u003C/span>\u003Cspan style=\"--0:#D9F5DD;--1:#111111\">'\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\">;\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\n\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#919F9F;--1:#5F636F\">/** \u003C/span>\u003Cspan style=\"--0:#C792EA;--1:#8844AE\">@type\u003C/span>\u003Cspan style=\"--0:#919F9F;--1:#5F636F\"> \u003C/span>\u003Cspan style=\"--0:#889FB2;--1:#4D667B\">{import('@sveltejs/kit').Config}\u003C/span>\u003Cspan style=\"--0:#919F9F;--1:#5F636F\"> */\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#C792EA;--1:#8844AE\">const \u003C/span>\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">config\u003C/span>\u003Cspan style=\"--0:#C792EA;--1:#8844AE\"> = {\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan class=\"indent\">\u003Cspan style=\"--0:#C792EA;--1:#8844AE\"> \u003C/span>\u003C/span>\u003Cspan style=\"--0:#C792EA;--1:#8844AE\">preprocess: \u003C/span>\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">vitePreprocess\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\">()\u003C/span>\u003Cspan style=\"--0:#C792EA;--1:#8844AE\">,\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan class=\"indent\">\u003Cspan style=\"--0:#C792EA;--1:#8844AE\"> \u003C/span>\u003C/span>\u003Cspan style=\"--0:#C792EA;--1:#8844AE\">kit: {\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan class=\"indent\">\u003Cspan style=\"--0:#C792EA;--1:#8844AE\"> \u003C/span>\u003C/span>\u003Cspan style=\"--0:#C792EA;--1:#8844AE\">adapter: \u003C/span>\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">adapter\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\">()\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan class=\"indent\">\u003Cspan style=\"--0:#C792EA;--1:#8844AE\"> \u003C/span>\u003C/span>\u003Cspan style=\"--0:#C792EA;--1:#8844AE\">}\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#C792EA;--1:#8844AE\">}\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\">;\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\n\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#C792EA;--1:#8844AE\">export\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#C792EA;--1:#8844AE\">default\u003C/span>\u003Cspan style=\"--1:#403F53\">\u003Cspan style=\"--0:#D6DEEB\"> \u003C/span>\u003Cspan style=\"--0:#D7DBE0\">config\u003C/span>\u003Cspan style=\"--0:#D6DEEB\">;\u003C/span>\u003C/span>\u003C/div>\u003C/div>\u003C/code>\u003C/pre>\u003Cdiv class=\"copy\">\u003Cdiv aria-live=\"polite\">\u003C/div>\u003Cbutton title=\"Copy to clipboard\" data-copied=\"Copied!\" data-code=\"import adapter from '@sveltejs/adapter-auto'; // or '@sveltejs/adapter-vercel'import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';/** @type {import('@sveltejs/kit').Config} */const config = { preprocess: vitePreprocess(), kit: { adapter: adapter() }};export default config;\">\u003Cdiv>\u003C/div>\u003C/button>\u003C/div>\u003C/figure>\u003C/div>\n\u003Cp>\u003Cstrong>After (AWS Ready):\u003C/strong>\u003C/p>\n\u003Cdiv class=\"expressive-code\">\u003Cfigure class=\"frame not-content\">\u003Cfigcaption class=\"header\">\u003C/figcaption>\u003Cpre data-language=\"javascript\">\u003Ccode>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#C792EA;--1:#8844AE\">import\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> adapter \u003C/span>\u003Cspan style=\"--0:#C792EA;--1:#8844AE\">from\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#D9F5DD;--1:#111111\">'\u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#984E4D\">@sveltejs/adapter-node\u003C/span>\u003Cspan style=\"--0:#D9F5DD;--1:#111111\">'\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\">;\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#C792EA;--1:#8844AE\">import\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> { vitePreprocess } \u003C/span>\u003Cspan style=\"--0:#C792EA;--1:#8844AE\">from\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#D9F5DD;--1:#111111\">'\u003C/span>\u003Cspan style=\"--0:#ECC48D;--1:#984E4D\">@sveltejs/vite-plugin-svelte\u003C/span>\u003Cspan style=\"--0:#D9F5DD;--1:#111111\">'\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\">;\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\n\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#919F9F;--1:#5F636F\">/** \u003C/span>\u003Cspan style=\"--0:#C792EA;--1:#8844AE\">@type\u003C/span>\u003Cspan style=\"--0:#919F9F;--1:#5F636F\"> \u003C/span>\u003Cspan style=\"--0:#889FB2;--1:#4D667B\">{import('@sveltejs/kit').Config}\u003C/span>\u003Cspan style=\"--0:#919F9F;--1:#5F636F\"> */\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#C792EA;--1:#8844AE\">const \u003C/span>\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">config\u003C/span>\u003Cspan style=\"--0:#C792EA;--1:#8844AE\"> = {\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan class=\"indent\">\u003Cspan style=\"--0:#C792EA;--1:#8844AE\"> \u003C/span>\u003C/span>\u003Cspan style=\"--0:#C792EA;--1:#8844AE\">preprocess: \u003C/span>\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">vitePreprocess\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\">()\u003C/span>\u003Cspan style=\"--0:#C792EA;--1:#8844AE\">,\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan class=\"indent\">\u003Cspan style=\"--0:#C792EA;--1:#8844AE\"> \u003C/span>\u003C/span>\u003Cspan style=\"--0:#C792EA;--1:#8844AE\">kit: {\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan class=\"indent\">\u003Cspan style=\"--0:#C792EA;--1:#8844AE\"> \u003C/span>\u003C/span>\u003Cspan style=\"--0:#C792EA;--1:#8844AE\">adapter: \u003C/span>\u003Cspan style=\"--0:#82AAFF;--1:#3B61B0\">adapter\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\">()\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan class=\"indent\">\u003Cspan style=\"--0:#C792EA;--1:#8844AE\"> \u003C/span>\u003C/span>\u003Cspan style=\"--0:#C792EA;--1:#8844AE\">}\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#C792EA;--1:#8844AE\">}\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\">;\u003C/span>\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\n\u003C/div>\u003C/div>\u003Cdiv class=\"ec-line\">\u003Cdiv class=\"code\">\u003Cspan style=\"--0:#C792EA;--1:#8844AE\">export\u003C/span>\u003Cspan style=\"--0:#D6DEEB;--1:#403F53\"> \u003C/span>\u003Cspan style=\"--0:#C792EA;--1:#8844AE\">default\u003C/span>\u003Cspan style=\"--1:#403F53\">\u003Cspan style=\"--0:#D6DEEB\"> \u003C/span>\u003Cspan style=\"--0:#D7DBE0\">config\u003C/span>\u003Cspan style=\"--0:#D6DEEB\">;\u003C/span>\u003C/span>\u003C/div>\u003C/div>\u003C/code>\u003C/pre>\u003Cdiv class=\"copy\">\u003Cdiv aria-live=\"polite\">\u003C/div>\u003Cbutton title=\"Copy to clipboard\" data-copied=\"Copied!\" data-code=\"import adapter from '@sveltejs/adapter-node';import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';/** @type {import('@sveltejs/kit').Config} */const config = { preprocess: vitePreprocess(), kit: { adapter: adapter() }};export default config;\">\u003Cdiv>\u003C/div>\u003C/button>\u003C/div>\u003C/figure>\u003C/div>\n\u003Cdiv class=\"sl-heading-wrapper level-h3\">\u003Ch3 id=\"3-deploy\">3. Deploy\u003C/h3>\u003Ca class=\"sl-anchor-link\" href=\"#3-deploy\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “3. Deploy”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>Your SvelteKit app is now decoupled!\u003C/p>\n\u003Cp>Run \u003Ccode dir=\"auto\">npx deploy-stack apply\u003C/code>. The CLI will automatically detect the standard Node build output, package it into a hardened Docker container, and deploy it to your AWS cluster.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"next-steps\">Next steps\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#next-steps\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Next steps”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul>\n\u003Cli>\u003Ca href=\"/deploy-stack/guides/cicd-pipeline/\">CI/CD Pipeline & First Deploy\u003C/a> for what happens on \u003Ccode dir=\"auto\">git push\u003C/code>.\u003C/li>\n\u003Cli>\u003Ca href=\"/deploy-stack/guides/frameworks/\">Supported Frameworks\u003C/a> for SvelteKit adapter requirements.\u003C/li>\n\u003C/ul>\n",{"headings":1116,"localImagePaths":1125,"remoteImagePaths":1126,"frontmatter":1127,"imagePaths":1128},[1117,1118,1120,1123,1124],{"depth":22,"slug":1026,"text":1027},{"depth":35,"slug":1029,"text":1119},"1. Install the Node Adapter",{"depth":35,"slug":1121,"text":1122},"2-update-svelteconfigjs","2. Update svelte.config.js",{"depth":35,"slug":1035,"text":1036},{"depth":22,"slug":918,"text":919},[],[],{"title":1105,"description":1106},[],"roadmap",{"id":1129,"data":1131,"body":1137,"filePath":1138,"digest":1139,"rendered":1140},{"title":1132,"description":1133,"editUrl":8,"head":1134,"template":10,"sidebar":1135,"pagefind":8,"draft":12},"Roadmap","Where deploy-stack has been and what comes next — completed phases and the current platform-hardening milestone.",[],{"hidden":12,"attrs":1136},{},"### Phase 1–3: The Core Engine (Completed)\n- [x] **Core MVP:** Interactive CLI, ECS Fargate + ALB generation, CI/CD, and Secrets sync.\n- [x] **Production Readiness:** CloudFront CDN edge distribution, native S3 state locking, and secure OIDC integration.\n- [x] **Smart Experience:** Zero-config framework auto-discovery for static output directories.\n- [x] **Trust & Observability:** DevSecOps Trivy scanning, automated 5XX alarms, 14-day log retention, and safe local overwrite protections.\n\n### Phase 4: Trust Anchors & TAM Expansion (Completed)\n- [x] **Ecosystem Distribution:** Native GitHub Marketplace Action for rapid discovery.\n- [x] **Cost Transparency:** Pre-flight AWS cost estimator injected directly into the CLI wizard.\n- [x] **Zero Vendor Lock-In:** Explicit `npx deploy-stack eject` command to safely strip `ManagedBy` tags and CLI metadata, leaving behind pure IaC.\n- [x] **Heavy Backend Monoliths:** Hardened, unprivileged container adapters for Go, Nuxt.js, Django, and Rails, complete with automated zero-trust RDS PostgreSQL provisioning.\n\n### Phase 5: The Activation Engine (Completed)\n- [x] **Local Execution Wrapper:** Native `deploy-stack apply` command with terminal-optimized streaming to eliminate Terraform context switching.\n- [x] **Ecosystem Integrations:** Official plugins published to the Astro Integrations directory (`astro-deploy-stack`) and Nuxt module registry (`nuxt-deploy-stack`).\n\n### Phase 6: Migration & Trust Engine (Completed)\n- [x] **Dry-Run Visualization:** Interactive pre-flight terminal UI with ASCII topology maps and precise, dynamic AWS cost estimation.\n- [x] **PaaS Importers:** Auto-parse `vercel.json` and Heroku `Procfile` configurations to map routing rules, web commands, and background workers automatically.\n- [x] **Docker Compose to ECS Translator:** Automatically converting a familiar local `docker-compose.yml` into production ECS task definitions.\n- [x] **AI Agent Rulesets:** Publishing `.cursorrules` and Copilot instructions that teach AI assistants exactly how to utilize the CLI on the user's behalf.\n\n### Phase 7: Team Workflows & Ecosystem Integrations (Completed)\n*Focus: Enhance collaborative development and expand native support across major framework ecosystems.*\n- [x] **Ephemeral PR Previews:** Generate GitHub Actions workflows that spin up temporary ECS Fargate tasks and post live preview URLs directly in pull request comments to streamline team code reviews.\n- [x] **AI Context Synchronization:** Implement `deploy-stack sync-ai` to automatically generate `.cursorrules` and AI context files, ensuring coding assistants generate accurate deployment commands tailored to the project.\n- [x] **Native Ecosystem Integrations:** Publish seamless, push-button plugins across major frameworks.\n - [x] `vite-plugin-deploy-stack` (Live on NPM)\n - [x] `svelte-adapter-deploy-stack` (SvelteKit adapter integration)\n - [x] `cookiecutter-django-deploy-stack` (Listed on Django Packages)\n - [x] `cookiecutter-fastapi-deploy-stack` (Cookiecutter for modern async Python)\n - [x] `nest-deploy-stack` (Native `nest add` schematic for NestJS)\n - [x] `rails-template-deploy-stack` (Zero-click Ruby on Rails application template)\n- [x] **Automated Troubleshooting:** `deploy-stack diagnose` (alias: `wtf`) automatically analyzes common day-2 AWS operational issues (e.g., Fargate OOM kills, ALB 502s) directly from the terminal.\n\n### Phase 8: Platform Hardening & Developer Experience (Completed)\n*Focus: Solidify the core engine's reliability, prove security compliance, and establish documentation hub before introducing Day-2 operational commands.*\n- [x] **Documentation Hub:** Launch a dedicated Astro Starlight documentation site featuring interactive architecture diagrams, core concept deep-dives, and detailed CLI references.\n- [x] **Continuous Infrastructure Validation:** Implement a GitHub Actions matrix pipeline that automatically generates, compiles, and validates Terraform syntax (`terraform validate`, `tflint`) against all supported frameworks on every commit.\n- [x] **Automated Security & Compliance Proving:** Integrate DevSecOps infrastructure scanning (`trivy` or `tfsec`) directly into the CI pipeline to mathematically guarantee zero-CVE, secure-by-default AWS provisioning.\n- [x] **Integration Stability Suite:** Expand Vitest coverage to enforce strict contracts for headless execution flags (`--preconfigured`, `--headless`), ensuring seamless interoperability with third-party scaffolding tools.\n\n### Phase 9: Day-2 Operations & Developer Retention (Current)\n*Focus: Uninterrupted Developer Flow. Deliver a seamless Day-2 environment where users maintain full infrastructure control without leaving the command line to troubleshoot.*\n- [x] **Context-Aware Log Streaming:** `deploy-stack logs \u003Cservice> --tail --error`. Implement a live stream using the CloudWatch Logs API to merge API/frontend logs in a color-coded terminal view, eliminating the need to navigate the AWS web console.\n- [x] **1-Click Container Access:** `deploy-stack exec \u003Cservice>`. Automatically drop the user into a secure bash shell inside a running Fargate container using AWS Systems Manager (SSM) Session Manager, abstracting away complex IAM trust policies and local agent requirements.\n- [x] **Secure Secrets Sync & Rolling Restarts:** `deploy-stack secrets pull/audit`. Fetch vault payloads to a local `.env`, compare local vs. remote keys, and trigger rolling ECS restarts for value-only rotations.\n- [ ] **Secure Database Tunneling:** `deploy-stack db connect`. Utilize SSM Port Forwarding to open a secure `localhost` tunnel directly to private RDS or ElastiCache instances, allowing tools like DBeaver or Prisma Studio to query production data without public internet exposure.\n- [x] **Health & Alarm Dashboard:** `deploy-stack status`. Query the ECS Service status (Desired vs. Running tasks) and CloudWatch Alarms (e.g., ALB 5XX errors), printing a clear green/red operational status matrix directly in the terminal.\n- [x] **Orphaned Resource Garbage Collection:** `deploy-stack gc`. Scan the AWS account for unattached Elastic IPs, abandoned ECR image layers, and lingering CloudWatch log groups left behind by PR previews or manual deletions, safely removing them to protect the user's AWS bill.","src/content/docs/roadmap.md","3154939af6e6ee97",{"html":1141,"metadata":1142},"\u003Cdiv class=\"sl-heading-wrapper level-h3\">\u003Ch3 id=\"phase-13-the-core-engine-completed\">Phase 1–3: The Core Engine (Completed)\u003C/h3>\u003Ca class=\"sl-anchor-link\" href=\"#phase-13-the-core-engine-completed\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Phase 1–3: The Core Engine (Completed)”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul class=\"contains-task-list\">\n\u003Cli class=\"task-list-item\">\u003Cinput type=\"checkbox\" checked disabled> \u003Cstrong>Core MVP:\u003C/strong> Interactive CLI, ECS Fargate + ALB generation, CI/CD, and Secrets sync.\u003C/li>\n\u003Cli class=\"task-list-item\">\u003Cinput type=\"checkbox\" checked disabled> \u003Cstrong>Production Readiness:\u003C/strong> CloudFront CDN edge distribution, native S3 state locking, and secure OIDC integration.\u003C/li>\n\u003Cli class=\"task-list-item\">\u003Cinput type=\"checkbox\" checked disabled> \u003Cstrong>Smart Experience:\u003C/strong> Zero-config framework auto-discovery for static output directories.\u003C/li>\n\u003Cli class=\"task-list-item\">\u003Cinput type=\"checkbox\" checked disabled> \u003Cstrong>Trust & Observability:\u003C/strong> DevSecOps Trivy scanning, automated 5XX alarms, 14-day log retention, and safe local overwrite protections.\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h3\">\u003Ch3 id=\"phase-4-trust-anchors--tam-expansion-completed\">Phase 4: Trust Anchors & TAM Expansion (Completed)\u003C/h3>\u003Ca class=\"sl-anchor-link\" href=\"#phase-4-trust-anchors--tam-expansion-completed\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Phase 4: Trust Anchors & TAM Expansion (Completed)”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul class=\"contains-task-list\">\n\u003Cli class=\"task-list-item\">\u003Cinput type=\"checkbox\" checked disabled> \u003Cstrong>Ecosystem Distribution:\u003C/strong> Native GitHub Marketplace Action for rapid discovery.\u003C/li>\n\u003Cli class=\"task-list-item\">\u003Cinput type=\"checkbox\" checked disabled> \u003Cstrong>Cost Transparency:\u003C/strong> Pre-flight AWS cost estimator injected directly into the CLI wizard.\u003C/li>\n\u003Cli class=\"task-list-item\">\u003Cinput type=\"checkbox\" checked disabled> \u003Cstrong>Zero Vendor Lock-In:\u003C/strong> Explicit \u003Ccode dir=\"auto\">npx deploy-stack eject\u003C/code> command to safely strip \u003Ccode dir=\"auto\">ManagedBy\u003C/code> tags and CLI metadata, leaving behind pure IaC.\u003C/li>\n\u003Cli class=\"task-list-item\">\u003Cinput type=\"checkbox\" checked disabled> \u003Cstrong>Heavy Backend Monoliths:\u003C/strong> Hardened, unprivileged container adapters for Go, Nuxt.js, Django, and Rails, complete with automated zero-trust RDS PostgreSQL provisioning.\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h3\">\u003Ch3 id=\"phase-5-the-activation-engine-completed\">Phase 5: The Activation Engine (Completed)\u003C/h3>\u003Ca class=\"sl-anchor-link\" href=\"#phase-5-the-activation-engine-completed\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Phase 5: The Activation Engine (Completed)”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul class=\"contains-task-list\">\n\u003Cli class=\"task-list-item\">\u003Cinput type=\"checkbox\" checked disabled> \u003Cstrong>Local Execution Wrapper:\u003C/strong> Native \u003Ccode dir=\"auto\">deploy-stack apply\u003C/code> command with terminal-optimized streaming to eliminate Terraform context switching.\u003C/li>\n\u003Cli class=\"task-list-item\">\u003Cinput type=\"checkbox\" checked disabled> \u003Cstrong>Ecosystem Integrations:\u003C/strong> Official plugins published to the Astro Integrations directory (\u003Ccode dir=\"auto\">astro-deploy-stack\u003C/code>) and Nuxt module registry (\u003Ccode dir=\"auto\">nuxt-deploy-stack\u003C/code>).\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h3\">\u003Ch3 id=\"phase-6-migration--trust-engine-completed\">Phase 6: Migration & Trust Engine (Completed)\u003C/h3>\u003Ca class=\"sl-anchor-link\" href=\"#phase-6-migration--trust-engine-completed\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Phase 6: Migration & Trust Engine (Completed)”\u003C/span>\u003C/a>\u003C/div>\n\u003Cul class=\"contains-task-list\">\n\u003Cli class=\"task-list-item\">\u003Cinput type=\"checkbox\" checked disabled> \u003Cstrong>Dry-Run Visualization:\u003C/strong> Interactive pre-flight terminal UI with ASCII topology maps and precise, dynamic AWS cost estimation.\u003C/li>\n\u003Cli class=\"task-list-item\">\u003Cinput type=\"checkbox\" checked disabled> \u003Cstrong>PaaS Importers:\u003C/strong> Auto-parse \u003Ccode dir=\"auto\">vercel.json\u003C/code> and Heroku \u003Ccode dir=\"auto\">Procfile\u003C/code> configurations to map routing rules, web commands, and background workers automatically.\u003C/li>\n\u003Cli class=\"task-list-item\">\u003Cinput type=\"checkbox\" checked disabled> \u003Cstrong>Docker Compose to ECS Translator:\u003C/strong> Automatically converting a familiar local \u003Ccode dir=\"auto\">docker-compose.yml\u003C/code> into production ECS task definitions.\u003C/li>\n\u003Cli class=\"task-list-item\">\u003Cinput type=\"checkbox\" checked disabled> \u003Cstrong>AI Agent Rulesets:\u003C/strong> Publishing \u003Ccode dir=\"auto\">.cursorrules\u003C/code> and Copilot instructions that teach AI assistants exactly how to utilize the CLI on the user’s behalf.\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h3\">\u003Ch3 id=\"phase-7-team-workflows--ecosystem-integrations-completed\">Phase 7: Team Workflows & Ecosystem Integrations (Completed)\u003C/h3>\u003Ca class=\"sl-anchor-link\" href=\"#phase-7-team-workflows--ecosystem-integrations-completed\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Phase 7: Team Workflows & Ecosystem Integrations (Completed)”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>\u003Cem>Focus: Enhance collaborative development and expand native support across major framework ecosystems.\u003C/em>\u003C/p>\n\u003Cul class=\"contains-task-list\">\n\u003Cli class=\"task-list-item\">\u003Cinput type=\"checkbox\" checked disabled> \u003Cstrong>Ephemeral PR Previews:\u003C/strong> Generate GitHub Actions workflows that spin up temporary ECS Fargate tasks and post live preview URLs directly in pull request comments to streamline team code reviews.\u003C/li>\n\u003Cli class=\"task-list-item\">\u003Cinput type=\"checkbox\" checked disabled> \u003Cstrong>AI Context Synchronization:\u003C/strong> Implement \u003Ccode dir=\"auto\">deploy-stack sync-ai\u003C/code> to automatically generate \u003Ccode dir=\"auto\">.cursorrules\u003C/code> and AI context files, ensuring coding assistants generate accurate deployment commands tailored to the project.\u003C/li>\n\u003Cli class=\"task-list-item\">\u003Cinput type=\"checkbox\" checked disabled> \u003Cstrong>Native Ecosystem Integrations:\u003C/strong> Publish seamless, push-button plugins across major frameworks.\n\u003Cul class=\"contains-task-list\">\n\u003Cli class=\"task-list-item\">\u003Cinput type=\"checkbox\" checked disabled> \u003Ccode dir=\"auto\">vite-plugin-deploy-stack\u003C/code> (Live on NPM)\u003C/li>\n\u003Cli class=\"task-list-item\">\u003Cinput type=\"checkbox\" checked disabled> \u003Ccode dir=\"auto\">svelte-adapter-deploy-stack\u003C/code> (SvelteKit adapter integration)\u003C/li>\n\u003Cli class=\"task-list-item\">\u003Cinput type=\"checkbox\" checked disabled> \u003Ccode dir=\"auto\">cookiecutter-django-deploy-stack\u003C/code> (Listed on Django Packages)\u003C/li>\n\u003Cli class=\"task-list-item\">\u003Cinput type=\"checkbox\" checked disabled> \u003Ccode dir=\"auto\">cookiecutter-fastapi-deploy-stack\u003C/code> (Cookiecutter for modern async Python)\u003C/li>\n\u003Cli class=\"task-list-item\">\u003Cinput type=\"checkbox\" checked disabled> \u003Ccode dir=\"auto\">nest-deploy-stack\u003C/code> (Native \u003Ccode dir=\"auto\">nest add\u003C/code> schematic for NestJS)\u003C/li>\n\u003Cli class=\"task-list-item\">\u003Cinput type=\"checkbox\" checked disabled> \u003Ccode dir=\"auto\">rails-template-deploy-stack\u003C/code> (Zero-click Ruby on Rails application template)\u003C/li>\n\u003C/ul>\n\u003C/li>\n\u003Cli class=\"task-list-item\">\u003Cinput type=\"checkbox\" checked disabled> \u003Cstrong>Automated Troubleshooting:\u003C/strong> \u003Ccode dir=\"auto\">deploy-stack diagnose\u003C/code> (alias: \u003Ccode dir=\"auto\">wtf\u003C/code>) automatically analyzes common day-2 AWS operational issues (e.g., Fargate OOM kills, ALB 502s) directly from the terminal.\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h3\">\u003Ch3 id=\"phase-8-platform-hardening--developer-experience-completed\">Phase 8: Platform Hardening & Developer Experience (Completed)\u003C/h3>\u003Ca class=\"sl-anchor-link\" href=\"#phase-8-platform-hardening--developer-experience-completed\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Phase 8: Platform Hardening & Developer Experience (Completed)”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>\u003Cem>Focus: Solidify the core engine’s reliability, prove security compliance, and establish documentation hub before introducing Day-2 operational commands.\u003C/em>\u003C/p>\n\u003Cul class=\"contains-task-list\">\n\u003Cli class=\"task-list-item\">\u003Cinput type=\"checkbox\" checked disabled> \u003Cstrong>Documentation Hub:\u003C/strong> Launch a dedicated Astro Starlight documentation site featuring interactive architecture diagrams, core concept deep-dives, and detailed CLI references.\u003C/li>\n\u003Cli class=\"task-list-item\">\u003Cinput type=\"checkbox\" checked disabled> \u003Cstrong>Continuous Infrastructure Validation:\u003C/strong> Implement a GitHub Actions matrix pipeline that automatically generates, compiles, and validates Terraform syntax (\u003Ccode dir=\"auto\">terraform validate\u003C/code>, \u003Ccode dir=\"auto\">tflint\u003C/code>) against all supported frameworks on every commit.\u003C/li>\n\u003Cli class=\"task-list-item\">\u003Cinput type=\"checkbox\" checked disabled> \u003Cstrong>Automated Security & Compliance Proving:\u003C/strong> Integrate DevSecOps infrastructure scanning (\u003Ccode dir=\"auto\">trivy\u003C/code> or \u003Ccode dir=\"auto\">tfsec\u003C/code>) directly into the CI pipeline to mathematically guarantee zero-CVE, secure-by-default AWS provisioning.\u003C/li>\n\u003Cli class=\"task-list-item\">\u003Cinput type=\"checkbox\" checked disabled> \u003Cstrong>Integration Stability Suite:\u003C/strong> Expand Vitest coverage to enforce strict contracts for headless execution flags (\u003Ccode dir=\"auto\">--preconfigured\u003C/code>, \u003Ccode dir=\"auto\">--headless\u003C/code>), ensuring seamless interoperability with third-party scaffolding tools.\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h3\">\u003Ch3 id=\"phase-9-day-2-operations--developer-retention-current\">Phase 9: Day-2 Operations & Developer Retention (Current)\u003C/h3>\u003Ca class=\"sl-anchor-link\" href=\"#phase-9-day-2-operations--developer-retention-current\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “Phase 9: Day-2 Operations & Developer Retention (Current)”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>\u003Cem>Focus: Uninterrupted Developer Flow. Deliver a seamless Day-2 environment where users maintain full infrastructure control without leaving the command line to troubleshoot.\u003C/em>\u003C/p>\n\u003Cul class=\"contains-task-list\">\n\u003Cli class=\"task-list-item\">\u003Cinput type=\"checkbox\" checked disabled> \u003Cstrong>Context-Aware Log Streaming:\u003C/strong> \u003Ccode dir=\"auto\">deploy-stack logs <service> --tail --error\u003C/code>. Implement a live stream using the CloudWatch Logs API to merge API/frontend logs in a color-coded terminal view, eliminating the need to navigate the AWS web console.\u003C/li>\n\u003Cli class=\"task-list-item\">\u003Cinput type=\"checkbox\" checked disabled> \u003Cstrong>1-Click Container Access:\u003C/strong> \u003Ccode dir=\"auto\">deploy-stack exec <service>\u003C/code>. Automatically drop the user into a secure bash shell inside a running Fargate container using AWS Systems Manager (SSM) Session Manager, abstracting away complex IAM trust policies and local agent requirements.\u003C/li>\n\u003Cli class=\"task-list-item\">\u003Cinput type=\"checkbox\" checked disabled> \u003Cstrong>Secure Secrets Sync & Rolling Restarts:\u003C/strong> \u003Ccode dir=\"auto\">deploy-stack secrets pull/audit\u003C/code>. Fetch vault payloads to a local \u003Ccode dir=\"auto\">.env\u003C/code>, compare local vs. remote keys, and trigger rolling ECS restarts for value-only rotations.\u003C/li>\n\u003Cli class=\"task-list-item\">\u003Cinput type=\"checkbox\" disabled> \u003Cstrong>Secure Database Tunneling:\u003C/strong> \u003Ccode dir=\"auto\">deploy-stack db connect\u003C/code>. Utilize SSM Port Forwarding to open a secure \u003Ccode dir=\"auto\">localhost\u003C/code> tunnel directly to private RDS or ElastiCache instances, allowing tools like DBeaver or Prisma Studio to query production data without public internet exposure.\u003C/li>\n\u003Cli class=\"task-list-item\">\u003Cinput type=\"checkbox\" checked disabled> \u003Cstrong>Health & Alarm Dashboard:\u003C/strong> \u003Ccode dir=\"auto\">deploy-stack status\u003C/code>. Query the ECS Service status (Desired vs. Running tasks) and CloudWatch Alarms (e.g., ALB 5XX errors), printing a clear green/red operational status matrix directly in the terminal.\u003C/li>\n\u003Cli class=\"task-list-item\">\u003Cinput type=\"checkbox\" checked disabled> \u003Cstrong>Orphaned Resource Garbage Collection:\u003C/strong> \u003Ccode dir=\"auto\">deploy-stack gc\u003C/code>. Scan the AWS account for unattached Elastic IPs, abandoned ECR image layers, and lingering CloudWatch log groups left behind by PR previews or manual deletions, safely removing them to protect the user’s AWS bill.\u003C/li>\n\u003C/ul>\n",{"headings":1143,"localImagePaths":1165,"remoteImagePaths":1166,"frontmatter":1167,"imagePaths":1168},[1144,1147,1150,1153,1156,1159,1162],{"depth":35,"slug":1145,"text":1146},"phase-13-the-core-engine-completed","Phase 1–3: The Core Engine (Completed)",{"depth":35,"slug":1148,"text":1149},"phase-4-trust-anchors--tam-expansion-completed","Phase 4: Trust Anchors & TAM Expansion (Completed)",{"depth":35,"slug":1151,"text":1152},"phase-5-the-activation-engine-completed","Phase 5: The Activation Engine (Completed)",{"depth":35,"slug":1154,"text":1155},"phase-6-migration--trust-engine-completed","Phase 6: Migration & Trust Engine (Completed)",{"depth":35,"slug":1157,"text":1158},"phase-7-team-workflows--ecosystem-integrations-completed","Phase 7: Team Workflows & Ecosystem Integrations (Completed)",{"depth":35,"slug":1160,"text":1161},"phase-8-platform-hardening--developer-experience-completed","Phase 8: Platform Hardening & Developer Experience (Completed)",{"depth":35,"slug":1163,"text":1164},"phase-9-day-2-operations--developer-retention-current","Phase 9: Day-2 Operations & Developer Retention (Current)",[],[],{"title":1132,"description":1133},[],"testing-strategy",{"id":1169,"data":1171,"body":1177,"filePath":1178,"digest":1179,"rendered":1180},{"title":1172,"description":1173,"editUrl":8,"head":1174,"template":10,"sidebar":1175,"pagefind":8,"draft":12},"Testing Strategy","How deploy-stack prevents regressions — unit tests, snapshot harness, API mocking, and CI validation.",[],{"hidden":12,"attrs":1176},{},"To ensure zero regressions in infrastructure generation and safe local execution, `deploy-stack` relies on a multi-layered testing strategy split between fast local snapshots and rigid CI/CD validation.\n\n## 1. Unit & argument testing\nWe use pure Node.js unit tests (via Vitest) to validate the CLI argument parser (`src/core/parser.js`). This ensures that flags (like `--headless` or `--no-telemetry`) are routed correctly and never hijack positional arguments like file paths.\n\n## 1.5. Ecosystem integration contracts\nBecause `deploy-stack` acts as the underlying engine for ecosystem wrappers (e.g., `nest-deploy-stack`, `cookiecutter-fastapi`), we strictly test execution flags that bypass interactive prompts (written contract: `specs/integration-suite.md`, enforced by `tests/headless.test.js`):\n* **Headless Validation:** Vitest deep-mocks `@clack/prompts` — the only interactive prompt library the CLI uses — and asserts that when `--headless` and `--preconfigured` are passed (e.g., `--framework=nestjs --port=3000`), none of its prompt functions (`text`, `select`, `multiselect`, `confirm`, `group`) ever fire and no interactive warnings are thrown. The suite also asserts flag values win over interactive defaults in the generated `terraform/main.tf` and `Dockerfile`, running end to end inside a temp directory so no files pollute the repo. This guarantees stability for automated ecosystem integrations.\n\n## 2. Infrastructure snapshot harness (the static contract)\nBecause `deploy-stack` generates highly dynamic Terraform (`.tf`), GitHub Actions (`.yml`), and `Dockerfile` configurations, we use **Vitest Snapshots** to lock in the expected text outputs.\n* **The Matrix:** The test suite generates dummy projects across 11 architectural topologies (including Django, Rails, Go, Nuxt, Next.js, SvelteKit, and Vercel/Heroku migrations).\n* **Negative Testing:** The suite explicitly checks for the *absence* of files (e.g., ensuring `database.tf` or `worker.tf` are not generated for static sites).\n* **Updating Snapshots:** If a template change is intentional, developers must run `npm run test:update` to overwrite the baseline `__snapshots__`.\n\n## 3. External API mocking\nTo ensure tests run sub-second and deterministically without requiring real AWS credentials, we intercept network boundaries:\n* **AWS Secrets Manager:** `tests/secrets.test.js` uses Vitest's `vi.hoisted()` and `vi.mock()` to intercept `@aws-sdk/client-secrets-manager` (plus an injected ECS client for the restart path). This verifies push/pull/audit payload handling, key-change detection, and network exceptions (like `ResourceNotFoundException`) completely offline.\n* **ECS & CloudWatch Logs:** `tests/diagnose.test.js` injects mock ECS/CloudWatch clients to verify failure analysis (stopped reasons, exit codes, log extraction) and behavior contracts — e.g., expired sessions (`UnrecognizedClientException`) exit gracefully with code 1, and unrecognized `secrets push` filenames fall back to `.env` with a warning.\n* **Telemetry:** PostHog tracking is mocked to prevent test executions from polluting production analytics.\n\n## 4. Continuous integration & execution validation (CI)\nWhile Vitest proves the CLI generates the *correct* files, GitHub Actions proves those files *actually work*. Unit and snapshot tests are gated via `.github/workflows/test.yml`; live template compilation is gated via `.github/workflows/iac-validation.yml`.\n* **Phase 1 (Generation):** Vitest runs unit and snapshot tests to verify the CLI contract.\n* **Phase 2 (Static Application Security Testing - SAST):** CI runs a pinned Trivy filesystem scan (`aquasecurity/trivy-action` by SHA) against each generated project directory, writing advisory `trivy-fs-results.txt` reports (`HIGH,CRITICAL`, `exit-code: 0`) instead of failing the build.\n* **Phase 3 (IaC Validation):** The `iac-validation` matrix workflow scaffolds all 10 supported frameworks headlessly (`--headless --preconfigured`), then runs `terraform init -backend=false` + `terraform validate`, `tflint`, the advisory filesystem scan, a stripped-Dockerfile `docker build`, and an advisory container-image scan (`trivy-image-results.txt`).\n* **Phase 4 (Release gate):** `.github/workflows/publish.yml` reuses `iac-validation.yml` via `workflow_call` as a `validate` job; `build-and-publish` has `needs: [validate]`, so NPM publishing on release is blocked until the full matrix passes.","src/content/docs/testing-strategy.md","b464ce1d26f44384",{"html":1181,"metadata":1182},"\u003Cp>To ensure zero regressions in infrastructure generation and safe local execution, \u003Ccode dir=\"auto\">deploy-stack\u003C/code> relies on a multi-layered testing strategy split between fast local snapshots and rigid CI/CD validation.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"1-unit--argument-testing\">1. Unit & argument testing\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#1-unit--argument-testing\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “1. Unit & argument testing”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>We use pure Node.js unit tests (via Vitest) to validate the CLI argument parser (\u003Ccode dir=\"auto\">src/core/parser.js\u003C/code>). This ensures that flags (like \u003Ccode dir=\"auto\">--headless\u003C/code> or \u003Ccode dir=\"auto\">--no-telemetry\u003C/code>) are routed correctly and never hijack positional arguments like file paths.\u003C/p>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"15-ecosystem-integration-contracts\">1.5. Ecosystem integration contracts\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#15-ecosystem-integration-contracts\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “1.5. Ecosystem integration contracts”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>Because \u003Ccode dir=\"auto\">deploy-stack\u003C/code> acts as the underlying engine for ecosystem wrappers (e.g., \u003Ccode dir=\"auto\">nest-deploy-stack\u003C/code>, \u003Ccode dir=\"auto\">cookiecutter-fastapi\u003C/code>), we strictly test execution flags that bypass interactive prompts (written contract: \u003Ccode dir=\"auto\">specs/integration-suite.md\u003C/code>, enforced by \u003Ccode dir=\"auto\">tests/headless.test.js\u003C/code>):\u003C/p>\n\u003Cul>\n\u003Cli>\u003Cstrong>Headless Validation:\u003C/strong> Vitest deep-mocks \u003Ccode dir=\"auto\">@clack/prompts\u003C/code> — the only interactive prompt library the CLI uses — and asserts that when \u003Ccode dir=\"auto\">--headless\u003C/code> and \u003Ccode dir=\"auto\">--preconfigured\u003C/code> are passed (e.g., \u003Ccode dir=\"auto\">--framework=nestjs --port=3000\u003C/code>), none of its prompt functions (\u003Ccode dir=\"auto\">text\u003C/code>, \u003Ccode dir=\"auto\">select\u003C/code>, \u003Ccode dir=\"auto\">multiselect\u003C/code>, \u003Ccode dir=\"auto\">confirm\u003C/code>, \u003Ccode dir=\"auto\">group\u003C/code>) ever fire and no interactive warnings are thrown. The suite also asserts flag values win over interactive defaults in the generated \u003Ccode dir=\"auto\">terraform/main.tf\u003C/code> and \u003Ccode dir=\"auto\">Dockerfile\u003C/code>, running end to end inside a temp directory so no files pollute the repo. This guarantees stability for automated ecosystem integrations.\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"2-infrastructure-snapshot-harness-the-static-contract\">2. Infrastructure snapshot harness (the static contract)\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#2-infrastructure-snapshot-harness-the-static-contract\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “2. Infrastructure snapshot harness (the static contract)”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>Because \u003Ccode dir=\"auto\">deploy-stack\u003C/code> generates highly dynamic Terraform (\u003Ccode dir=\"auto\">.tf\u003C/code>), GitHub Actions (\u003Ccode dir=\"auto\">.yml\u003C/code>), and \u003Ccode dir=\"auto\">Dockerfile\u003C/code> configurations, we use \u003Cstrong>Vitest Snapshots\u003C/strong> to lock in the expected text outputs.\u003C/p>\n\u003Cul>\n\u003Cli>\u003Cstrong>The Matrix:\u003C/strong> The test suite generates dummy projects across 11 architectural topologies (including Django, Rails, Go, Nuxt, Next.js, SvelteKit, and Vercel/Heroku migrations).\u003C/li>\n\u003Cli>\u003Cstrong>Negative Testing:\u003C/strong> The suite explicitly checks for the \u003Cem>absence\u003C/em> of files (e.g., ensuring \u003Ccode dir=\"auto\">database.tf\u003C/code> or \u003Ccode dir=\"auto\">worker.tf\u003C/code> are not generated for static sites).\u003C/li>\n\u003Cli>\u003Cstrong>Updating Snapshots:\u003C/strong> If a template change is intentional, developers must run \u003Ccode dir=\"auto\">npm run test:update\u003C/code> to overwrite the baseline \u003Ccode dir=\"auto\">__snapshots__\u003C/code>.\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"3-external-api-mocking\">3. External API mocking\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#3-external-api-mocking\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “3. External API mocking”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>To ensure tests run sub-second and deterministically without requiring real AWS credentials, we intercept network boundaries:\u003C/p>\n\u003Cul>\n\u003Cli>\u003Cstrong>AWS Secrets Manager:\u003C/strong> \u003Ccode dir=\"auto\">tests/secrets.test.js\u003C/code> uses Vitest’s \u003Ccode dir=\"auto\">vi.hoisted()\u003C/code> and \u003Ccode dir=\"auto\">vi.mock()\u003C/code> to intercept \u003Ccode dir=\"auto\">@aws-sdk/client-secrets-manager\u003C/code> (plus an injected ECS client for the restart path). This verifies push/pull/audit payload handling, key-change detection, and network exceptions (like \u003Ccode dir=\"auto\">ResourceNotFoundException\u003C/code>) completely offline.\u003C/li>\n\u003Cli>\u003Cstrong>ECS & CloudWatch Logs:\u003C/strong> \u003Ccode dir=\"auto\">tests/diagnose.test.js\u003C/code> injects mock ECS/CloudWatch clients to verify failure analysis (stopped reasons, exit codes, log extraction) and behavior contracts — e.g., expired sessions (\u003Ccode dir=\"auto\">UnrecognizedClientException\u003C/code>) exit gracefully with code 1, and unrecognized \u003Ccode dir=\"auto\">secrets push\u003C/code> filenames fall back to \u003Ccode dir=\"auto\">.env\u003C/code> with a warning.\u003C/li>\n\u003Cli>\u003Cstrong>Telemetry:\u003C/strong> PostHog tracking is mocked to prevent test executions from polluting production analytics.\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"sl-heading-wrapper level-h2\">\u003Ch2 id=\"4-continuous-integration--execution-validation-ci\">4. Continuous integration & execution validation (CI)\u003C/h2>\u003Ca class=\"sl-anchor-link\" href=\"#4-continuous-integration--execution-validation-ci\">\u003Cspan aria-hidden=\"true\" class=\"sl-anchor-icon\">\u003Csvg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\u003Cpath d=\"m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42\">\u003C/path>\u003C/svg>\u003C/span>\u003Cspan class=\"sr-only\" data-pagefind-ignore>Section titled “4. Continuous integration & execution validation (CI)”\u003C/span>\u003C/a>\u003C/div>\n\u003Cp>While Vitest proves the CLI generates the \u003Cem>correct\u003C/em> files, GitHub Actions proves those files \u003Cem>actually work\u003C/em>. Unit and snapshot tests are gated via \u003Ccode dir=\"auto\">.github/workflows/test.yml\u003C/code>; live template compilation is gated via \u003Ccode dir=\"auto\">.github/workflows/iac-validation.yml\u003C/code>.\u003C/p>\n\u003Cul>\n\u003Cli>\u003Cstrong>Phase 1 (Generation):\u003C/strong> Vitest runs unit and snapshot tests to verify the CLI contract.\u003C/li>\n\u003Cli>\u003Cstrong>Phase 2 (Static Application Security Testing - SAST):\u003C/strong> CI runs a pinned Trivy filesystem scan (\u003Ccode dir=\"auto\">aquasecurity/trivy-action\u003C/code> by SHA) against each generated project directory, writing advisory \u003Ccode dir=\"auto\">trivy-fs-results.txt\u003C/code> reports (\u003Ccode dir=\"auto\">HIGH,CRITICAL\u003C/code>, \u003Ccode dir=\"auto\">exit-code: 0\u003C/code>) instead of failing the build.\u003C/li>\n\u003Cli>\u003Cstrong>Phase 3 (IaC Validation):\u003C/strong> The \u003Ccode dir=\"auto\">iac-validation\u003C/code> matrix workflow scaffolds all 10 supported frameworks headlessly (\u003Ccode dir=\"auto\">--headless --preconfigured\u003C/code>), then runs \u003Ccode dir=\"auto\">terraform init -backend=false\u003C/code> + \u003Ccode dir=\"auto\">terraform validate\u003C/code>, \u003Ccode dir=\"auto\">tflint\u003C/code>, the advisory filesystem scan, a stripped-Dockerfile \u003Ccode dir=\"auto\">docker build\u003C/code>, and an advisory container-image scan (\u003Ccode dir=\"auto\">trivy-image-results.txt\u003C/code>).\u003C/li>\n\u003Cli>\u003Cstrong>Phase 4 (Release gate):\u003C/strong> \u003Ccode dir=\"auto\">.github/workflows/publish.yml\u003C/code> reuses \u003Ccode dir=\"auto\">iac-validation.yml\u003C/code> via \u003Ccode dir=\"auto\">workflow_call\u003C/code> as a \u003Ccode dir=\"auto\">validate\u003C/code> job; \u003Ccode dir=\"auto\">build-and-publish\u003C/code> has \u003Ccode dir=\"auto\">needs: [validate]\u003C/code>, so NPM publishing on release is blocked until the full matrix passes.\u003C/li>\n\u003C/ul>\n",{"headings":1183,"localImagePaths":1199,"remoteImagePaths":1200,"frontmatter":1201,"imagePaths":1202},[1184,1187,1190,1193,1196],{"depth":22,"slug":1185,"text":1186},"1-unit--argument-testing","1. Unit & argument testing",{"depth":22,"slug":1188,"text":1189},"15-ecosystem-integration-contracts","1.5. Ecosystem integration contracts",{"depth":22,"slug":1191,"text":1192},"2-infrastructure-snapshot-harness-the-static-contract","2. Infrastructure snapshot harness (the static contract)",{"depth":22,"slug":1194,"text":1195},"3-external-api-mocking","3. External API mocking",{"depth":22,"slug":1197,"text":1198},"4-continuous-integration--execution-validation-ci","4. Continuous integration & execution validation (CI)",[],[],{"title":1172,"description":1173},[],"meta::meta",["Map",1205,1206,1207,1208,1209,1210],"astro-config-digest","{\"root\":{},\"srcDir\":{},\"publicDir\":{},\"outDir\":{},\"cacheDir\":{},\"site\":\"https://anton-codes-iac.github.io\",\"compressHTML\":\"jsx\",\"base\":\"/deploy-stack\",\"trailingSlash\":\"ignore\",\"output\":\"static\",\"scopedStyleStrategy\":\"where\",\"build\":{\"format\":\"directory\",\"client\":{},\"server\":{},\"assets\":\"_astro\",\"serverEntry\":\"entry.mjs\",\"redirects\":true,\"inlineStylesheets\":\"auto\",\"concurrency\":1},\"server\":{\"open\":false,\"host\":\"127.0.0.1\",\"port\":4321,\"allowedHosts\":[\"127.0.0.1\",\"127.0.0.1\",\"127.0.0.1\",\"127.0.0.1\"]},\"redirects\":{\"/adrs/001-initial-architecture/\":\"/deploy-stack/adrs/0001-s3-native-state-locking/\"},\"image\":{\"endpoint\":{\"route\":\"/_image\"},\"service\":{\"entrypoint\":\"astro/assets/services/sharp\",\"config\":{}},\"dangerouslyProcessSVG\":false,\"domains\":[],\"remotePatterns\":[],\"responsiveStyles\":false},\"devToolbar\":{\"enabled\":true},\"markdown\":{\"syntaxHighlight\":false,\"shikiConfig\":{\"langs\":[],\"langAlias\":{},\"theme\":\"github-dark\",\"themes\":{},\"wrap\":false,\"transformers\":[]},\"remarkPlugins\":[],\"rehypePlugins\":[],\"remarkRehype\":{},\"processor\":{\"name\":\"satteri\",\"options\":{\"mdastPlugins\":[null,{\"name\":\"starlight-directives-restoration\"}],\"hastPlugins\":[null,null],\"features\":{\"directive\":true}}}},\"security\":{\"checkOrigin\":true,\"allowedDomains\":[],\"csp\":false,\"actionBodySizeLimit\":1048576,\"serverIslandBodySizeLimit\":1048576},\"env\":{\"schema\":{},\"validateSecrets\":false},\"prerenderConflictBehavior\":\"warn\",\"fetchFile\":\"fetch\",\"experimental\":{\"clientPrerender\":false,\"contentIntellisense\":false,\"chromeDevtoolsWorkspace\":false,\"incrementalBuild\":false,\"collectionStorage\":\"single-file\"},\"legacy\":{\"collectionsBackwardsCompat\":false},\"prefetch\":{\"prefetchAll\":true},\"i18n\":{\"defaultLocale\":\"en\",\"locales\":[\"en\"],\"routing\":{\"prefixDefaultLocale\":false,\"redirectToDefaultLocale\":false,\"fallbackType\":\"redirect\"}}}","astro-version","7.3.3","content-config-digest","28e7bc87492477d9"]
|