October 3, 2026 · v1.5.21
v1.5.21 serves traffic through CloudFront distributions to S3 and custom origins, and CloudFormation now replaces a resource when a create-only property changes and executes IMPORT change sets, including ImportExistingResources. EFS gains its CloudFormation types and file system policy, protection and replication; API Gateway v2 gains custom domain names and API mappings; Athena runs DDL against the Glue catalog; AgentCore adds Memory resources; and SES enforces verified senders and an account sandbox. A round of ECS, Cognito, STS, Lambda and API Gateway fixes rounds it out.
CreateDomainName, GetDomainName, GetDomainNames, UpdateDomainName, DeleteDomainName and Create/Get/Update/DeleteApiMapping(s), with domain tags through TagResource, list pagination, and BadRequestException "Invalid stage identifier specified" for a mapping to a missing stage; AWS::ApiGatewayV2::DomainName and AWS::ApiGatewayV2::ApiMapping in CloudFormation. A domain and its mappings are the same resource as the API Gateway v1 domain and base path mappings, and a request whose Host is the domain reaches the mapped HTTP API. Reported by @wparad.RequiresRecreation: Always now creates a new resource under a new generated name, points its dependents at it and deletes the old one after the update succeeds (a rollback deletes the new one instead). With an explicit, unchanged name the update fails with the AWS message naming the physical id; this now also covers AWS::Lambda::Function. A named SQS queue or SNS topic fails with AWS's already-exists error instead and keeps its messages or subscriptions. Contributed by @iot-rocket.AWS::EFS::FileSystem, AWS::EFS::MountTarget and AWS::EFS::AccessPoint. The three types create, update in place and are replaced on create-only changes through the EFS store, with Ref and Fn::GetAtt as in the template reference, so CDK efs.FileSystem stacks deploy. Contributed by @fabio-andre-rodrigues.PutFileSystemPolicy, DescribeFileSystemPolicy, DeleteFileSystemPolicy, UpdateFileSystemProtection and Create/Describe/DeleteReplicationConfiguration; CreateMountTarget checks the subnet, security groups, zone and address (SubnetNotFound, SecurityGroupNotFound, MountTargetConflict, AvailabilityZonesMismatch, IpAddressInUse), and DeleteFileSystem with mount targets answers 409. Contributed by @fabio-andre-rodrigues.CreateMemory, GetMemory, ListMemories, UpdateMemory and DeleteMemory; memory strategies are recorded without extraction. Contributed by @pingedbrain.ListDatabases and GetDatabase read the Glue Data Catalog; CREATE EXTERNAL TABLE and DROP TABLE apply to it, creating the Glue table Athena would (formats, SerDe, TBLPROPERTIES); CREATE TABLE without EXTERNAL is rejected as Athena rejects it, and Iceberg tables are not supported; a query may name a table as "awsdatacatalog"."db"."t" or db.t. Contributed by @sjincho.<label>.cloudfront.net (or <label>.cloudfront.<MINISTACK_HOST>) is served: ordered cache behaviors, ViewerProtocolPolicy, AllowedMethods, DefaultRootObject, cache and origin request policy forwarding (and legacy ForwardedValues), viewer-request / viewer-response CloudFront Functions, response headers policies, and S3 or custom origins; an S3 origin is read only as the bucket policy allows it (OAC, OAI or public). DomainName has the AWS d + 13-character shape. List*Policies return the AWS-managed cache, origin request and response headers policies, and Create/Get/DeleteMonitoringSubscription are implemented. Not modelled: caching, WAF, logging, geo restrictions, custom error pages, signed URLs and cookies, Lambda@Edge. Contributed by @skialpine.AWS::EKS::Cluster applies Version, Logging, ResourcesVpcConfig, AccessConfig.AuthenticationMode and Tags in place and AWS::EKS::Nodegroup applies ScalingConfig, Labels, Taints, UpdateConfig, LaunchTemplate, Version, ReleaseVersion and Tags, where every such change used to report UPDATE_COMPLETE and was dropped. Contributed by @iot-rocket.AWS::Pipes::Pipe and AWS::Scheduler::ScheduleGroup update in place. A pipe keeps its stream position and CreationTime when Description, Target, RoleArn, DesiredState or Tags change, gets Description and Tags on create and refuses a create-only source change under an explicit Name, and a schedule group takes template and stack tag changes while keeping tags added through TagResource. Contributed by @iot-rocket.EncryptionConfiguration change on AWS::ECR::Repository replaces the repository under a new generated name (generated names now carry the usual suffix) and is refused for an explicit RepositoryName, where it used to be ignored. Contributed by @iot-rocket.Path change on AWS::IAM::Role and a Path or Description change on AWS::IAM::ManagedPolicy replace the resource under a new generated name, a named role refuses it as a custom-named replacement, a named policy fails with the IAM duplicate-name error as on AWS, also when only its Path changes, generated policy names get the suffix other generated names have, and both ARNs include the Path. Contributed by @iot-rocket.IMPORT change sets execute. Existing SQS queues, SSM parameters, S3 buckets, DynamoDB tables, IAM roles, log groups, Lambda functions, IoT policies, IoT CA certificates and Cognito user pools are adopted into a new or existing stack without being changed (IMPORT_IN_PROGRESS to IMPORT_COMPLETE, or a rollback when a resource is gone), and an import that changes Outputs or stack tags is refused, as on AWS. Contributed by @iot-rocket.GATEWAY_PORT. It always requested localhost:4566, so a container started on another port reported unhealthy while serving; it now resolves the port as the server does (GATEWAY_PORT, then EDGE_PORT, then 4566). Contributed by @skialpine.process.cwd() and os.getcwd() were MiniStack's own directory, so libraries that read files relative to it (such as node-config) missed the function's files; the working directory is now the code root (LAMBDA_TASK_ROOT), as on AWS. Contributed by @drakeo338. Reported by @shane-patzlsberger.AssumeRole honors trust-policy denies and conditions. With AUTH=true, a matching Deny (including NotAction) overrides an Allow, and sts:ExternalId and sts:RoleSessionName conditions are evaluated; a denied call returns AccessDenied and creates no session. Contributed by @AdrianAcala.nonce from /oauth2/authorize, both tokens carry cognito:groups, and a user linked by a PreSignUp trigger signs in as the linked profile instead of a new one. Contributed by @kjdev.Transform. Under AUTH=true, a nested stack whose template declares a Transform or calls Fn::Transform fails with Requires capabilities : [CAPABILITY_AUTO_EXPAND] unless the parent acknowledged CAPABILITY_AUTO_EXPAND, as on AWS. Contributed by @iot-rocket.AWS::RDS::DBCluster and AWS::RDS::DBInstance update in place. A stack update re-ran the create, which gave the resource a new endpoint, resource id and create time and emptied the cluster's member list; the properties the create stores now change on the existing record, a create-only or Engine change replaces the resource or, under a custom identifier, is refused, a cluster MasterUsername change leaves the cluster as it is, change sets report which properties replace, and a stack-created cluster can now be described and answers Fn::GetAtt DBClusterResourceId. Contributed by @iot-rocket.PutAccountDetails with ProductionAccessEnabled=false puts the account (per region) in the sandbox, as on AWS, and GetAccount reports it with the submitted Details. A sandboxed send to a recipient that is neither a verified address or domain identity nor a @simulator.amazonses.com address fails with MessageRejected "Email address is not verified. The following identities failed the check in region …" for v1 SendEmail, SendRawEmail and SendTemplatedEmail and v2 SendEmail; bulk sends reject only the affected entries. Accounts stay in production by default. Reported by @skialpine.AWS::ECS::Cluster and AWS::ECS::TaskDefinition update in place. A cluster change keeps the cluster (settings or configuration dropped from the template stay), and a task definition change registers the next revision of the family and deregisters the old one instead of overwriting revision 1. Contributed by @iot-rocket.IMPORT types. SNS topics, KMS keys and aliases, IoT thing types, Cognito user pool clients, groups, resource servers and identity pools, and API Gateway REST APIs and stages can be imported, including two-key identifiers, and Fn::GetAtt on an identity pool's Id resolves. Contributed by @iot-rocket.ImportExistingResources. A CREATE or UPDATE change set imports an added resource whose static custom name already exists (it needs DeletionPolicy Retain or RetainExceptOnCreate), and a rollback releases imported resources instead of deleting them. Contributed by @iot-rocket.UpdateService with forceNewDeployment replaces the tasks. It was ignored when the task definition did not change. Rolling deployments now pin image digests from the first task, honor versionConsistency disabled, use repositoryCredentials for private registries, and report imageDigest and the Fargate platform version; a task falls back to the local image when the pull fails. Contributed by @AdrianAcala.PutScalingPolicy with TargetTrackingScaling creates the TargetTracking-<group>-AlarmHigh-<uuid> alarm and, unless DisableScaleIn is set, the AlarmLow one on the tracked metric, lists them in Alarms of PutScalingPolicy and DescribePolicies, replaces them when the policy is updated and deletes them with the policy or its group. An AWS::AutoScaling::ScalingPolicy in a template does the same. Contributed by @iot-rocket.429 Too Many Requests by the plan's throttle and its per-method apiStages[].throttle, in addition to the stage's method settings, and a throttled response carries x-amzn-ErrorType: TooManyRequestsException. Contributed by @iot-rocket.enum, pattern, length, range and multipleOf bounds, integer versus number versus boolean, items, additionalProperties, patternProperties, dependencies, allOf / anyOf / oneOf / not, formats, and $ref to local definitions and to other models of the API); an empty body and a body nested more than 1000 levels deep are refused, and a schema that applies itself to the same value again answers 500. BAD_REQUEST_BODY and BAD_REQUEST_PARAMETERS read {"message": "..."} and carry x-amzn-ErrorType: BadRequestException. Contributed by @iot-rocket.$connect authorization. A CUSTOM $connect route ran no authorizer. It now runs its REQUEST authorizer, refuses the handshake with 401, 403 or 500 and passes principalId and the context to requestContext.authorizer of the connection's events. CreateAuthorizer, CreateRoute, UpdateRoute and the CloudFormation resources refuse a JWT authorizer, JWT route authorization and authorization on a route other than $connect with BadRequestException, as AWS does; a JWT $connect route kept in saved state refuses the handshake with 500 instead of validating the token. requestContext.stage names the stage in the connection URL instead of $default. Contributed by @iot-rocket.USE_SSL=1 when MiniStack runs in a container. The gateway certificate, CA bundle and Java truststore were bind-mounted into every Lambda container from MiniStack's own filesystem (MINISTACK_SSL_CERT, or the generated ministack-tls/server.crt under the temp directory), paths the host Docker daemon cannot see, so every invocation failed with bind source path does not exist. In a container they are now copied into the Lambda container, as function code already is. Contributed by @skialpine.DescribeDBClusterSnapshotAttributes. Was unimplemented (InvalidAction: Unknown RDS action), so Terraform's aws_db_cluster_snapshot resource failed on read (reading RDS DB Cluster Snapshot … attribute) after creating the snapshot. Returns the restore attribute with no shared accounts (the manual-snapshot default); ModifyDBClusterSnapshotAttribute is not implemented. Unknown snapshot ids answer DBClusterSnapshotNotFoundFault. Contributed by @skialpine.SendEmail, SendRawEmail, SendTemplatedEmail and SendBulkTemplatedEmail and v2 SendEmail and SendBulkEmail accepted any Source / FromEmailAddress. AWS requires the sender to be a verified identity in the account and region, in production as well as the sandbox, and now so does MiniStack: the send fails with MessageRejected "Email address is not verified. The following identities failed the check in region …". A verified domain covers its addresses and subdomains, domain names compare case-insensitively and email addresses case-sensitively, as AWS documents. Tests that send from an address they never created as an identity need a VerifyEmailIdentity / CreateEmailIdentity first. Reported by @skialpine.FAILED with "didn't contain changes"; it now lists each resource the stack holds, other than a custom resource or wait condition, as a Modify with Scope: Tags, and stack tags given in another order are no change for a change set or UpdateStack. Contributed by @iot-rocket.Modify whose detail names the cause (ChangeSource ResourceReference, ResourceAttribute or ParameterReference, with CausingEntity). Contributed by @iot-rocket.DescribeClusters statistics. include=["STATISTICS"] returns the sixteen running/pending task and active/draining service counters per launch type instead of an empty list. Contributed by @iot-rocket.DescribeClusters honours include. settings and tags come back empty and attachments and configuration are left out unless requested, CreateCluster keeps configuration, and a CloudFormation cluster reports its tags and the default containerInsights setting. Contributed by @iot-rocket.AWS::S3::MultiRegionAccessPoint and AWS::AutoScaling::LaunchConfiguration are replaced on update. Every property of both types is create-only, so a change now creates the resource under a new generated name and deletes the old one after the update (a Regions change was dropped and the stack reported the alias as the physical id, a launch configuration was overwritten under its old name), fails with the custom-named-resource error when the name is explicit, and is reported as Replacement: True in a change set. Contributed by @iot-rocket.AWS::SQS::Queue with FifoQueue gets a generated .fifo name. FifoQueue: true without a QueueName creates a FIFO queue with a generated .fifo name instead of a standard queue, and a QueueName whose .fifo suffix disagrees with FifoQueue fails the resource. Contributed by @iot-rocket.Fn::Select in a condition. A condition such as !Not [!Equals [!Select [2, !Ref KeySpec], ""]] was always true; conditions now resolve Fn::Select, and an index outside the list fails with Template error: Fn::Select cannot select nonexistent value at index N, in conditions and in properties. Contributed by @mishukdutta-cz.Ref to a list parameter. A Ref to a CommaDelimitedList or List<...> parameter gave the raw string, so a list property got one item per character; it now gives the space-trimmed list. A list in a nested stack's Parameters or in an AWS::SSM::Parameter Value fails the resource. Contributed by @mishukdutta-cz.docker pull ministackorg/ministack:1.5.21 docker run -d -p 4566:4566 ministackorg/ministack:1.5.21
Or pin in compose.yaml:
services:
ministack:
image: ministackorg/ministack:1.5.21
ports:
- "4566:4566"
Issues and PRs welcome on GitHub. Discussion on r/ministack.