Skip to content

Repository files navigation

testcontainers-floci-go

Go Reference CI License: MIT

Go Testcontainers module for Floci — the open-source, drop-in replacement for LocalStack Community Edition.

Floci emulates 42 AWS services in a single container with:

  • ~24 ms startup time (native image)
  • ~13 MiB idle memory
  • ~90 MB Docker image
  • No auth tokens, no feature gates, MIT license

Installation

go get github.com/floci-io/testcontainers-floci-go

Requires Go 1.25+ and a running Docker daemon.

Quick start

package myservice_test

import (
    "context"
    "strings"
    "testing"

    "github.com/aws/aws-sdk-go-v2/aws"
    "github.com/aws/aws-sdk-go-v2/config"
    "github.com/aws/aws-sdk-go-v2/credentials"
    "github.com/aws/aws-sdk-go-v2/service/s3"

    floci "github.com/floci-io/testcontainers-floci-go"
)

func TestS3(t *testing.T) {
    ctx := context.Background()

    fc, err := floci.NewFlociContainer().Start(ctx)
    if err != nil {
        t.Fatal(err)
    }
    t.Cleanup(func() { _ = fc.Stop(ctx) })

    cfg, err := config.LoadDefaultConfig(ctx,
        config.WithRegion(fc.GetRegion()),
        config.WithBaseEndpoint(fc.GetEndpoint()),
        config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(
            fc.GetAccessKey(), fc.GetSecretKey(), "",
        )),
    )
    if err != nil {
        t.Fatal(err)
    }

    client := s3.NewFromConfig(cfg, func(o *s3.Options) {
        o.UsePathStyle = true // required for local endpoints
    })

    _, err = client.CreateBucket(ctx, &s3.CreateBucketInput{
        Bucket: aws.String("my-bucket"),
    })
    if err != nil {
        t.Fatal(err)
    }

    _, err = client.PutObject(ctx, &s3.PutObjectInput{
        Bucket: aws.String("my-bucket"),
        Key:    aws.String("hello.txt"),
        Body:   strings.NewReader("hello from floci"),
    })
    if err != nil {
        t.Fatal(err)
    }

    out, err := client.ListObjectsV2(ctx, &s3.ListObjectsV2Input{
        Bucket: aws.String("my-bucket"),
    })
    if err != nil {
        t.Fatal(err)
    }

    t.Logf("objects: %d", len(out.Contents))
}

S3 note: always use strings.NewReader or bytes.NewReader (seekable) when uploading objects. bytes.NewBufferString is not seekable and causes the AWS SDK to attempt trailing checksums, which require TLS and fail against a plain HTTP local endpoint.

Sharing a container across tests

Use TestMain to start the container once for the whole package:

package myservice_test

import (
    "context"
    "os"
    "testing"

    floci "github.com/floci-io/testcontainers-floci-go"
)

var fc *floci.StartedFlociContainer

func TestMain(m *testing.M) {
    ctx := context.Background()
    var err error
    fc, err = floci.NewFlociContainer().Start(ctx)
    if err != nil {
        panic(err)
    }
    code := m.Run()
    _ = fc.Stop(ctx)
    os.Exit(code)
}

Service configuration

Each of Floci's 42 services can be configured individually using typed config structs. Pass any struct to the corresponding With*Config method — unset fields keep their defaults.

S3

fc, _ := floci.NewFlociContainer().
    WithS3Config(floci.S3Config{
        Enabled:                     true,
        DefaultPresignExpirySeconds: 7200,
    }).
    Start(ctx)

SQS

fc, _ := floci.NewFlociContainer().
    WithSqsConfig(floci.SqsConfig{
        Enabled:                  true,
        DefaultVisibilityTimeout: 60,
        MaxMessageSize:           262144,
    }).
    Start(ctx)

DynamoDB

fc, _ := floci.NewFlociContainer().
    WithDynamoDbConfig(floci.DynamoDbConfig{Enabled: true}).
    Start(ctx)

Lambda

fc, _ := floci.NewFlociContainer().
    WithDedicatedNetwork(). // required for Lambda to reach Floci
    WithLambdaConfig(floci.LambdaConfig{
        Enabled:               true,
        DefaultMemoryMb:       256,
        DefaultTimeoutSeconds: 30,
        HotReloadEnabled:      true,
        ExposeRuntimePorts:    true, // invoke Lambdas from the host
    }).
    Start(ctx)

RDS (PostgreSQL / MySQL / MariaDB)

fc, _ := floci.NewFlociContainer().
    WithDedicatedNetwork().
    WithRdsConfig(floci.RdsConfig{
        Enabled:              true,
        DefaultPostgresImage: "postgres:16-alpine",
    }).
    Start(ctx)

ElastiCache (Redis / Valkey)

fc, _ := floci.NewFlociContainer().
    WithDedicatedNetwork().
    WithElastiCacheConfig(floci.ElastiCacheConfig{
        Enabled:      true,
        DefaultImage: "valkey/valkey:8",
    }).
    Start(ctx)

OpenSearch

fc, _ := floci.NewFlociContainer().
    WithDedicatedNetwork().
    WithOpenSearchConfig(floci.OpenSearchConfig{
        Enabled: true,
        Mock:    false,
    }).
    Start(ctx)

MSK (Kafka via Redpanda)

fc, _ := floci.NewFlociContainer().
    WithDedicatedNetwork().
    WithMskConfig(floci.MskConfig{
        Enabled:      true,
        DefaultImage: "redpandadata/redpanda:latest",
    }).
    Start(ctx)

All available config structs

Struct AWS service
AcmConfig AWS Certificate Manager
ApiGatewayConfig API Gateway (v1)
ApiGatewayV2Config API Gateway (v2)
AppConfigConfig AppConfig
AppConfigDataConfig AppConfig Data
AthenaConfig Athena
BedrockRuntimeConfig Bedrock Runtime
CloudFormationConfig CloudFormation
CloudWatchLogsConfig CloudWatch Logs
CloudWatchMetricsConfig CloudWatch Metrics
CodeBuildConfig CodeBuild
CodeDeployConfig CodeDeploy
CognitoConfig Cognito
DynamoDbConfig DynamoDB
Ec2Config EC2
EcrConfig ECR
EcsConfig ECS
EksConfig EKS
ElastiCacheConfig ElastiCache
ElbV2Config ELB v2
EventBridgeConfig EventBridge
FirehoseConfig Kinesis Firehose
GlueConfig Glue
IamConfig IAM
KinesisConfig Kinesis
KmsConfig KMS
LambdaConfig Lambda
MskConfig MSK (Kafka)
OpenSearchConfig OpenSearch
PipesConfig EventBridge Pipes
RdsConfig RDS
ResourceGroupsTaggingConfig Resource Groups Tagging
S3Config S3
SchedulerConfig EventBridge Scheduler
SecretsManagerConfig Secrets Manager
SesConfig SES
SesV2Config SES v2
SnsConfig SNS
SqsConfig SQS
SsmConfig SSM Parameter Store
StepFunctionsConfig Step Functions

Container options

fc, _ := floci.NewFlociContainer().
    WithImage("floci/floci:latest").   // pin a specific tag
    WithRegion("eu-west-1").
    WithAccountID("111122223333").
    WithAvailabilityZone("eu-west-1a").
    WithDedicatedNetwork().            // isolated Docker network for stateful services
    Start(ctx)

Connection details

Method Returns
GetEndpoint() http://host:port — pass as base endpoint to AWS SDK clients
GetRegion() AWS region string
GetAccessKey() Access key ("test")
GetSecretKey() Secret key ("test")
GetAccountID() AWS account ID
GetAvailabilityZone() Availability zone
GetDedicatedNetworkName() Docker network name (empty if none)
GetMappedPort(ctx, port) Host port mapped from the given container port

Dedicated network

Services that spawn real Docker containers (Lambda, RDS, ElastiCache, MSK, OpenSearch, ECR, EKS) need a Docker network to communicate with Floci. Call WithDedicatedNetwork() to have the module create and manage one automatically:

fc, _ := floci.NewFlociContainer().
    WithDedicatedNetwork().
    WithLambdaConfig(floci.LambdaConfig{Enabled: true}).
    Start(ctx)

// The network name is passed to Floci automatically via FLOCI_SERVICES_DOCKER_NETWORK.
// fc.GetDedicatedNetworkName() returns it if you need it elsewhere.

The network is removed when Stop is called.

Docker image variants

Tag Description
floci/floci:latest Native image — sub-second startup (recommended)
floci/floci:x.y.z Pinned release
floci/floci:latest-compat Includes Python 3, AWS CLI, and boto3
floci/floci:nightly Latest nightly build from main

Requirements

  • Go 1.25+
  • Docker (running locally or in CI)
  • github.com/testcontainers/testcontainers-go v0.42.0

Examples

Running the tests

go test -v ./...

Requires Docker running locally; the floci/floci:latest image is pulled automatically on first run.

Related projects

License

MIT

About

No description, website, or topics provided.

Resources

Code of conduct

Contributing

Security policy

Stars

4 stars

Watchers

1 watching

Forks

Releases

Sponsor this project

Packages

Contributors

Languages