May 16, 2026 · v1.3.42
Four ships in this release: MWAA (real Apache Airflow containers, both v2 and v3), CloudWatch alarms that actually publish to SNS, Lambda emitting the four canonical observability metrics on every invocation, and the AWS::ApiGateway::Account CFN resource that unblocks a very common CDK pattern.
boto3.client("mwaa") now works against MiniStack. CreateEnvironment spins up a real apache/airflow:<version> container in standalone mode on the same Docker network as MiniStack, syncs DAGs from your SourceBucketArn + DagS3Path into /opt/airflow/dags/ once the container reaches AVAILABLE, and forces an aggressive DAG scan interval so newly synced files become visible to Airflow within seconds.
The full lifecycle is covered: CreateEnvironment / GetEnvironment / UpdateEnvironment / ListEnvironments / DeleteEnvironment (stops + removes the container, releases the host port). Token helpers CreateWebLoginToken and CreateCliToken return the correct field names per the boto3 MWAA model. InvokeRestApi proxies straight through to the running Airflow REST API:
/api/v2/ via Simple Auth Manager with ALL_ADMINS=true/api/v1/ with Basic auth, using the standalone-generated admin password captured from the container after bootimport boto3, json
mwaa = boto3.client("mwaa", endpoint_url="http://localhost:4566")
s3 = boto3.client("s3", endpoint_url="http://localhost:4566")
s3.create_bucket(Bucket="my-dags")
s3.put_object(
Bucket="my-dags",
Key="dags/hello.py",
Body=b"""
from datetime import datetime
from airflow import DAG
from airflow.operators.empty import EmptyOperator
with DAG('hello', start_date=datetime(2026,1,1), schedule=None):
EmptyOperator(task_id='say_hi')
""",
)
mwaa.create_environment(
Name="local-airflow",
AirflowVersion="3.0.6",
DagS3Path="dags/",
SourceBucketArn="arn:aws:s3:::my-dags",
ExecutionRoleArn="arn:aws:iam::000000000000:role/r",
NetworkConfiguration={"SubnetIds": ["s1","s2"], "SecurityGroupIds": ["sg-1"]},
)
# Poll until AVAILABLE, then list DAGs via the InvokeRestApi proxy
resp = mwaa.invoke_rest_api(Name="local-airflow", Method="GET", Path="/dags")
print(resp["RestApiStatusCode"], resp["RestApiResponse"]) # 200, dag list
End-to-end verified locally with real DAGs visible on both apache/airflow:2.10.4 and apache/airflow:3.0.6. Mount the Docker socket into MiniStack (-v /var/run/docker.sock:/var/run/docker.sock) and put both containers on the same network so the spawned Airflow can reach MiniStack S3 and vice versa.
Alarms stored AlarmActions for ages but never dispatched them on state transition. That changes now. On every OK ↔ ALARM ↔ INSUFFICIENT_DATA transition, the matching AlarmActions / OKActions / InsufficientDataActions list is consulted and SNS topic ARNs are published with the AWS-shaped payload:
{
"AlarmName": "errors-too-high",
"AlarmDescription": "...",
"AWSAccountId": "000000000000",
"NewStateValue": "ALARM",
"NewStateReason": "Threshold Crossed: Sum 5 GreaterThanThreshold 0.0",
"StateChangeTime": "2026-05-16T18:00:00Z",
"Region": "us-east-1",
"OldStateValue": "OK",
"Trigger": { "MetricName": "Errors", "Namespace": "MyApp",
"Statistic": "Sum", "Period": 60,
"EvaluationPeriods": 1, "ComparisonOperator": "GreaterThanThreshold",
"Threshold": 0.0, "Dimensions": [] }
}
Fires on both SetAlarmState (manual flip) and the auto-evaluation path triggered by PutMetricData. ActionsEnabled=False (or DisableAlarmActions) suppresses dispatch. Closes the single largest inter-service integration gap — alerting-driven test scenarios now work end-to-end against MiniStack.
Every Lambda invocation now publishes the four canonical AWS/Lambda metrics, dimensioned by FunctionName:
Invocations (Count) — 1 per call, for both RequestResponse and Event invocation typesErrors (Count) — 1 when the handler raised (Handled or Unhandled)Duration (Milliseconds) — wall-clock around the worker callThrottles (Count) — 1 when reserved concurrency rejected the call (HTTP 429)lam.invoke(FunctionName="my-fn", Payload=b"{}")
lam.invoke(FunctionName="my-fn", Payload=b"{}")
stats = cw.get_metric_statistics(
Namespace="AWS/Lambda",
MetricName="Invocations",
Dimensions=[{"Name": "FunctionName", "Value": "my-fn"}],
StartTime=time.time() - 600, EndTime=time.time(),
Period=60, Statistics=["Sum"],
)
sum(p["Sum"] for p in stats["Datapoints"]) # → 2.0
Same shape and granularity real CloudWatch emits, so alarms set on AWS/Lambda namespaces now evaluate against real datapoints — and combined with the alarm actions fix above, end-to-end Lambda observability tests (invoke → error → alarm transitions to ALARM → SNS notification arrives) work in one local run.
cloudWatchRole: trueThe singleton CFN resource that stores CloudWatchRoleArn for the API Gateway account was missing from the supported resource map, so any CDK stack using new RestApi({ cloudWatchRole: true }) failed with Unsupported resource type: AWS::ApiGateway::Account. The new handler writes the role ARN into the same store the runtime UpdateAccount / GetAccount API reads from, so it round-trips end-to-end — CDK stacks reach CREATE_COMPLETE and aws apigateway get-account reflects the value.
docker pull ministackorg/ministack:1.3.42 docker run -d -p 4566:4566 ministackorg/ministack:1.3.42
Or pin in compose.yaml:
services:
ministack:
image: ministackorg/ministack:1.3.42
ports:
- "4566:4566"
For MWAA, mount the Docker socket and (if you're running MiniStack inside a container) put MiniStack and the Airflow containers on a shared network:
services:
ministack:
image: ministackorg/ministack:1.3.42
ports:
- "4566:4566"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
- DOCKER_NETWORK=ministack-net
networks: [ministack-net]
networks:
ministack-net:
name: ministack-net
Issues and PRs welcome on GitHub. Discussion on r/ministack.