deploy-stack 0.18.0 → 0.18.2
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 +67 -6
- package/.github/workflows/publish.yml +5 -0
- package/.muserules +6 -1
- package/README.md +6 -6
- 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 +68 -0
- package/apps/docs/package.json +17 -0
- package/{docs/adr → apps/docs/src/content/docs/adrs}/0001-s3-native-state-locking.md +4 -1
- package/{docs/adr → apps/docs/src/content/docs/adrs}/0002-eject-mechanism-pure-iac.md +4 -1
- package/{docs/adr → apps/docs/src/content/docs/adrs}/0003-sync-ai-context-strategy.md +4 -1
- package/{docs/adr → apps/docs/src/content/docs/adrs}/0004-iac-driven-diagnostic-context.md +4 -1
- package/apps/docs/src/content/docs/cli/apply.md +29 -0
- package/apps/docs/src/content/docs/cli/destroy.md +29 -0
- package/apps/docs/src/content/docs/cli/diagnose.md +28 -0
- package/apps/docs/src/content/docs/cli/doctor.md +28 -0
- package/apps/docs/src/content/docs/cli/eject.md +29 -0
- package/apps/docs/src/content/docs/cli/init.md +42 -0
- package/apps/docs/src/content/docs/cli/secrets.md +36 -0
- package/apps/docs/src/content/docs/cli/sync-ai.md +27 -0
- package/apps/docs/src/content/docs/guides/aws-credentials.md +68 -0
- package/apps/docs/src/content/docs/guides/cicd-pipeline.md +46 -0
- package/{docs → apps/docs/src/content/docs}/guides/database-connections.md +4 -1
- package/apps/docs/src/content/docs/guides/docker-compose.md +37 -0
- package/apps/docs/src/content/docs/guides/dockerfiles.md +46 -0
- package/{docs → apps/docs/src/content/docs}/guides/ephemeral-pr-previews.md +4 -1
- package/{docs → apps/docs/src/content/docs/guides}/examples.md +15 -8
- package/apps/docs/src/content/docs/guides/frameworks.md +88 -0
- package/{docs → apps/docs/src/content/docs}/guides/headless.md +4 -1
- package/apps/docs/src/content/docs/guides/rerun-init.md +43 -0
- package/{docs → apps/docs/src/content/docs}/guides/secrets-management.md +4 -1
- package/apps/docs/src/content/docs/index.mdx +36 -0
- package/{docs/migration → apps/docs/src/content/docs/migrations}/astro-vercel-to-aws.md +4 -1
- package/{docs/migration → apps/docs/src/content/docs/migrations}/heroku-procfile-to-aws.md +4 -1
- package/{docs/migration → apps/docs/src/content/docs/migrations}/nextjs-vercel-to-aws.md +4 -1
- package/{docs/migration → apps/docs/src/content/docs/migrations}/sveltekit-vercel-to-aws.md +4 -1
- package/{docs/ROADMAP.md → apps/docs/src/content/docs/roadmap.md} +5 -2
- package/{docs → apps/docs/src/content/docs}/testing-strategy.md +13 -9
- 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 +1 -1
- package/package.json +7 -2
- package/src/commands/diagnose.js +21 -7
- package/src/commands/init.js +5 -5
- package/src/commands/secrets.js +23 -8
- package/src/core/telemetry.js +8 -5
- package/src/utils/aws.js +16 -0
- package/src/utils/generator.js +6 -5
- package/src/utils/prompts.js +1 -1
- package/templates/docker/nestjs.Dockerfile +15 -3
- package/templates/docker/svelte.Dockerfile +4 -2
- package/templates/terraform/backend.tf +14 -1
- package/templates/terraform/secrets.tf +0 -6
- package/tests/__snapshots__/generator.test.js.snap +4 -2
- package/tests/diagnose.test.js +2 -2
- package/tests/secrets.test.js +29 -0
- package/docs/frameworks.md +0 -34
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[["Map",1,2,783,784],"docs",["Map",3,4,45,46,73,74,101,102,126,127,154,155,179,180,201,202,224,225,247,248,271,272,297,298,320,321,354,355,390,391,418,419,455,456,488,489,516,517,549,550,577,578,611,612,642,643,668,669,699,700,730,731,757,758],"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","When deploying infrastructure via Terraform across local developer workstations and automated CI/CD pipelines, remote state management is required to prevent ra",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","40345e0a6541eb96",{"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","deploy-stack abstracts away the complexity of writing raw Terraform for ECS Fargate, ALBs, CloudFront, OIDC, and Secrets Manager. However, a primary reason seni",[],{"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","9c1475e3f4529f56",{"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","Modern engineering teams heavily utilize AI coding assistants (Cursor, GitHub Copilot, Windsurf, Claude Code, etc.) in their local IDEs. However, when dealing w",[],{"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","7597d897f1f03e9e",{"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)","To provide a seamless developer experience, the deploy-stack diagnose command needs to automatically fetch CloudWatch logs and ECS task failures without requiri",[],{"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","df79b4dfe0f766be",{"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},[],"cli/apply",{"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},"apply","Provision or update your AWS infrastructure with Terraform.",[],{"hidden":12,"attrs":133},{},"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- 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","d6a07f4cd020c9bc",{"html":138,"metadata":139},"\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>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=\"/_astro/ec.w36nc.css\">\u003Cscript type=\"module\" src=\"/_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":140,"localImagePaths":150,"remoteImagePaths":151,"frontmatter":152,"imagePaths":153},[141,144,147],{"depth":22,"slug":142,"text":143},"what-it-does","What it does",{"depth":22,"slug":145,"text":146},"usage","Usage",{"depth":22,"slug":148,"text":149},"flags","Flags",[],[],{"title":129,"description":130},[],"cli/destroy",{"id":154,"data":156,"body":162,"filePath":163,"digest":164,"rendered":165},{"title":157,"description":158,"editUrl":8,"head":159,"template":10,"sidebar":160,"pagefind":8,"draft":12},"destroy","Tear down all AWS resources provisioned for this project.",[],{"hidden":12,"attrs":161},{},"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](/cli/apply/)\n- [doctor](/cli/doctor/)","src/content/docs/cli/destroy.md","5b15bd3bde59fb97",{"html":166,"metadata":167},"\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=\"/_astro/ec.w36nc.css\">\u003Cscript type=\"module\" src=\"/_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=\"/cli/apply/\">apply\u003C/a>\u003C/li>\n\u003Cli>\u003Ca href=\"/cli/doctor/\">doctor\u003C/a>\u003C/li>\n\u003C/ul>\n",{"headings":168,"localImagePaths":175,"remoteImagePaths":176,"frontmatter":177,"imagePaths":178},[169,170,171,172],{"depth":22,"slug":142,"text":143},{"depth":22,"slug":145,"text":146},{"depth":22,"slug":148,"text":149},{"depth":22,"slug":173,"text":174},"see-also","See also",[],[],{"title":157,"description":158},[],"cli/diagnose",{"id":179,"data":181,"body":187,"filePath":188,"digest":189,"rendered":190},{"title":182,"description":183,"editUrl":8,"head":184,"template":10,"sidebar":185,"pagefind":8,"draft":12},"diagnose","Diagnose failing ECS deployments from logs and task state.",[],{"hidden":12,"attrs":186},{},"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 `region` in `terraform/main.tf` (default `us-east-1`), overridable via `AWS_REGION`; 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](/guides/aws-credentials/).\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.","src/content/docs/cli/diagnose.md","6b58970b73c1b501",{"html":191,"metadata":192},"\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\">region\u003C/code> in \u003Ccode dir=\"auto\">terraform/main.tf\u003C/code> (default \u003Ccode dir=\"auto\">us-east-1\u003C/code>), overridable via \u003Ccode dir=\"auto\">AWS_REGION\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=\"/guides/aws-credentials/\">AWS credentials guide\u003C/a>.\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=\"/_astro/ec.w36nc.css\">\u003Cscript type=\"module\" src=\"/_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",{"headings":193,"localImagePaths":197,"remoteImagePaths":198,"frontmatter":199,"imagePaths":200},[194,195,196],{"depth":22,"slug":142,"text":143},{"depth":22,"slug":145,"text":146},{"depth":22,"slug":148,"text":149},[],[],{"title":182,"description":183},[],"cli/doctor",{"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},"doctor","Check that required tools are installed before provisioning.",[],{"hidden":12,"attrs":208},{},"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)](/cli/init/)\n- [apply](/cli/apply/)","src/content/docs/cli/doctor.md","845c83f70e6a12d5",{"html":213,"metadata":214},"\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=\"/_astro/ec.w36nc.css\">\u003Cscript type=\"module\" src=\"/_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=\"/cli/init/\">npx deploy-stack (init)\u003C/a>\u003C/li>\n\u003Cli>\u003Ca href=\"/cli/apply/\">apply\u003C/a>\u003C/li>\n\u003C/ul>\n",{"headings":215,"localImagePaths":220,"remoteImagePaths":221,"frontmatter":222,"imagePaths":223},[216,217,218,219],{"depth":22,"slug":142,"text":143},{"depth":22,"slug":145,"text":146},{"depth":22,"slug":148,"text":149},{"depth":22,"slug":173,"text":174},[],[],{"title":204,"description":205},[],"cli/eject",{"id":224,"data":226,"body":232,"filePath":233,"digest":234,"rendered":235},{"title":227,"description":228,"editUrl":8,"head":229,"template":10,"sidebar":230,"pagefind":8,"draft":12},"eject","Decouple your project from deploy-stack into vanilla Terraform.",[],{"hidden":12,"attrs":231},{},"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](/cli/apply/)\n- [destroy](/cli/destroy/)","src/content/docs/cli/eject.md","c6902f3dd1a617d5",{"html":236,"metadata":237},"\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=\"/_astro/ec.w36nc.css\">\u003Cscript type=\"module\" src=\"/_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=\"/cli/apply/\">apply\u003C/a>\u003C/li>\n\u003Cli>\u003Ca href=\"/cli/destroy/\">destroy\u003C/a>\u003C/li>\n\u003C/ul>\n",{"headings":238,"localImagePaths":243,"remoteImagePaths":244,"frontmatter":245,"imagePaths":246},[239,240,241,242],{"depth":22,"slug":142,"text":143},{"depth":22,"slug":145,"text":146},{"depth":22,"slug":148,"text":149},{"depth":22,"slug":173,"text":174},[],[],{"title":227,"description":228},[],"cli/init",{"id":247,"data":249,"body":255,"filePath":256,"digest":257,"rendered":258},{"title":250,"description":251,"editUrl":8,"head":252,"template":10,"sidebar":253,"pagefind":8,"draft":12},"Initializing Project (npx deploy-stack)","Scaffold production-ready AWS infrastructure and CI/CD pipelines.",[],{"hidden":12,"attrs":254},{},"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`, `nextjs`, `nuxt`, `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\nSee the [Headless Mode guide](/guides/headless/) for automation examples.","src/content/docs/cli/init.md","4185805c935e773a",{"html":259,"metadata":260},"\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=\"/_astro/ec.w36nc.css\">\u003Cscript type=\"module\" src=\"/_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\">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>, \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>See the \u003Ca href=\"/guides/headless/\">Headless Mode guide\u003C/a> for automation examples.\u003C/p>\n",{"headings":261,"localImagePaths":267,"remoteImagePaths":268,"frontmatter":269,"imagePaths":270},[262,263,264],{"depth":22,"slug":142,"text":143},{"depth":22,"slug":145,"text":146},{"depth":22,"slug":265,"text":266},"headless-flags","Headless flags",[],[],{"title":250,"description":251},[],"cli/secrets",{"id":271,"data":273,"body":279,"filePath":280,"digest":281,"rendered":282},{"title":274,"description":275,"editUrl":8,"head":276,"template":10,"sidebar":277,"pagefind":8,"draft":12},"secrets push","Push local environment variables to AWS Secrets Manager.",[],{"hidden":12,"attrs":278},{},"Upload your `.env` file to the Secrets Manager vault provisioned for this project, so your deployed app can read the values at runtime.\n\n## What it does\n\n- Reads and parses a local env file (defaults to `.env`) and pushes every key as a single JSON secret string to the `\u003Cproject-name>-secrets` vault via `UpdateSecretCommand`. This is the value of the command: no manual AWS console edits, and your app picks up the values on the next deployment.\n- Resolves the AWS region from the `region` setting in `terraform/main.tf`, falling back to `AWS_REGION` or your AWS profile default.\n- Writes the pushed key names to `terraform/secret_keys.json` so the Terraform configuration and CI redeploy know which variables exist. Commit this file and push to trigger a deployment with the new variables.\n- Emits a `secrets_pushed` telemetry event. Exits non-zero on failure.\n\n## Usage\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## Flags\n\nThis command accepts no CLI flags.\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, the 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](/guides/aws-credentials/).\n\n## See also\n\n- [Secrets management guide](/guides/secrets-management/)\n- [apply](/cli/apply/)","src/content/docs/cli/secrets.md","be9b34af27acbba9",{"html":283,"metadata":284},"\u003Cp>Upload your \u003Ccode dir=\"auto\">.env\u003C/code> file to the Secrets Manager vault provisioned for this project, so your deployed app can read the values at runtime.\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>Reads and parses a local env file (defaults to \u003Ccode dir=\"auto\">.env\u003C/code>) and pushes every key as a single JSON secret string to the \u003Ccode dir=\"auto\"><project-name>-secrets\u003C/code> vault via \u003Ccode dir=\"auto\">UpdateSecretCommand\u003C/code>. This is the value of the command: no manual AWS console edits, and your app picks up the values on the next deployment.\u003C/li>\n\u003Cli>Resolves the AWS region 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.\u003C/li>\n\u003Cli>Writes the pushed key names to \u003Ccode dir=\"auto\">terraform/secret_keys.json\u003C/code> so the Terraform configuration and CI redeploy know which variables exist. Commit this file and push to trigger a deployment with the new variables.\u003C/li>\n\u003Cli>Emits a \u003Ccode dir=\"auto\">secrets_pushed\u003C/code> telemetry event. Exits non-zero on failure.\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=\"/_astro/ec.w36nc.css\">\u003Cscript type=\"module\" src=\"/_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\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=\"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, the 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=\"/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=\"/guides/secrets-management/\">Secrets management guide\u003C/a>\u003C/li>\n\u003Cli>\u003Ca href=\"/cli/apply/\">apply\u003C/a>\u003C/li>\n\u003C/ul>\n",{"headings":285,"localImagePaths":293,"remoteImagePaths":294,"frontmatter":295,"imagePaths":296},[286,287,288,289,292],{"depth":22,"slug":142,"text":143},{"depth":22,"slug":145,"text":146},{"depth":22,"slug":148,"text":149},{"depth":22,"slug":290,"text":291},"prerequisites","Prerequisites",{"depth":22,"slug":173,"text":174},[],[],{"title":274,"description":275},[],"cli/sync-ai",{"id":297,"data":299,"body":305,"filePath":306,"digest":307,"rendered":308},{"title":300,"description":301,"editUrl":8,"head":302,"template":10,"sidebar":303,"pagefind":8,"draft":12},"sync-ai","Regenerate AI assistant rules for an existing project.",[],{"hidden":12,"attrs":304},{},"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)](/cli/init/)","src/content/docs/cli/sync-ai.md","2e778c74c657e2ab",{"html":309,"metadata":310},"\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=\"/_astro/ec.w36nc.css\">\u003Cscript type=\"module\" src=\"/_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=\"/cli/init/\">npx deploy-stack (init)\u003C/a>\u003C/li>\n\u003C/ul>\n",{"headings":311,"localImagePaths":316,"remoteImagePaths":317,"frontmatter":318,"imagePaths":319},[312,313,314,315],{"depth":22,"slug":142,"text":143},{"depth":22,"slug":145,"text":146},{"depth":22,"slug":148,"text":149},{"depth":22,"slug":173,"text":174},[],[],{"title":300,"description":301},[],"guides/aws-credentials",{"id":320,"data":322,"body":328,"filePath":329,"digest":330,"rendered":331},{"title":323,"description":324,"editUrl":8,"head":325,"template":10,"sidebar":326,"pagefind":8,"draft":12},"Troubleshooting AWS Credentials & Authentication","Troubleshooting AWS authentication, expired tokens, and SSO logins.",[],{"hidden":12,"attrs":327},{},"`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","afde8a4b567063a1",{"html":332,"metadata":333},"\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=\"/_astro/ec.w36nc.css\">\u003Cscript type=\"module\" src=\"/_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":334,"localImagePaths":350,"remoteImagePaths":351,"frontmatter":352,"imagePaths":353},[335,338,341,344,347],{"depth":22,"slug":336,"text":337},"1-quick-refresh-by-setup-type","1. Quick Refresh by Setup Type",{"depth":35,"slug":339,"text":340},"a-aws-iam-identity-center-aws-sso","A. AWS IAM Identity Center (AWS SSO)",{"depth":35,"slug":342,"text":343},"b-standard-long-lived-access-keys-awscredentials","B. Standard Long-Lived Access Keys (~/.aws/credentials)",{"depth":35,"slug":345,"text":346},"c-temporary-session-tokens-aws_session_token","C. Temporary Session Tokens (AWS_SESSION_TOKEN)",{"depth":22,"slug":348,"text":349},"2-common-error-references","2. Common Error References",[],[],{"title":323,"description":324},[],"guides/cicd-pipeline",{"id":354,"data":356,"body":363,"filePath":364,"digest":365,"rendered":366},{"title":357,"description":358,"editUrl":8,"head":359,"template":10,"sidebar":360,"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":361,"hidden":12,"attrs":362},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](/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](/guides/dockerfiles/)).\n\n## Related workflows\n\n- `preview.yml` / `teardown.yml` exist only when ephemeral PR previews are enabled. See [Ephemeral PR Previews](/guides/ephemeral-pr-previews/).\n- Secrets are injected at deploy time from AWS Secrets Manager, never from the repo. See [Secrets Management](/guides/secrets-management/).","src/content/docs/guides/cicd-pipeline.md","069e7ffe7e4b5457",{"html":367,"metadata":368},"\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=\"/_astro/ec.w36nc.css\">\u003Cscript type=\"module\" src=\"/_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=\"/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=\"/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=\"/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=\"/guides/secrets-management/\">Secrets Management\u003C/a>.\u003C/li>\n\u003C/ul>\n",{"headings":369,"localImagePaths":385,"remoteImagePaths":386,"frontmatter":387,"imagePaths":389},[370,373,376,379,382],{"depth":22,"slug":371,"text":372},"when-it-runs","When it runs",{"depth":22,"slug":374,"text":375},"no-stored-aws-keys","No stored AWS keys",{"depth":22,"slug":377,"text":378},"the-five-stages","The five stages",{"depth":22,"slug":380,"text":381},"why-you-see-a-503-first","Why you see a 503 first",{"depth":22,"slug":383,"text":384},"related-workflows","Related workflows",[],[],{"title":357,"description":358,"sidebar":388},{"order":361},[],"guides/database-connections",{"id":390,"data":392,"body":398,"filePath":399,"digest":400,"rendered":401},{"title":393,"description":394,"editUrl":8,"head":395,"template":10,"sidebar":396,"pagefind":8,"draft":12},"Managed Database Connections","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 PostgreS",[],{"hidden":12,"attrs":397},{},"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","4fcb20411e58e4bd",{"html":402,"metadata":403},"\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":404,"localImagePaths":414,"remoteImagePaths":415,"frontmatter":416,"imagePaths":417},[405,408,411],{"depth":22,"slug":406,"text":407},"zero-trust-architecture","Zero-Trust Architecture",{"depth":22,"slug":409,"text":410},"auto-injected-environment-variables","Auto-Injected Environment Variables",{"depth":22,"slug":412,"text":413},"running-database-migrations","Running Database Migrations",[],[],{"title":393,"description":394},[],"guides/docker-compose",{"id":418,"data":420,"body":427,"filePath":428,"digest":429,"rendered":430},{"title":421,"description":422,"editUrl":8,"head":423,"template":10,"sidebar":424,"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":425,"hidden":12,"attrs":426},4,{},"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](/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](/guides/cicd-pipeline/).\n- Runtime secrets still belong in AWS Secrets Manager, not in Compose `environment:`. See [Secrets Management](/guides/secrets-management/).\n\n## See also\n\n- [Supported Frameworks](/guides/frameworks/) for detection and defaults.\n- [Dockerfiles & the container contract](/guides/dockerfiles/) for runtime requirements.","src/content/docs/guides/docker-compose.md","e0fbb12f3002662e",{"html":431,"metadata":432},"\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=\"/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=\"/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=\"/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=\"/guides/frameworks/\">Supported Frameworks\u003C/a> for detection and defaults.\u003C/li>\n\u003Cli>\u003Ca href=\"/guides/dockerfiles/\">Dockerfiles & the container contract\u003C/a> for runtime requirements.\u003C/li>\n\u003C/ul>\n",{"headings":433,"localImagePaths":450,"remoteImagePaths":451,"frontmatter":452,"imagePaths":454},[434,437,440,443,446,449],{"depth":22,"slug":435,"text":436},"file-discovery","File discovery",{"depth":22,"slug":438,"text":439},"which-service-is-web","Which service is “web”",{"depth":22,"slug":441,"text":442},"port-mapping","Port mapping",{"depth":22,"slug":444,"text":445},"environment-and-command-injection","Environment and command injection",{"depth":22,"slug":447,"text":448},"what-this-means-in-practice","What this means in practice",{"depth":22,"slug":173,"text":174},[],[],{"title":421,"description":422,"sidebar":453},{"order":425},[],"guides/dockerfiles",{"id":455,"data":457,"body":463,"filePath":464,"digest":465,"rendered":466},{"title":458,"description":459,"editUrl":8,"head":460,"template":10,"sidebar":461,"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":35,"hidden":12,"attrs":462},{},"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](/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](/guides/secrets-management/)).\n\n## See also\n\n- [CI/CD Pipeline & First Deploy](/guides/cicd-pipeline/) for how the image is built and rolled out.\n- [diagnose](/cli/diagnose/) for reading ECS failure output when the contract is broken.","src/content/docs/guides/dockerfiles.md","67d3dd8a5124b2b6",{"html":467,"metadata":468},"\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=\"/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=\"/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=\"/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=\"/cli/diagnose/\">diagnose\u003C/a> for reading ECS failure output when the contract is broken.\u003C/li>\n\u003C/ul>\n",{"headings":469,"localImagePaths":483,"remoteImagePaths":484,"frontmatter":485,"imagePaths":487},[470,473,476,479,482],{"depth":22,"slug":471,"text":472},"the-container-contract","The container contract",{"depth":22,"slug":474,"text":475},"per-framework-prerequisites","Per-framework prerequisites",{"depth":22,"slug":477,"text":478},"what-command-runs-your-app","What command runs your app",{"depth":22,"slug":480,"text":481},"keeping-images-lean","Keeping images lean",{"depth":22,"slug":173,"text":174},[],[],{"title":458,"description":459,"sidebar":486},{"order":35},[],"guides/ephemeral-pr-previews",{"id":488,"data":490,"body":496,"filePath":497,"digest":498,"rendered":499},{"title":491,"description":492,"editUrl":8,"head":493,"template":10,"sidebar":494,"pagefind":8,"draft":12},"Ephemeral PR Previews","When enabled, deploy-stack automatically configures your GitHub Actions pipeline to spin up isolated, temporary AWS environments every time a developer opens a ",[],{"hidden":12,"attrs":495},{},"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","c33e74b3605701e8",{"html":500,"metadata":501},"\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":502,"localImagePaths":512,"remoteImagePaths":513,"frontmatter":514,"imagePaths":515},[503,506,509],{"depth":35,"slug":504,"text":505},"️-how-it-works","🏗️ How it Works",{"depth":35,"slug":507,"text":508},"️-the-rule-of-thumb-should-i-enable-this","⚖️ The Rule of Thumb: Should I enable this?",{"depth":35,"slug":510,"text":511},"-aws-cost-implications","💰 AWS Cost Implications",[],[],{"title":491,"description":492},[],"guides/frameworks",{"id":516,"data":518,"body":524,"filePath":525,"digest":526,"rendered":527},{"title":519,"description":520,"editUrl":8,"head":521,"template":10,"sidebar":522,"pagefind":8,"draft":12},"Supported Frameworks & Detection","Which frameworks deploy-stack detects, the signals it looks for, and the valid --framework ids.",[],{"order":22,"hidden":12,"attrs":523},{},"During setup, `deploy-stack` inspects your repo (`src/utils/detector.js`) and preselects a framework preset. This page documents the detection signals in precedence order, the preset ids accepted by `--framework`, and the per-framework defaults that flow into your infrastructure.\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## 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## See also\n\n- [Dockerfiles & the container contract](/guides/dockerfiles/) for what your app must do at runtime.\n- [Headless Mode](/guides/headless/) for automating framework selection.","src/content/docs/guides/frameworks.md","c17e9dfff8bd211c",{"html":528,"metadata":529},"\u003Cp>During setup, \u003Ccode dir=\"auto\">deploy-stack\u003C/code> inspects your repo (\u003Ccode dir=\"auto\">src/utils/detector.js\u003C/code>) and preselects a framework preset. This page documents the detection signals in precedence order, the preset ids accepted by \u003Ccode dir=\"auto\">--framework\u003C/code>, and the per-framework defaults that flow into your infrastructure.\u003C/p>\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=\"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=\"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=\"/guides/dockerfiles/\">Dockerfiles & the container contract\u003C/a> for what your app must do at runtime.\u003C/li>\n\u003Cli>\u003Ca href=\"/guides/headless/\">Headless Mode\u003C/a> for automating framework selection.\u003C/li>\n\u003C/ul>\n",{"headings":530,"localImagePaths":544,"remoteImagePaths":545,"frontmatter":546,"imagePaths":548},[531,534,537,540,543],{"depth":22,"slug":532,"text":533},"detection-precedence","Detection precedence",{"depth":22,"slug":535,"text":536},"valid---framework-ids","Valid --framework ids",{"depth":22,"slug":538,"text":539},"per-framework-defaults","Per-framework defaults",{"depth":22,"slug":541,"text":542},"post-detection-checks","Post-detection checks",{"depth":22,"slug":173,"text":174},[],[],{"title":519,"description":520,"sidebar":547},{"order":22},[],"guides/headless",{"id":549,"data":551,"body":557,"filePath":558,"digest":559,"rendered":560},{"title":552,"description":553,"editUrl":8,"head":554,"template":10,"sidebar":555,"pagefind":8,"draft":12},"Headless Mode & Automation Guide","The deploy-stack CLI is designed to be fully automatable for CI/CD pipelines, custom scripts, Cookiecutters, and framework plugins (like vite-plugin-deploy-stac",[],{"hidden":12,"attrs":556},{},"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.\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`, `nextjs`, `nuxt`, `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```","src/content/docs/guides/headless.md","38061b2aa97a829d",{"html":561,"metadata":562},"\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.\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\">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>, \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=\"/_astro/ec.w36nc.css\">\u003Cscript type=\"module\" src=\"/_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",{"headings":563,"localImagePaths":573,"remoteImagePaths":574,"frontmatter":575,"imagePaths":576},[564,567,570],{"depth":22,"slug":565,"text":566},"required-flags","Required Flags",{"depth":22,"slug":568,"text":569},"optional-configuration-flags","Optional Configuration Flags",{"depth":22,"slug":571,"text":572},"example-usage","Example Usage",[],[],{"title":552,"description":553},[],"guides/rerun-init",{"id":577,"data":579,"body":586,"filePath":587,"digest":588,"rendered":589},{"title":580,"description":581,"editUrl":8,"head":582,"template":10,"sidebar":583,"pagefind":8,"draft":12},"Re-running Init Safely","What happens when setup finds existing files — backups, regeneration, and how to recover.",[],{"order":584,"hidden":12,"attrs":585},5,{},"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](/cli/apply/) for converging AWS after regeneration.\n- [eject](/cli/eject/) for what happens to backups on decoupling.","src/content/docs/guides/rerun-init.md","4f88050eaa9a3c83",{"html":590,"metadata":591},"\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=\"/cli/apply/\">apply\u003C/a> for converging AWS after regeneration.\u003C/li>\n\u003Cli>\u003Ca href=\"/cli/eject/\">eject\u003C/a> for what happens to backups on decoupling.\u003C/li>\n\u003C/ul>\n",{"headings":592,"localImagePaths":606,"remoteImagePaths":607,"frontmatter":608,"imagePaths":610},[593,596,599,602,605],{"depth":22,"slug":594,"text":595},"the-conflict-prompt","The conflict prompt",{"depth":22,"slug":597,"text":598},"backups-stay-local","Backups stay local",{"depth":22,"slug":600,"text":601},"what-regeneration-touches","What regeneration touches",{"depth":22,"slug":603,"text":604},"suggested-workflow","Suggested workflow",{"depth":22,"slug":173,"text":174},[],[],{"title":580,"description":581,"sidebar":609},{"order":584},[],"guides/secrets-management",{"id":611,"data":613,"body":619,"filePath":620,"digest":621,"rendered":622},{"title":614,"description":615,"editUrl":8,"head":616,"template":10,"sidebar":617,"pagefind":8,"draft":12},"Secrets Management in deploy-stack","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 Man",[],{"hidden":12,"attrs":618},{},"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.","src/content/docs/guides/secrets-management.md","a7f5c526052bc34e",{"html":623,"metadata":624},"\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=\"/_astro/ec.w36nc.css\">\u003Cscript type=\"module\" src=\"/_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",{"headings":625,"localImagePaths":638,"remoteImagePaths":639,"frontmatter":640,"imagePaths":641},[626,629,632,635],{"depth":22,"slug":627,"text":628},"the-secrets-lifecycle","The Secrets Lifecycle",{"depth":35,"slug":630,"text":631},"step-1-provision-the-vault-day-1","Step 1: Provision the Vault (Day 1)",{"depth":35,"slug":633,"text":634},"step-2-push-secrets-to-aws","Step 2: Push Secrets to AWS",{"depth":35,"slug":636,"text":637},"step-3-map-secrets-into-the-container","Step 3: Map Secrets into the Container",[],[],{"title":614,"description":615},[],"index",{"id":642,"data":644,"body":665,"filePath":666,"digest":667,"deferredRender":8},{"title":645,"description":646,"editUrl":8,"head":647,"template":648,"hero":649,"sidebar":663,"pagefind":8,"draft":12},"deploy-stack docs","Official developer portal for deploy-stack — concepts, guides, and CLI reference.",[],"splash",{"tagline":650,"actions":651},"Provision production-ready AWS infrastructure and CI/CD pipelines in seconds.",[652,659],{"text":653,"link":654,"variant":655,"icon":656},"Read the guides","/guides/headless/","primary",{"type":657,"name":658},"icon","right-arrow",{"text":660,"link":661,"variant":662},"CLI reference","/cli/init/","minimal",{"hidden":12,"attrs":664},{},"import { LinkCard, CardGrid } from '@astrojs/starlight/components';\n\n## Start here\n\n\u003CCardGrid stagger>\n \u003CLinkCard \n title=\"Deployment Guides\" \n description=\"Learn how to deploy Rails, NestJS, and SvelteKit applications.\" \n href=\"/guides/aws-credentials/\" \n />\n \u003CLinkCard \n title=\"CLI Reference\" \n description=\"Explore the full list of commands and headless flags.\" \n href=\"/cli/init/\" \n />\n \u003CLinkCard \n title=\"Architecture Decisions\" \n description=\"Read the ADRs detailing our infrastructure choices.\" \n href=\"/adrs/001-initial-architecture/\" \n />\n\u003C/CardGrid>","src/content/docs/index.mdx","2c80ac5c49f7c2b7","migration/astro-vercel-to-aws",{"id":668,"data":670,"body":676,"filePath":677,"digest":678,"rendered":679},{"title":671,"description":672,"editUrl":8,"head":673,"template":10,"sidebar":674,"pagefind":8,"draft":12},"Migrating Astro from Vercel to AWS Fargate","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 proprie",[],{"hidden":12,"attrs":675},{},"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.","src/content/docs/migration/astro-vercel-to-aws.md","666d1af7722f4741",{"html":680,"metadata":681},"\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",{"headings":682,"localImagePaths":695,"remoteImagePaths":696,"frontmatter":697,"imagePaths":698},[683,686,689,692],{"depth":22,"slug":684,"text":685},"how-to-fix","How to Fix",{"depth":35,"slug":687,"text":688},"1-install-the-node-adapter","1. Install the Node adapter",{"depth":35,"slug":690,"text":691},"2-update-astroconfigmjs","2. Update astro.config.mjs",{"depth":35,"slug":693,"text":694},"3-deploy","3. Deploy",[],[],{"title":671,"description":672},[],"migration/heroku-procfile-to-aws",{"id":699,"data":701,"body":707,"filePath":708,"digest":709,"rendered":710},{"title":702,"description":703,"editUrl":8,"head":704,"template":10,"sidebar":705,"pagefind":8,"draft":12},"Migrating from Heroku to AWS (Procfile Support)","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 C",[],{"hidden":12,"attrs":706},{},"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.","src/content/docs/migration/heroku-procfile-to-aws.md","8511cfa0ee126dd9",{"html":711,"metadata":712},"\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=\"/_astro/ec.w36nc.css\">\u003Cscript type=\"module\" src=\"/_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",{"headings":713,"localImagePaths":726,"remoteImagePaths":727,"frontmatter":728,"imagePaths":729},[714,717,720,723],{"depth":22,"slug":715,"text":716},"how-it-works","How it Works",{"depth":35,"slug":718,"text":719},"the-web-process","The web Process",{"depth":35,"slug":721,"text":722},"the-worker-process","The worker Process",{"depth":22,"slug":724,"text":725},"example","Example",[],[],{"title":702,"description":703},[],"migration/nextjs-vercel-to-aws",{"id":730,"data":732,"body":738,"filePath":739,"digest":740,"rendered":741},{"title":733,"description":734,"editUrl":8,"head":735,"template":10,"sidebar":736,"pagefind":8,"draft":12},"Migrating Next.js from Vercel to AWS Fargate","If you are seeing a warning from deploy-stack about output: 'standalone', your Next.js configuration is missing a crucial setting required for containerized env",[],{"hidden":12,"attrs":737},{},"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.","src/content/docs/migration/nextjs-vercel-to-aws.md","3afde57bc9b3aae3",{"html":742,"metadata":743},"\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=\"/_astro/ec.w36nc.css\">\u003Cscript type=\"module\" src=\"/_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",{"headings":744,"localImagePaths":753,"remoteImagePaths":754,"frontmatter":755,"imagePaths":756},[745,746,749,752],{"depth":22,"slug":684,"text":685},{"depth":35,"slug":747,"text":748},"1-update-nextconfigjs-or-mjs--cjs","1. Update next.config.js (or .mjs / .cjs)",{"depth":35,"slug":750,"text":751},"2-optional-define-a-health-check-route","2. (Optional) Define a Health Check Route",{"depth":35,"slug":693,"text":694},[],[],{"title":733,"description":734},[],"migration/sveltekit-vercel-to-aws",{"id":757,"data":759,"body":765,"filePath":766,"digest":767,"rendered":768},{"title":760,"description":761,"editUrl":8,"head":762,"template":10,"sidebar":763,"pagefind":8,"draft":12},"Migrating SvelteKit from Vercel to AWS Fargate","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 Verc",[],{"hidden":12,"attrs":764},{},"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.","src/content/docs/migration/sveltekit-vercel-to-aws.md","80974f3dd419d37e",{"html":769,"metadata":770},"\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=\"/_astro/ec.w36nc.css\">\u003Cscript type=\"module\" src=\"/_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",{"headings":771,"localImagePaths":779,"remoteImagePaths":780,"frontmatter":781,"imagePaths":782},[772,773,775,778],{"depth":22,"slug":684,"text":685},{"depth":35,"slug":687,"text":774},"1. Install the Node Adapter",{"depth":35,"slug":776,"text":777},"2-update-svelteconfigjs","2. Update svelte.config.js",{"depth":35,"slug":693,"text":694},[],[],{"title":760,"description":761},[],"meta::meta",["Map",785,786,787,788,789,790],"astro-config-digest","{\"root\":{},\"srcDir\":{},\"publicDir\":{},\"outDir\":{},\"cacheDir\":{},\"compressHTML\":\"jsx\",\"base\":\"/\",\"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\":false,\"port\":4321,\"allowedHosts\":[]},\"redirects\":{},\"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"]
|