Skip to content

About

Light, fluffy, and always free - Testcontainers for Node.js: run the Floci local AWS emulator in your integration tests.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

9 stars

Watchers

0 watching

Forks

Repository files navigation

Floci Floci

Any Cloud. Locally.
Light, fluffy, and always free: Testcontainers for Node.js
No account. No auth token. No feature gates.

npm version Node versions CI License: MIT GitHub Stars

Quick Start · Configuration · Emulators · Docs


What is this?

A Node.js / TypeScript Testcontainers module for Floci, the free, open-source local cloud emulators. FlociContainer starts a Floci (AWS) container for your integration tests and gives you an endpoint and credentials to point the AWS SDK v3 at, plus a typed, per-service configuration API over the emulator's environment variables. No cloud account, no auth token, MIT license.

See the Floci documentation for the full list of supported AWS services.

The Floci emulators

testcontainers-floci-node is the Node.js member of the Floci Testcontainers family. Floci is named after floccus, the cloud formation that looks like popcorn.

Emulator Cloud Port Supported
floci AWS 4566 ✅ @floci/testcontainers
floci-az Azure 4577 Planned
floci-gcp GCP 4588 Planned
floci-oci OCI 4599 Planned

Installation

# npm
npm install --save-dev @floci/testcontainers

# yarn
yarn add --dev @floci/testcontainers

# pnpm
pnpm add --save-dev @floci/testcontainers

Quick start

import { S3Client, CreateBucketCommand, ListBucketsCommand } from '@aws-sdk/client-s3';
import { FlociContainer } from '@floci/testcontainers';

describe('S3', () => {
  let floci: Awaited<ReturnType<FlociContainer['start']>>;

  beforeAll(async () => {
    floci = await new FlociContainer().start();
  });

  afterAll(async () => {
    await floci.stop();
  });

  it('creates and lists a bucket', async () => {
    const s3 = new S3Client({
      endpoint: floci.getEndpoint(),
      region: floci.getRegion(),
      credentials: {
        accessKeyId: floci.getAccessKey(),
        secretAccessKey: floci.getSecretKey(),
      },
      forcePathStyle: true,
    });

    await s3.send(new CreateBucketCommand({ Bucket: 'my-bucket' }));
    const { Buckets } = await s3.send(new ListBucketsCommand({}));
    expect(Buckets?.map((b) => b.Name)).toContain('my-bucket');
  });
});

Jest note

Integration tests that use AWS SDK v3 may need Node VM modules enabled when running under Jest:

NODE_OPTIONS=--experimental-vm-modules npm test

Sharing a container across tests

import { FlociContainer, StartedFlociContainer } from '@floci/testcontainers';

let floci: StartedFlociContainer;

beforeAll(async () => {
  floci = await new FlociContainer().start();
});

afterAll(async () => {
  await floci.stop();
});

AWS SDK v3 Example (S3)

import { FlociContainer } from "@floci/testcontainers";
import { S3Client, CreateBucketCommand, ListBucketsCommand } from "@aws-sdk/client-s3";

const floci = await new FlociContainer().start();

const client = new S3Client({
  region: "us-east-1",
  endpoint: floci.getEndpoint(),
  credentials: {
    accessKeyId: "test",
    secretAccessKey: "test",
  },
  forcePathStyle: true,
});

await client.send(
  new CreateBucketCommand({
    Bucket: "example-bucket",
  }),
);

const buckets = await client.send(new ListBucketsCommand({}));

console.log(buckets.Buckets);

await floci.stop();

Service configuration

Each AWS service emulated by Floci can be configured individually using typed config classes passed to a with*Config(...) method on FlociContainer. See the Floci documentation for the full list of supported services.

Per-service examples

S3

import { FlociContainer, S3Config } from '@floci/testcontainers';

const floci = await new FlociContainer()
  .withS3Config(new S3Config(true, 7200))
  .start();

SQS

import { SqsConfig } from '@floci/testcontainers';

const floci = await new FlociContainer()
  .withSqsConfig(new SqsConfig(true, 60, 262144))
  .start();

DynamoDB

import { DynamoDbConfig } from '@floci/testcontainers';

const floci = await new FlociContainer()
  .withDynamoDbConfig(new DynamoDbConfig(true))
  .start();

Lambda

import { LambdaConfig } from '@floci/testcontainers';

const floci = await new FlociContainer()
  .withLambdaConfig(new LambdaConfig(
    true,   // enabled
    256,    // defaultMemoryMb
    30,     // defaultTimeoutSeconds
    false,  // ephemeral
    true,   // hotReloadEnabled
  ))
  .start();

RDS (PostgreSQL / MySQL / MariaDB)

import { RdsConfig } from '@floci/testcontainers';

const floci = await new FlociContainer()
  .withRdsConfig(new RdsConfig(true, 7001, 99, 'postgres:16-alpine'))
  .start();

ElastiCache (Redis / Valkey)

import { ElastiCacheConfig } from '@floci/testcontainers';

const floci = await new FlociContainer()
  .withElastiCacheConfig(new ElastiCacheConfig(true, 'valkey/valkey:8'))
  .start();

OpenSearch

import { OpenSearchConfig } from '@floci/testcontainers';

const floci = await new FlociContainer()
  .withOpenSearchConfig(new OpenSearchConfig(true, false))
  .start();

MSK (Kafka via Redpanda)

import { MskConfig } from '@floci/testcontainers';

const floci = await new FlociContainer()
  .withMskConfig(new MskConfig(true, false, 'redpandadata/redpanda:latest'))
  .start();

TLS Configuration

import { FlociContainer, TlsConfig } from '@floci/testcontainers';

const floci = await new FlociContainer()
  .withTlsConfig(new TlsConfig(true, true))
  .start();

const endpoint = floci.getSecureEndpoint(); // https://host:port
await floci.stop();

Storage Configuration

import { FlociContainer, StorageConfig } from '@floci/testcontainers';

const floci = await new FlociContainer()
  .withStorageConfig(new StorageConfig('/tmp/floci-data', true))
  .start();

await floci.stop();

Docker Socket

By default, the host Docker socket is mounted read-write at /var/run/docker.sock for container-backed services. Tests using only in-process services can omit this mount:

const floci = await new FlociContainer().withoutDockerSocket().start();
await floci.stop();

For a rootless Docker daemon, pass its host socket path. It is still mounted at /var/run/docker.sock inside Floci:

const floci = await new FlociContainer()
  .withDockerSocket(`${process.env.XDG_RUNTIME_DIR}/docker.sock`)
  .start();
await floci.stop();

withDockerSocket() also restores the default mount after withoutDockerSocket().

DuckDB Configuration

import { FlociContainer, DuckDbConfig } from '@floci/testcontainers';

const floci = await new FlociContainer()
  .withDuckDbConfig(new DuckDbConfig('floci/floci-duck:latest'))
  .start();

await floci.stop();

Log Level

import { FlociContainer } from '@floci/testcontainers';

const floci = await new FlociContainer()
  .withLogLevel('DEBUG')
  .start();

await floci.stop();

All available config classes

Config class AWS service
AcmConfig AWS Certificate Manager
ApiGatewayConfig API Gateway (v1)
ApiGatewayV2Config API Gateway (v2)
AppConfigConfig AppConfig
AppConfigDataConfig AppConfig Data
AthenaConfig Athena
BackupConfig AWS Backup
BcmDataExportsConfig BCM Data Exports
BedrockRuntimeConfig Bedrock Runtime
CloudFormationConfig CloudFormation
CloudFrontConfig CloudFront
CloudWatchLogsConfig CloudWatch Logs
CloudWatchMetricsConfig CloudWatch Metrics
CodeBuildConfig CodeBuild
CodeDeployConfig CodeDeploy
CognitoConfig Cognito
ConfigServiceConfig AWS Config
CostExplorerConfig Cost Explorer
CurConfig Cost and Usage Reports
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)
NeptuneConfig Neptune
OpenSearchConfig OpenSearch
PipesConfig EventBridge Pipes
PricingConfig AWS Pricing
RdsConfig RDS
ResourceGroupsTaggingConfig Resource Groups Tagging
Route53Config Route 53
S3Config S3
SchedulerConfig EventBridge Scheduler
SecretsManagerConfig Secrets Manager
SesConfig SES
SesV2Config SES v2
SnsConfig SNS
SqsConfig SQS
SsmConfig SSM Parameter Store
StepFunctionsConfig Step Functions
TextractConfig Textract
TransferFamilyConfig Transfer Family

Container options

By default, only the Floci gateway port (4566) is published to the host. Extra service ports are published only when their service configuration is explicitly supplied, or when withExposedPort(port) is called. Replacing a service configuration updates its published port range; disabling the service removes that range. This avoids publishing hundreds of unused proxy ports for tests that only use the gateway.

const floci = await new FlociContainer('floci/floci:x.y.z')  // pin a specific tag
  .withRegion('eu-west-1')
  .withAccountId('111122223333')
  .withAvailabilityZone('eu-west-1a')
  .withDedicatedNetwork()   // isolated Docker network for stateful services
  .start();

Connection details

Method Returns
getEndpoint() http://host:port — pass as endpoint to AWS SDK clients
getRegion() AWS region string
getAccessKey() Access key ("test" by default)
getSecretKey() Secret key ("test" by default)
getAccountId() AWS account ID
getMappedPort(port) Host port mapped from the given container port

Troubleshooting

Docker not running

Symptom: Cannot connect to the Docker daemon or container fails to start.

Cause: The Docker daemon is not running or the current user lacks permissions.

Resolution:

  1. Start Docker Desktop or the Docker daemon (sudo systemctl start docker)
  2. Verify with docker info
  3. Ensure your user is in the docker group (sudo usermod -aG docker $USER)

Port conflicts

Symptom: Bind for 0.0.0.0:<port> failed: port is already allocated

Cause: Another process or container is using the same port.

Resolution:

  1. Identify the conflicting process: lsof -i :<port> or docker ps
  2. Stop the conflicting process or container
  3. Alternatively, let Testcontainers use random port mapping (the default behavior)

Timeout errors

Symptom: Timeout waiting for container to be ready or test timeout exceeded.

Cause: The Floci container takes longer to start than the configured timeout.

Resolution:

  1. Increase the Jest test timeout: jest.setTimeout(120_000)
  2. Ensure Docker has sufficient resources (CPU/memory)
  3. Check container logs for startup errors: docker logs <container-id>
  4. Use withLogLevel('DEBUG') to get more verbose container output

Docker image tags

By default FlociContainer runs the floating latest tag of the emulator image (floci/floci:latest), so you always test against the current emulator. Pass an image name to the constructor to pin a release or follow main:

new FlociContainer('floci/floci:x.y.z');   // a specific release
new FlociContainer('floci/floci:nightly'); // built from main every night
Tag Description
floci/floci:latest Latest release, native image (default, recommended)
floci/floci:x.y.z Pinned release (native)
floci/floci:nightly Built from main every night

Every emulator publishes latest, x.y.z and nightly tags.

Requirements

  • Node.js 18+
  • Docker (running locally or in CI)
  • testcontainers >= 10.0.0

Building and testing

npm ci                     # install dependencies
npm run typecheck          # type-check (the lint gate CI enforces)
npm test                   # all tests, unit and integration
npm run test:unit          # unit tests only, no Docker required
npm run test:integration   # integration tests, Docker must be running

See CONTRIBUTING.md for the project layout, the branching model, and how to add a service.

Other languages

Language Repository
Java testcontainers-floci
Node.js / TypeScript testcontainers-floci-node (this repo)
Python testcontainers-floci-python
Go testcontainers-floci-go
.NET testcontainers-floci-dotnet

Community

License

MIT. See LICENSE.


Floci™ is a trademark of Hector Ventura. Code is MIT-licensed; see TRADEMARK.md for name and logo use.

About

Light, fluffy, and always free - Testcontainers for Node.js: run the Floci local AWS emulator in your integration tests.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

9 stars

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Contributors

Languages