Monday, April 13, 2026

Alternatives for LocalStack in TestContainers with Spring Boot

Introduction

Since around 20 March 2026 the company behind LocalStack decided to not support the community edition of LocalStack anymore, only paid versions are now available. 


A lot of forks were made from that now-frozen community repository, like this one.  Will any of these become the new community supported version?

Many teams use LocalStack within TestContainers in a Spring Boot application for example. What are the alternatives? This post is about exploring those.

Note that you can also pin the version of LocalStack to the last community edition release 4.14.0, that will keep on working after 6 april 2026. But there won’t be any updates on it of course…

Two forms are emerging: either replace the whole AWS stack as much as possible with one framework, or take an separate implementation per AWS service, so: implementation A to support one specific AWS service and implementation B for another AWS service, etc.

Below I’ve split my analysis in those two forms. 



Full AWS stack replacements


Several options exist for full AWS stack replacements.

Floci

 

Floci is a free, open-source local AWS emulator which can be found at https://github.com/floci-io/floci
It is a very recently created project , but got quite some traction already.  
A week ago Floci changed into a separate organisation, see this LinkedIn post
It is not a ful drop-in replacement in TestContainers, but it comes close. Below shows what needs changing in a Kotlin Spring Boot application if you are currently using LocalStack.

Replacing LocalStack 

 

You probably have something like this currently configured with LocalStack:

@TestConfiguration

class TestConfig {

  @Bean

  fun localStackContainer(): LocalStackContainer {

    return LocalStackContainer(DockerImageName.parse("localstack/localstack:4:14:0"))

      .withServices(LocalStackContainer.Service.DYNAMODB)

  }


Basically changing it by replacing the docker image name and the environment variables should get it working already you’d think, so like this:

@TestConfiguration

class TestConfig {

@Bean

fun localStackContainer(): LocalStackContainer {

  return LocalStackContainer(DockerImageName.parse(hectorvent/floci:1.5.2).asCompatibleSubstituteFor("localstack/localstack"))

    .withEnv("AWS_ENDPOINT_URL", "http://localhost:4566")

    .withEnv("AWS_DEFAULT_REGION", "us-east-1")

    .withEnv("AWS_ACCESS_KEY_ID", "test")

    .withEnv("AWS_SECRET_ACCESS_KEY", "test")

}


But no it is not that easy. You’ll need to insert a different startup-script.
For example like this:

class FlociContainer(dockerImageName: DockerImageName) :   LocalStackContainer(dockerImageName) {


override fun containerIsStarting(containerInfo: InspectContainerResponse) {

  var shell: String

  var executable: String

  if (dockerImageName.contains("jvm")) {

    shell = "/bin/sh"

    executable = "java -jar quarkus-app/quarkus-run.jar"

  } else {

    shell = "/bin/bash"

    executable = "./application"

  }

  var command = shell + "\n" + executable + "\n"

  try {

    copyFileToContainer(Transferable.of(command, 511), STARTER_SCRIPT)

  } catch (e: Throwable) {

    logger.error("Failed to copy startup script to container: ${e.message}", e)

    throw e

  }

}

}

 

Note the logic to determine the correct command for the startup-script to use in the Docker image. Floci has a native version which has a different startup-script than the JVM version.
You’ll also need to parse for a different startup string than LocalStack has.
TestContainers has a module for LocalStack that looks for this string: ".*Ready\\.\n"
But Floci does not log that string, so one to search for is: ".*started in.*"
So you need to override that for the Floci version. 


Then the test configuration becomes this:

@TestConfiguration

class TestConfig {

@Bean

fun localStackContainer(): LocalStackContainer {

  val flociImage: DockerImageName =

  DockerImageName.parse(hectorvent/floci:1.5.2).asCompatibleSubstituteFor("localstack/localstack")

 

  return FlociContainer(flociImage)

          // Floci settings

    .withEnv("AWS_ENDPOINT_URL", "http://localhost:4566")

    .withEnv("AWS_DEFAULT_REGION", "us-east-1")

    .withEnv("AWS_ACCESS_KEY_ID", "test")

    .withEnv("AWS_SECRET_ACCESS_KEY", "test")

          // Disable services you don’t need

    .withEnv("FLOCI_SERVICES_SSM_ENABLED", "false")

    .withEnv("FLOCI_SERVICES_ELASTICSEARCH_ENABLED", "false")

           // Floci container has different log-text to look for

    .waitingFor(LogMessageWaitStrategy().withRegEx(".*started in.*").withTimes(1))

}

 

That should do it!


Building Docker JVM image of Floci and start it locally 

 

For building a Docker image for the JVM version, you can run these commands in the root directory of the Floci project: 

  1. Create the artifact (application) for the Docker image: ./mvnw clean package
  2. Build the docker image: docker build . -t floci-jvm-package:1.5.1-fix-001 -f Dockerfile.jvm-package 


To start the JVM image of Floci locally you can run this command in the root directory:

 

docker-compose -f docker-compose-fix-001.xml up

 

With this in that docker compose file:

 

services:

    floci:

        image:    floci-jvm-package:1.5.1-fix-001

        ports:

            - "4566:4566"

        volumes:

            - ./data:/app/data

            - ./init/start.d:/etc/floci/init/start.d:ro

            - ./init/stop.d:/etc/floci/init/stop.d:ro


The output will look something like this then:

Container common-adapters-floci-1    Recreated                                                                                                                                                                                                                                                                                                                          0.1s

Attaching to floci-1

floci-1    |      ______ _            ____      _____ _____

floci-1    |    |    ____| |        / __ \ / ____|_      _|

floci-1    |    | |__    | |      | |    | | |            | |   

floci-1    |    |    __| | |      | |    | | |            | |   

floci-1    |    | |        | |___| |__| | |____ _| |_

floci-1    |    |_|        |______\____/ \_____|_____|

floci-1    |

floci-1    |

floci-1    |                      Powered by Quarkus 3.32.3

floci-1    | 2026-04-11 15:51:14,012 INFO    [io.github.hectorvent.floci.lifecycle.EmulatorLifecycle] (main) === AWS Local Emulator Starting ===

floci-1    | 2026-04-11 15:51:14,012 INFO    [io.github.hectorvent.floci.lifecycle.EmulatorLifecycle] (main) Storage mode: memory

floci-1    | 2026-04-11 15:51:14,012 INFO    [io.github.hectorvent.floci.lifecycle.EmulatorLifecycle] (main) Persistent path: ./data

floci-1    | 2026-04-11 15:51:14,012 INFO    [io.github.hectorvent.floci.core.common.ServiceRegistry] (main) Enabled services: [ssm, sqs, s3, dynamodb, sns, lambda, apigateway, iam, elasticache, rds, events, scheduler, logs, monitoring, secretsmanager, apigatewayv2, kinesis, kms, cognito-idp, states, cloudformation, acm, email, es]

floci-1    | 2026-04-11 15:51:14,013 INFO    [io.github.hectorvent.floci.lifecycle.EmulatorLifecycle] (main) === AWS Local Emulator Ready ===

floci-1    | 2026-04-11 15:51:14,021 INFO    [io.quarkus] (main) floci 1.4.0 native (powered by Quarkus 3.32.3) started in 0.085s. Listening on: http://0.0.0.0:4566

floci-1    | 2026-04-11 15:51:14,021 INFO    [io.quarkus] (main) Profile prod activated.

floci-1    | 2026-04-11 15:51:14,021 INFO    [io.quarkus] (main) Installed features: [cdi, config-yaml, rest, rest-jackson, smallrye-context-propagation, vertx]


Notice also the ‘started in 0.085s’, which contains the string for which the above example FlociContainer is looking for to see if the container started successfully.


Automatically create your application queues in Floci


A fancy enhancement for when you are using Spring Boot: you can write a BeanFactoryPostProcessor which parses for @SqsListener annotations to automatically create the queues in the Floci container.

Run Integration Tests against locally running Floci


To run your tests against a locally running Floci instance directly, for example to see its logging, started up with Docker like mentioned above, you override the endpoint when creating the DynamoDBClient:

    return DynamoDbClient.builder()

          .endpointOverride(URI.create("http://localhost:4566"))

    .credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create(localStackContainer.accessKey, localStackContainer.secretKey)))

    .region(Region.of(localStackContainer.region))

    .build()

}


Testcontainers module for Floci

 

There’s now also a testcontainers module available for floci so you should not need all above manual changes: https://testcontainers.com/modules/floci/

I did not test this module yet.

 



Other full replacement options

  • Rust focused it seems: Rustack
  • Only 1 maintainer and runs standalone, some AWS, some GCP, some Azure services supported: CloudTwin
  • Python focused it seems: Moto: This post talks about Moto mocking and using it in unittests. Is it a mocking or emulating framework?
  • Any of the LocalStack forks.


AWS stack per-service replacement


Multiple options exist, this list might not be listing all options. 

  1. DynamoDB: 
    1. use the AWS DynamoDB Local replacement. Docker image is here. Can be used on its own, and there’s a TestContainers module for it too here.
    2. Dynalite: last update in 2020, seems to be dead. The TestContainers module seems to be gone now too
  2. S3: Minio Not supported anymore, see here.
  3. SQS: https://lib.rs/crates/tc_elasticmq. Docker image here.






No comments: