⚡ This infrastructure and CI/CD pipeline was auto-generated by deploy-stack.
It contains a production-ready AWS ECS Fargate architecture and a zero-secret GitHub Actions deployment pipeline.
This infrastructure provisions a highly available Application Load Balancer (ALB) and an ECS Fargate container (Size: Micro (0.25 vCPU, 512MB RAM)).
- Estimated Monthly Cost: ~$25.00 / month
- Note: AWS bills by the hour. If you destroy this stack after a few hours of testing, it will cost less than $0.20.
⚠️ DISCLAIMER: This cost is a rough estimate. AWS pricing changes and varies by region. You are solely responsible for all AWS charges incurred by deploying this infrastructure. The creators ofdeploy-stackare not liable for unexpected cloud costs, compromised credentials, or runaway billing. Always monitor your AWS Billing Dashboard and set up budget alerts.
-
Initial Provisioning:
cd terraform terraform init terraform apply -
Push Secrets (Optional): If your application requires environment variables, create a local
.envfile and sync it directly to AWS Secrets Manager:npx deploy-stack secrets push .env
-
Automated CI/CD (Keyless via OIDC): Push this repository to GitHub. Your deployment pipeline uses the official deploy-stack GitHub Action and AWS IAM OpenID Connect (OIDC) to authenticate securely with temporary credentials—no long-lived AWS secret keys are required in GitHub Secrets. Every push to
mainwill automatically run your infrastructure changes, build your container, and deploy your application.
AWS only permits one GitHub Actions OIDC provider per AWS account. If terraform apply fails with an EntityAlreadyExists error regarding the OIDC provider, it indicates GitHub Actions was previously configured in this account.
The Fix:
Open terraform/oidc.tf and update the default value of create_oidc_provider to false:
variable "create_oidc_provider" {
type = bool
default = false # <--- Change this from true to false
}Re-run terraform apply to link directly to your existing provider.
If you are done testing and want to stop all AWS billing, you must destroy the infrastructure.
Run the automated teardown command from the root of your project:
npx deploy-stack destroyType yes when prompted. This will execute a safe Terraform teardown of your Load Balancer, ECS cluster, and networking components, followed by automatically emptying and deleting your remote S3 state bucket.
Before you push your code to GitHub, ensure your application is configured to run inside a Docker container and respond to AWS Load Balancer health checks.
AWS constantly pings your container to ensure it is alive. If you configured a custom health check path (e.g., /api/health) during the CLI setup, you must create that route in your application. If AWS receives a 404 Not Found, it will assume your app is broken and terminate the container.
Make sure your app returns a 200 OK at your configured path:
- Next.js (App Router): Create
app/api/health/route.tsreturning a 200 response. - Express.js: Add
app.get('/api/health', (req, res) => res.sendStatus(200)); - FastAPI/Python: Add
@app.get("/api/health")returning a 200 status. - Ruby on Rails: Rails 7.1+ includes a default
/uphealth check. EnsureRails.application.config.force_ssl = trueisn't blocking HTTP health checks from the ALB. - Django: Add a simple view in
urls.pythat returnsHttpResponse("OK", status=200)at your configured path. - Go: Add a handler to your mux:
http.HandleFunc("/api/health", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(200) }) - Nuxt 3: Create a server route at
server/routes/health.tsreturning200.
Next.js must be configured in "standalone" mode so it can bundle a minimal Node.js server. Without this, your GitHub Actions Docker build will crash.
Open next.config.js or next.config.ts in your root directory and add output: 'standalone':
import type { NextConfig } from 'next';
const nextConfig: NextConfig = {
output: 'standalone', // <--- Add this exact line
};
export default nextConfig;When running inside a Docker container, your server must bind to all network interfaces (0.0.0.0), not just localhost or 127.0.0.1. If you bind to localhost, the AWS Load Balancer will not be able to route traffic to your application.
Make sure your app is configured correctly:
- Express.js:
app.listen(port, '0.0.0.0', () => ...) - FastAPI:
uvicorn.run(app, host="0.0.0.0", port=8000) - Ruby on Rails: Bound automatically by the CLI's Puma command (
-b tcp://0.0.0.0:3000). - Django: Bound automatically by the CLI's Gunicorn command (
--bind 0.0.0.0:3000). - Go: Ensure your
ListenAndServestring looks like this:http.ListenAndServe(":8080", nil)orhttp.ListenAndServe("0.0.0.0:8080", nil). - Nuxt 3: Bound automatically via the
NITRO_HOST=0.0.0.0environment variable injected by the CLI Dockerfile.
If you are deploying a static site, your application is served via a highly optimized, unprivileged Nginx container.
- Zero-Config Build: The CLI automatically detected your framework's output folder (
dist,build, etc.) and pre-configured your Dockerfile. - Health Checks: You do not need to configure a custom
/healthroute. Nginx will automatically return a200 OKwhen AWS pings the root/index page.
If you opted to include a managed AWS RDS PostgreSQL database, the infrastructure automatically creates the database in isolated private subnets and injects the credentials into your container's environment variables.
Ensure your backend framework (Rails, Django, Go, etc.) is configured to consume these variables at runtime:
DB_HOST: The AWS RDS endpoint URLDB_PORT:5432DB_NAME: The auto-formatted database nameDB_USER: The auto-generated master usernameDB_PASSWORD: The securely injected master password (sourced from AWS Secrets Manager)
This pipeline includes automated DevSecOps guardrails using Trivy. Every time you push code, the pipeline will scan both your Docker container and your Terraform configurations for vulnerabilities and misconfigurations.
To view the security reports:
- Navigate to the Actions tab in GitHub.
- Click on the latest deployment run.
- Scroll down the Summary page to view the generated vulnerability tables.