Tools: MiniStack now emulates MWAA. Develop and test Airflow DAGs locally, skip the $353/month (2026)

Tools: MiniStack now emulates MWAA. Develop and test Airflow DAGs locally, skip the $353/month (2026)

The scenario

Cost per test run by region

AWS pricing by region

Services with uniform global pricing

DynamoDB on-demand (per 1M request units)

S3 Standard

Monthly cost: AWS vs MiniStack

AWS monthly cost by test frequency

MiniStack monthly cost

Savings summary

Monthly savings (AWS → MiniStack)

Annual savings

Volume / performance testing (occasional)

What runs where

Quick start

1. Start MiniStack

2. Provision infrastructure

3. Point tests at MiniStack

4. Run in CI

Free tier reminder

Services supported by MiniStack MWAA costs $353/month. That's $0.49/hour, 24/7, just for the environment to exist. Whether you're actively developing DAGs, running tests in CI, or doing nothing at all; you pay. MiniStack now emulates MWAA. You can develop and validate Airflow DAGs locally. DAG structure, task routing, API calls on a single Docker container. No AWS environment needed until you deploy to production. If you had a dedicated MWAA environment for development and testing, that's $353/month you no longer need. And MWAA is just one piece. Here's what a full integration test suite actually costs on AWS. A typical integration test suite that validates cloud infrastructure: Per test run: ~50 DynamoDB operations, ~10 S3 operations, 9 Glue jobs (10 workers, ~4 min each), 20 Athena queries, 4 MWAA API calls. Per-run cost is identical across all regions: ~$2.64 (Glue pricing is uniform) The only regional variation is in DynamoDB and S3, which together account for less than $0.001 per run — negligible. Note: MWAA is typically shared infrastructure (dev + test + orchestration), not test-only. Attributing 20% to testing: ~$71/month. MiniStack runs on a CI runner. No AWS charges. Based on standard CI runner at $0.008/minute, 2 minutes per run. These run against real AWS (not practical on a local emulator due to cluster scale): Running all three once per month: ~$16/month additional. MiniStack supports Glue catalog operations and Athena (via DuckDB), but running full PySpark ETL jobs locally requires additional setup (local Spark + Iceberg). No AWS credentials. No IAM roles. No VPN. Every push, every PR. If running against real AWS at low frequency, free tier covers most services: Bottom line: Glue and MWAA are the only services that cost real money for testing. MiniStack eliminates the need for both in CI. 55+ AWS services including: S3, DynamoDB, Lambda, SQS, SNS, SSM, Glue, Athena, MWAA, Step Functions, ECS, RDS, ElastiCache, Kinesis, EventBridge, Cognito, KMS, IAM, STS, Secrets Manager, CloudWatch, CloudFormation, API Gateway, Route53, EKS, ECR, and more. Full list: github.com/ministackorg/ministack Some tools paywall their services. MiniStack is MIT licensed. Free forever. GitHub · Docker Hub · ministack.org · LinkedIn All pricing verified against AWS published rates for 2025/2026. MiniStack is MIT-licensed open-source software. CI runner costs based on GitHub Actions standard runners at $0.008/minute. Templates let you quickly answer FAQs or store snippets for re-use. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse

Command

Copy

# -weight: 500;">docker-compose.yml services: ministack: image: ministackorg/ministack:latest ports: - "4566:4566" # -weight: 500;">docker-compose.yml services: ministack: image: ministackorg/ministack:latest ports: - "4566:4566" # -weight: 500;">docker-compose.yml services: ministack: image: ministackorg/ministack:latest ports: - "4566:4566" -weight: 500;">docker compose up -d -weight: 500;">docker compose up -d -weight: 500;">docker compose up -d import boto3 endpoint = "http://localhost:4566" ddb = boto3.client("dynamodb", endpoint_url=endpoint, aws_access_key_id="test", aws_secret_access_key="test", region_name="us-east-1") ddb.create_table( TableName="my-table", KeySchema=[{"AttributeName": "pk", "KeyType": "HASH"}], AttributeDefinitions=[{"AttributeName": "pk", "AttributeType": "S"}], BillingMode="PAY_PER_REQUEST", ) import boto3 endpoint = "http://localhost:4566" ddb = boto3.client("dynamodb", endpoint_url=endpoint, aws_access_key_id="test", aws_secret_access_key="test", region_name="us-east-1") ddb.create_table( TableName="my-table", KeySchema=[{"AttributeName": "pk", "KeyType": "HASH"}], AttributeDefinitions=[{"AttributeName": "pk", "AttributeType": "S"}], BillingMode="PAY_PER_REQUEST", ) import boto3 endpoint = "http://localhost:4566" ddb = boto3.client("dynamodb", endpoint_url=endpoint, aws_access_key_id="test", aws_secret_access_key="test", region_name="us-east-1") ddb.create_table( TableName="my-table", KeySchema=[{"AttributeName": "pk", "KeyType": "HASH"}], AttributeDefinitions=[{"AttributeName": "pk", "AttributeType": "S"}], BillingMode="PAY_PER_REQUEST", ) # One config change — everything else stays the same endpoint_url = os.environ.get("ENDPOINT_URL") # None for real AWS def make_client(-weight: 500;">service): kwargs = {} if endpoint_url: kwargs["endpoint_url"] = endpoint_url kwargs["aws_access_key_id"] = "test" kwargs["aws_secret_access_key"] = "test" return boto3.client(-weight: 500;">service, region_name="us-east-1", **kwargs) # One config change — everything else stays the same endpoint_url = os.environ.get("ENDPOINT_URL") # None for real AWS def make_client(-weight: 500;">service): kwargs = {} if endpoint_url: kwargs["endpoint_url"] = endpoint_url kwargs["aws_access_key_id"] = "test" kwargs["aws_secret_access_key"] = "test" return boto3.client(-weight: 500;">service, region_name="us-east-1", **kwargs) # One config change — everything else stays the same endpoint_url = os.environ.get("ENDPOINT_URL") # None for real AWS def make_client(-weight: 500;">service): kwargs = {} if endpoint_url: kwargs["endpoint_url"] = endpoint_url kwargs["aws_access_key_id"] = "test" kwargs["aws_secret_access_key"] = "test" return boto3.client(-weight: 500;">service, region_name="us-east-1", **kwargs) # Real AWS pytest tests/ # MiniStack ENDPOINT_URL=http://localhost:4566 pytest tests/ # Real AWS pytest tests/ # MiniStack ENDPOINT_URL=http://localhost:4566 pytest tests/ # Real AWS pytest tests/ # MiniStack ENDPOINT_URL=http://localhost:4566 pytest tests/ # GitHub Actions example jobs: integration-tests: runs-on: ubuntu-latest services: ministack: image: ministackorg/ministack:latest ports: - 4566:4566 steps: - uses: actions/checkout@v4 - run: -weight: 500;">pip -weight: 500;">install boto3 pytest - run: python scripts/provision.py # Create tables, params, buckets - run: ENDPOINT_URL=http://localhost:4566 pytest tests/ -v # GitHub Actions example jobs: integration-tests: runs-on: ubuntu-latest services: ministack: image: ministackorg/ministack:latest ports: - 4566:4566 steps: - uses: actions/checkout@v4 - run: -weight: 500;">pip -weight: 500;">install boto3 pytest - run: python scripts/provision.py # Create tables, params, buckets - run: ENDPOINT_URL=http://localhost:4566 pytest tests/ -v # GitHub Actions example jobs: integration-tests: runs-on: ubuntu-latest services: ministack: image: ministackorg/ministack:latest ports: - 4566:4566 steps: - uses: actions/checkout@v4 - run: -weight: 500;">pip -weight: 500;">install boto3 pytest - run: python scripts/provision.py # Create tables, params, buckets - run: ENDPOINT_URL=http://localhost:4566 pytest tests/ -v