Example application deployed to AWS ECS using Atmos and OpenTofu.
This repository demonstrates an elegant, self-contained approach to deploying containerized applications on ECS Fargate with automated CI/CD pipelines.
A simple Go web server designed to demonstrate container deployment strategies. Each request increments a counter, and the background color is configurable - making it easy to visualize blue/green deployments and load balancing. The /dashboard endpoint displays a grid of auto-refreshing iframes to show traffic distribution across instances. See app/ for details.
graph TB
subgraph "AWS Account"
subgraph "VPC"
subgraph "Private Subnets"
ECS[ECS Fargate Service]
EFS[(EFS Volume)]
end
subgraph "Public Subnets"
ALB[Application Load Balancer]
end
end
ECR[ECR Registry]
R53[Route 53]
end
Internet((Internet)) --> R53
R53 --> ALB
ALB --> ECS
ECS --> EFS
ECR -.-> ECS
subgraph "Dependencies (Pre-existing)"
VPC_DEP[VPC Component]
ECS_DEP[ECS Cluster Component]
EFS_DEP[EFS Component]
end
VPC_DEP -.->|vpc_id, subnet_ids| ECS
ECS_DEP -.->|cluster_arn, alb_listener_arn| ECS
EFS_DEP -.->|efs_id| EFS
This project uses:
- Atmos - Configuration orchestration and stack management
- OpenTofu - Infrastructure as Code (Terraform-compatible)
- AWS ECS Fargate - Serverless container orchestration
- AWS ECR - Container image registry
- AWS EFS - Persistent file storage (optional)
This repository dogfoods the Atmos 1.225 native runtime path end to end:
- Native CI with GitHub output variables, status checks, PR comments, and step summaries.
- GitHub artifact caching for the Atmos cache root, including toolchain installs, Terraform registry artifacts, source-provisioned workdirs, and remote import cache entries.
- Provider and module caching through the Terraform registry cache proxy.
- Toolchain installation for OpenTofu, TFLint, and Trivy from
.tool-versionsand stackdependencies.tools. - Source provisioning and workdir provisioning for remote VPC and ECS cluster fixture components.
- Remote stack imports for shared account-map compatibility configuration.
- Generate blocks for provider files, emulator-friendly fixture overrides, and compatibility shims.
- Lifecycle hooks, including ordered fixture setup/teardown, a built-in Trivy hook, and a custom command hook for TFLint.
- SAST reporting with Trivy SARIF flowing into GitHub Code Scanning results.
- Custom commands for local developer workflows.
- Native container components for local application runs without Docker Compose.
- Terraform tests with fixtures and cloud emulators.
- Emulator components for local and CI AWS-compatible tests without cloud credentials.
Run the application locally using Atmos native containers:
# Start the app locally (builds and runs on http://localhost:8080)
atmos up
# Stop the app
atmos downSee .github/workflows/ for detailed workflow diagrams.
| Workflow | Trigger | Action |
|---|---|---|
feature-branch.yml |
PR, merge queue | Build image, run tests, deploy preview (PR with deploy label), deploy dev (merge queue gate) |
validate.yml |
PR, merge queue | Lint CODEOWNERS |
main-branch.yaml |
Push to main |
Update draft release notes |
release.yaml |
Published release, manual dispatch | Promote image, deploy to staging and/or prod |
preview-cleanup.yml |
PR closed | Destroy preview environment |
Before deploying, you must have the following infrastructure deployed:
- VPC - With public/private subnets
- ECS Cluster - With an Application Load Balancer (ALB) and DNS records configured
- EFS (optional) - For persistent storage volumes
Then configure the dependencies in terraform/stacks/. You have two options:
Option 1: Use !terraform.state (recommended)
Update the dependency configurations in terraform/stacks/deps/ to point to your infrastructure's remote state:
deps/vpc.yaml- VPC component remote state locationdeps/ecs.yaml- ECS cluster component remote state locationdeps/efs.yaml- EFS component remote state location (if using volumes)
Option 2: Hardcode values (brownfield)
Replace the !terraform.state lookups in terraform/stacks/defaults/app.yaml with hardcoded values for your infrastructure. See terraform/stacks/defaults/README.md for required variables and a complete example.
# Deploy to dev environment
atmos terraform deploy app -s dev
# Deploy to staging
atmos terraform deploy app -s staging
# Deploy to production
atmos terraform deploy app -s prod- Open a PR → CI runs build and tests; add the
deploylabel to deploy a preview environment. - Approve and click "Merge when ready" → the merge queue runs build, tests, and
atmos terraform deploy app -s devbefore fast-forwardingmainto the queue commit. - Publish a GitHub release → automatically deploys to staging, then prod.
- Manually dispatch the release workflow with a
tagandenvironment→ redeploy a previous version (rollback or hotfix) without cutting a new release.
See .github/workflows/README.md for design rationale and detailed sequence diagrams.
Stack configurations are in terraform/stacks/. Each environment imports shared defaults and specifies environment-specific settings:
# terraform/stacks/dev.yaml
import:
- _default.yaml
- defaults/app.yaml
- deps/*
vars:
stage: devContainer configuration is defined in terraform/stacks/defaults/app.yaml and can be customized per environment.
.
├── app/ # Go application
│ ├── main.go # Web server
│ ├── Dockerfile # Multi-stage container build
│ ├── public/ # Static HTML assets
│ ├── rootfs/ # Container filesystem overlay
│ └── test/ # Local test harness
├── atmos.yaml # Atmos configuration
├── .atmos.d/ # Atmos custom commands
├── terraform/
│ ├── components/ # Terraform/OpenTofu modules
│ │ └── ecs-task/ # ECS task definition component
│ └── stacks/ # Environment configurations
│ ├── defaults/ # Shared component config
│ ├── deps/ # Dependency references
│ ├── dev.yaml
│ ├── staging.yaml
│ ├── prod.yaml
│ └── preview.yaml
└── .github/
├── workflows/ # CI/CD pipelines
├── README.yaml # README source
└── README.md # Generated README
To regenerate the README from this file, run:
atmos docs generate readmeCheck out these related projects.
- Atmos - Universal Tool for DevOps and Cloud Automation
- aws-vpc component - Cloud Posse Terraform component for provisioning VPCs and subnets
Join our Open Source Community on Slack. It's FREE for everyone! Our "SweetOps" community is where you get to talk with others who share a similar vision for how to rollout and manage infrastructure. This is the best place to talk shop, ask questions, solicit feedback, and work together as a community to build totally sweet infrastructure.
Sign up for our newsletter and join 3,000+ DevOps engineers, CTOs, and founders who get insider access to the latest DevOps trends, so you can always stay in the know. Dropped straight into your Inbox every week — and usually a 5-minute read.
Join us every Wednesday via Zoom for your weekly dose of insider DevOps trends, AWS news and Terraform insights, all sourced from our SweetOps community, plus a live Q&A that you can't find anywhere else. It's FREE for everyone!
Licensed under the Apache License, Version 2.0. See LICENSE for full details.
All other trademarks referenced herein are the property of their respective owners.
Copyright © 2017-2026 Cloud Posse, LLC
