June 3, 2026 · v1.3.57
Karpenter and Cluster Autoscaler can finally run against MiniStack locally — EC2 Fleet's CreateFleet + DescribeFleets land with full instant-type synchronous launch and multi-config × multi-override capacity distribution. EKS gains OIDC Identity Provider Config so the post-1.29 OIDC-on-cluster bindings work end-to-end. DynamoDB stops lying about export / import status at submit time, and PartiQL UPDATE / DELETE finally returns ConditionalCheckFailedException when a non-key predicate fails instead of silently no-op'ing.
CreateFleet + DescribeFleetsTier-1 capacity allocation, parsed against the AWS request shape:
TargetCapacitySpecification including DefaultTargetCapacityType — this is what drives spot vs on-demand, not the top-level Type. The FleetType enum is {request, maintain, instant}; "spot" is never one of its values. Comparing fleet_type == "spot" would have been dead code that silently mis-classified every Karpenter request.LaunchTemplateConfigs[*] with Overrides[*] — total capacity is round-robin distributed across every (config, override) slot, and the response carries one Instances[*] item per non-empty slot with its own LaunchTemplateAndOverrides block. Karpenter's 2 configs × 2 overrides × TotalTargetCapacity=4 now launches one of each combination, not four copies of configs[0].Overrides[0].Instances + Errors are emitted only when Type=instant; maintain / request return FleetId alone with FulfilledCapacity=0 and ActivityStatus=pending_fulfillment — matching the AWS-documented "instant launches synchronously, others fulfill asynchronously" contract.DescribeFleets on an unknown FleetId returns InvalidFleetId.NotFound (HTTP 400) instead of silently dropping it from the response set, so typo'd IDs surface immediately.Reported by @b-rajesh. Contributed by @b-rajesh.
Three new ops at /clusters/{name}/identity-provider-configs/{verb}:
AssociateIdentityProviderConfig — validates required fields (oidc.identityProviderConfigName, issuerUrl, clientId). AWS allows one OIDC IdP per cluster regardless of name, so any second associate — same name or different — returns ResourceInUseException. The issuer URL + client ID + optional usernameClaim / groupsClaim are forwarded to the k3s API server via --kube-apiserver-arg=oidc-* flags on restart.DescribeIdentityProviderConfig — the identityProviderConfigArn is generated at associate time (arn:aws:eks:{region}:{account}:identityproviderconfig/{cluster}/oidc/{name}/{uuid}) and stable across describes so Terraform / CDK / Pulumi don't see false-positive drift on every poll.DisassociateIdentityProviderConfig — synchronous pop in the request handler (no rogue background thread that would race a describe).The cluster stays ACTIVE throughout. Real AWS keeps the cluster status unchanged during IdP work — the change is carried in the returned update record, not on the cluster shape. Tags supplied at associate time are reachable via ListTagsForResource(resourceArn=idp_arn). The destructive k3s restart that wipes in-cluster workloads on associate / disassociate — a local-emulator limitation, since k3s can't hot-swap kube-apiserver flags — is logged as a warning so the side effect is surfaced. Contributed by @b-rajesh.
ExportTableToPointInTime + ImportTable return IN_PROGRESS at submit timeReal AWS always reports IN_PROGRESS at submit time; the flip to COMPLETED happens later, observed by polling DescribeExport / DescribeImport. MiniStack used to lie: ExportTableToPointInTime set IN_PROGRESS on the create record, then overwrote it to COMPLETED on the very next DescribeExport, so callers never observed the in-progress state; ImportTable built the response with ImportStatus=COMPLETED synchronously.
Now both submit with IN_PROGRESS and no EndTime. The first DescribeExport / DescribeImport still returns IN_PROGRESS within the grace window (MINISTACK_DDB_EXPORT_COMPLETE_AFTER_SEC / MINISTACK_DDB_IMPORT_COMPLETE_AFTER_SEC, default 1s each). After the window elapses, the next describe flips to COMPLETED and stamps EndTime. Tests can set the window to a fraction of a second; production usage is unchanged. Reported by @hicksy. Export contributed by @HarrisonTCodes.
UPDATE / DELETE with a false non-key predicate returns ConditionalCheckFailedExceptionUPDATE "t" SET n=9 WHERE pk='x' AND name='beta' against an item with name='alpha' previously silently no-op'd — the PartiQL handlers iterated every row applying the entire WHERE clause and reported success when nothing matched. AWS treats non-key clauses in a PartiQL UPDATE / DELETE as a conditional check on the PK-targeted item: if the targeted row doesn't exist or any non-PK predicate fails, the request must surface ConditionalCheckFailedException and leave the item unchanged.
The PartiQL handlers now split the WHERE conditions into primary-key equalities and "the rest"; require an = clause on every primary-key attribute (UPDATE / DELETE without one returns ValidationException up front, instead of falling through to an all-table scan); look up the single targeted item by PK / SK; and return ConditionalCheckFailedException if the item is missing or any non-key predicate fails. Reported by @hicksy.
/_ministack/sqs/messages admin endpointMirrors the existing /_ministack/ses/messages pattern. GET returns every queue's messages grouped by account — MessageId, Body, MD5OfBody, SentTimestamp, VisibleAt, IsVisible, ReceiveCount, FirstReceiveTimestamp, MessageAttributes, Attributes, MessageGroupId, MessageDeduplicationId, SequenceNumber — with optional ?account=<12-digit> and ?QueueUrl=<url> filters. Pure introspection; does not touch visible_at / receive_count / any field a concurrent ReceiveMessage mutates, so it's safe to call from a test asserting in-flight state. Reported by @mbamber.
MINISTACK_RDS_PUBLIC_ENDPOINT env varWhen MiniStack itself runs in Docker, the RDS code path auto-detects MiniStack's own network and emits Endpoint.Address = container_ip + Endpoint.Port = container_port — reachable from apps sharing that Docker network (the documented happy path), but invisible to clients outside it. For remote-MiniStack deployments (the host is reachable but the Docker network isn't), set MINISTACK_RDS_PUBLIC_ENDPOINT=1: DescribeDBInstances then returns {MINISTACK_HOST, host_port} — the host-published Docker port. Off by default, so existing native and same-network deployments keep their current behavior byte-for-byte. AWS-shape valid: Endpoint.Port is an arbitrary integer in botocore (1150–65535), so reporting the host-bound port is honest about what clients should connect to.
StartConfigurationSession accepts identifier by ID or nameApplicationIdentifier, EnvironmentIdentifier, and ConfigurationProfileIdentifier are documented in service-2.json as accepting either form. MiniStack treated them as IDs only, so passing a name (a perfectly valid AWS pattern) produced a session token referring to a non-resolvable triple that failed at the first GetLatestConfiguration. Each identifier now resolves via ID-first, name-fallback lookups; unresolved → ResourceNotFoundException 404. Contributed by @LiamMacP.
MINISTACK_HOST honored consistently across servicesecs._discover_poll_endpoint, elasticache._spawn_redis_container, opensearch._spawn_dataplane, lambda_svc._execute_function_local (subprocess AWS_ENDPOINT_URL), and several response-URL builders previously hardcoded "localhost" and ignored MINISTACK_HOST. They now resolve through a module-level _MINISTACK_HOST = os.environ.get("MINISTACK_HOST", "localhost"), so a MiniStack running on a different host can be reached over the network with the standard describe-... commands — set MINISTACK_HOST=<remote-ip> at boot. Default behavior unchanged for existing localhost deployments. Contributed by @neriyaco.
docker pull ministackorg/ministack:1.3.57 docker run -d -p 4566:4566 ministackorg/ministack:1.3.57
Or pin in compose.yaml:
services:
ministack:
image: ministackorg/ministack:1.3.57
ports:
- "4566:4566"
Issues and PRs welcome on GitHub. Discussion on r/ministack.