Any Cloud. Locally.
Light, fluffy, and always free
No account. No auth token. No feature gates. Just docker compose up.
Quick Start · Features · Services · SDKs · Testcontainers · Migration · Docs
Floci is a free, open-source local AWS emulator for development, testing, and CI.
It gives you AWS-shaped services on your machine without requiring a cloud account, an auth token, or paid feature gates. Point your AWS SDK, CLI, Terraform, CDK, OpenTofu, or test suite at http://localhost:4566 and keep your existing workflows.
Already using LocalStack? Floci is a drop-in replacement: swap the image and keep going. See Migrating from LocalStack.
Floci is the AWS member of the Floci emulator family, named after floccus, the cloud formation that looks like popcorn.
The fastest way to run Floci is with the official CLI
floci startExport the AWS environment variables:
eval $(floci env)Use your existing AWS tools normally:
aws s3 mb s3://my-bucket
aws dynamodb create-table \
--table-name demo-table \
--attribute-definitions AttributeName=pk,AttributeType=S \
--key-schema AttributeName=pk,KeyType=HASH \
--billing-mode PAY_PER_REQUEST
aws dynamodb list-tablesThis short demo shows the CLI flow: start Floci, export the local AWS environment, run standard AWS CLI commands, and stop the emulator.
floci-cli-demo.mp4
All AWS services are available at http://localhost:4566. Any region works. Credentials can be any non-empty values unless you explicitly enable stricter service-specific auth checks.
Prefer Docker Compose?
Create a compose.yaml file:
services:
floci:
image: floci/floci:latest
ports:
- "4566:4566"Start Floci:
docker compose upThen configure your AWS environment manually:
export AWS_ENDPOINT_URL=http://localhost:4566
export AWS_DEFAULT_REGION=us-east-1
export AWS_ACCESS_KEY_ID=test
export AWS_SECRET_ACCESS_KEY=testUsing the old hectorvent/floci image?
Update your image name:
# Before
image: hectorvent/floci:latest
# After
image: floci/floci:latestThe old hectorvent/floci repository no longer receives updates.
Floci ships a browser console for inspecting the resources in your local emulator.
Open it at: http://localhost:4566/_floci/ui
Nothing runs at boot. The first request pulls the console image, starts it as a sidecar container on Floci's Docker network, hands it Floci's own reachable address plus the standard AWS environment, polls its health endpoint, and redirects the browser once it reports it can reach Floci. The sidecar's port is bound by Docker, so it needs no ports: entry of your own, and its logs are streamed into CloudWatch Logs under /floci/ui.
Starting a container needs the Docker socket:
services:
floci:
image: floci/floci:latest
ports:
- "4566:4566"
volumes:
- /var/run/docker.sock:/var/run/docker.sockThe console is not fixed to the one Floci ships. Any console implementing the Floci console contract runs with nothing but an image name: listen on the port in PORT, serve GET /api/health, talk to Floci at AWS_ENDPOINT_URL.
environment:
FLOCI_SERVICES_UI_IMAGE: acme/my-console:1.0A console that differs from the contract's defaults says so in its own io.floci.console.* image labels, so its operators do not have to. StackPort, for example, listens on 8080 rather than 4500; on an image that predates the labels, say it by hand:
environment:
FLOCI_SERVICES_UI_IMAGE: davireis/stackport:latest
FLOCI_SERVICES_UI_CONTAINER_NAME: floci-stackport
FLOCI_SERVICES_UI_PORT: "8080"
FLOCI_SERVICES_UI_INTERNAL_PORT: "8080"Floci Dash is another. It honours PORT, so only the two things it does differ on need naming:
environment:
FLOCI_SERVICES_UI_IMAGE: ghcr.io/ofsazib/floci-dash:latest
FLOCI_SERVICES_UI_CONTAINER_NAME: floci-dash
FLOCI_SERVICES_UI_ENDPOINT_ENV: FLOCI_URL
FLOCI_SERVICES_UI_STATUS_PATH: /api/healthzThe endpoint itself is never configured by hand: Floci resolves its own reachable address at start time and injects it as AWS_ENDPOINT_URL.
Full reference: Web Console and Console Contract v1.
Local AWS without the cloud account
Run AWS-compatible services locally without an AWS account, auth token, or paid feature gates.
Real Docker where fidelity matters
Lambda, RDS, Neptune, ElastiCache, MSK, ECS, EC2, EKS, OpenSearch, CodeBuild, and Managed Service for Apache Flink use real Docker-backed execution instead of shallow mocks.
Drop-in AWS compatibility
Point standard AWS clients at http://localhost:4566. Existing credentials, regions, SDKs, CLI commands, and IaC workflows stay familiar.
Terraform can provision AWS-shaped resources locally through Floci using the standard HashiCorp AWS provider. See the Terraform with Floci guide for provider configuration, resource examples, and optional emulated S3 state.
Fast enough for CI
The native image starts in milliseconds and keeps idle memory low, making it practical for local development and test pipelines.
Configurable persistence
Choose from in-memory, persistent, hybrid, and write-ahead log storage depending on the durability profile you need.
LocalStack's community edition sunset in March 2026, requiring auth tokens and freezing security updates. Floci is the no-strings-attached alternative.
Broad AWS coverage. Free forever. See the Services Overview for the full list of emulated services.
flowchart LR
Client["AWS SDK / CLI"]
subgraph Floci ["Floci, port 4566"]
Router["HTTP Router\nJAX-RS / Vert.x"]
subgraph Stateless ["Stateless Services"]
A["SSM · SQS · SNS\nIAM · STS · KMS\nSecrets Manager · SES\nCognito · Kinesis\nEventBridge · Scheduler · AppConfig\nCloudWatch · Step Functions\nCloudFormation · ACM · Config · CloudTrail\nAPI Gateway · AppSync · ELB v2 · Auto Scaling\nElastic Beanstalk · CodeDeploy · CodePipeline · Backup · FIS · Bedrock Runtime · Bedrock AgentCore · Route53 · Transfer"]
end
subgraph Stateful ["Stateful Services"]
B["S3 · DynamoDB\nDynamoDB Streams"]
end
subgraph Containers ["Container Services"]
C["Lambda\nElastiCache\nRDS\nNeptune\nECS\nEC2\nMSK\nEKS\nOpenSearch\nCodeBuild\nManaged Flink"]
D["Athena -> floci-duck\nDuckDB sidecar"]
end
Router --> Stateless
Router --> Stateful
Router --> Containers
Stateless & Stateful --> Store[("StorageBackend\nmemory · hybrid · persistent · wal")]
end
Docker["Docker Engine"]
Client -->|"HTTP :4566\nAWS wire protocol"| Router
Containers -->|"Docker API\nIAM / SigV4 auth"| Docker
Floci supports local emulation for application services, data services, eventing, identity, infrastructure, billing, and container-backed workloads.
For operation-level compatibility, see the Services Overview.
Detailed service notes
Floci uses real Docker containers when in-process emulation would reduce fidelity. This applies to stateful databases, connection-heavy protocols, runtimes, and build systems.
Docker-backed services require the Docker socket:
docker run -d --name floci \
-p 4566:4566 \
-v /var/run/docker.sock:/var/run/docker.sock \
-u root \
floci/floci:latestFloci can trade speed for durability depending on the workflow. Configure the default mode with FLOCI_STORAGE_MODE, or override storage per service.
Use memory for fast test runs. Use hybrid when you want state preserved across container restarts without much overhead.
For more detail, see the Storage Configuration documentation.
Floci supports per-account resource isolation with no extra setup. If AWS_ACCESS_KEY_ID is exactly 12 digits, Floci uses it as the account ID. Resources created by one account are invisible to another.
AWS_ACCESS_KEY_ID=111111111111 aws sqs create-queue --queue-name orders
AWS_ACCESS_KEY_ID=222222222222 aws sqs create-queue --queue-name ordersAny other key format, such as test or AKIA..., causes Floci to fall back to FLOCI_DEFAULT_ACCOUNT_ID, which defaults to 000000000000.
STS temporary credentials are routed too: credentials from AssumeRole resolve to the assumed role's account, so the cross-account assume-role-then-provision pattern works locally. Resolution precedence is 12-digit AKID → temporary-session lookup → FLOCI_DEFAULT_ACCOUNT_ID.
See the Multi-Account Isolation docs.
Point your existing AWS SDK at http://localhost:4566.
Java, AWS SDK v2
var client = DynamoDbClient.builder()
.endpointOverride(URI.create("http://localhost:4566"))
.region(Region.US_EAST_1)
.credentialsProvider(StaticCredentialsProvider.create(
AwsBasicCredentials.create("test", "test")))
.build();
client.createTable(b -> b
.tableName("demo-table")
.billingMode(BillingMode.PAY_PER_REQUEST)
.attributeDefinitions(
AttributeDefinition.builder().attributeName("pk").attributeType(ScalarAttributeType.S).build())
.keySchema(
KeySchemaElement.builder().attributeName("pk").keyType(KeyType.HASH).build()));
System.out.println(client.listTables().tableNames());Python, boto3
import boto3
client = boto3.client(
"ssm",
endpoint_url="http://localhost:4566",
region_name="us-east-1",
aws_access_key_id="test",
aws_secret_access_key="test",
)
client.put_parameter(
Name="/demo/app/message",
Value="hello from floci",
Type="String",
Overwrite=True,
)
response = client.get_parameter(Name="/demo/app/message")
print(response["Parameter"]["Value"])Node.js, AWS SDK v3
import { SQSClient, SendMessageCommand } from "@aws-sdk/client-sqs";
const client = new SQSClient({
endpoint: "http://localhost:4566",
region: "us-east-1",
credentials: { accessKeyId: "test", secretAccessKey: "test" },
});
await client.send(
new SendMessageCommand({
QueueUrl: "http://localhost:4566/000000000000/demo-queue",
MessageBody: "hello from floci",
}),
);Go, AWS SDK v2
package main
import (
"context"
"fmt"
"log"
"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"
)
func main() {
cfg, err := config.LoadDefaultConfig(context.TODO(),
config.WithRegion("us-east-1"),
config.WithCredentialsProvider(
credentials.NewStaticCredentialsProvider("test", "test", ""),
),
config.WithBaseEndpoint("http://localhost:4566"),
)
if err != nil {
log.Fatal(err)
}
client := s3.NewFromConfig(cfg, func(o *s3.Options) {
o.UsePathStyle = true
})
out, err := client.ListBuckets(context.TODO(), nil)
if err != nil {
log.Fatal(err)
}
fmt.Println(out.Buckets)
}Rust, AWS SDK
use aws_sdk_secretsmanager::config::{Credentials, Region};
use aws_sdk_secretsmanager::Client;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = aws_config::defaults(aws_config::BehaviorVersion::latest())
.region(Region::new("us-east-1"))
.credentials_provider(Credentials::new("test", "test", None, None, "floci"))
.endpoint_url("http://localhost:4566")
.load()
.await;
let client = Client::new(&config);
client
.create_secret()
.name("demo/secret")
.secret_string("hello from floci")
.send()
.await?;
Ok(())
}Bash, AWS CLI
export AWS_ACCESS_KEY_ID=test
export AWS_SECRET_ACCESS_KEY=test
export AWS_DEFAULT_REGION=us-east-1
aws --endpoint-url http://localhost:4566 s3 mb s3://my-bucket
aws --endpoint-url http://localhost:4566 s3 lsFloci has Testcontainers modules for starting isolated Floci instances directly from tests. This avoids shared state, manual daemon setup, and port conflicts.
For Testcontainers 1.x, use the versions as indicated in the table below.
Java
<dependency>
<groupId>io.floci</groupId>
<artifactId>testcontainers-floci</artifactId>
<version>1.14.0</version>
<scope>test</scope>
</dependency>@Testcontainers
class S3IntegrationTest {
@Container
static FlociContainer floci = new FlociContainer();
@Test
void shouldCreateBucket() {
S3Client s3 = S3Client.builder()
.endpointOverride(URI.create(floci.getEndpoint()))
.region(Region.of(floci.getRegion()))
.credentialsProvider(StaticCredentialsProvider.create(
AwsBasicCredentials.create(floci.getAccessKey(), floci.getSecretKey())))
.forcePathStyle(true)
.build();
s3.createBucket(b -> b.bucket("my-bucket"));
}
}For Testcontainers 2.x / Spring Boot 4.x, use version 2.15.0.
Node.js / TypeScript
npm install --save-dev @floci/testcontainersimport { FlociContainer } from "@floci/testcontainers";
import { S3Client, CreateBucketCommand } from "@aws-sdk/client-s3";
describe("S3", () => {
let floci: FlociContainer;
beforeAll(async () => {
floci = await new FlociContainer().start();
});
afterAll(async () => {
await floci.stop();
});
it("creates 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" }));
});
});Python
pip install testcontainers-flociimport boto3
from floci import FlociContainer
def test_s3_create_bucket():
with FlociContainer() as floci:
s3 = boto3.client(
"s3",
endpoint_url=floci.get_endpoint(),
region_name=floci.get_region(),
aws_access_key_id=floci.get_access_key(),
aws_secret_access_key=floci.get_secret_key(),
)
s3.create_bucket(Bucket="my-bucket")The compatibility-tests directory validates Floci across SDKs and tooling workflows.
2,576 automated compatibility tests across 5 SDKs and 3 IaC tools.
Floci is a drop-in replacement for LocalStack Community. The port, credentials, SDK configuration, and CLI endpoint pattern work the same way. Swap the image and keep going.
# Before
image: localstack/localstack
# After, standard image
image: floci/floci:latest
# After, if init scripts need AWS CLI or boto3
image: floci/floci:latest-compatLocalStack environment variables are translated automatically:
Init scripts mounted under /etc/localstack/init/ run unchanged. The /_localstack/init and /_localstack/health endpoints are still served. Once the emulator is up, the log also ends with a LocalStack-style Ready. line, so tooling that watches the log for it, such as the default wait strategy of Testcontainers' LocalStackContainer, works unchanged. Set LOCALSTACK_PARITY=false to opt out of automatic translation.
See the full migration guide.
Every tag combines a variant and a channel.
Use latest for stable releases, a pinned version for reproducible builds, and nightly to track main.
# Recommended
image: floci/floci:latest
# Includes AWS CLI and boto3
image: floci/floci:latest-compat
# ARM64 baseline for Raspberry Pi 4 / pre-LSE cores
image: floci/floci:latest-baseline
# Pinned release
image: floci/floci:x.y.z
# Track main
image: floci/floci:nightlyStable releases ship on the 1st and 3rd Tuesday of each month. Between trains, floci/floci:nightly tracks main. Every merged fix is available the next day, and dated nightly-mmddyyyy tags let you pin a specific night's build.
Versions are derived from Conventional Commits by semantic-release; CHANGELOG.md is generated, never hand-edited. Releases are cut from main only: there are no maintenance branches.
All settings are overridable through environment variables with the FLOCI_ prefix.
Full reference: configuration docs
When your application runs in a different container, set FLOCI_HOSTNAME to the Floci service name so returned URLs, such as SQS QueueUrl values, resolve correctly.
services:
floci:
image: floci/floci:latest
ports:
- "4566:4566"
environment:
- FLOCI_HOSTNAME=floci
my-app:
environment:
- AWS_ENDPOINT_URL=http://floci:4566
depends_on:
- flociWithout this, services may return URLs using localhost, which points to the wrong container from the application container.
Join the Floci community on Slack or GitHub Discussions. Feature ideas, compatibility questions, design tradeoffs, and rough proposals are welcome.
Floci is independent open source, funded by the people and companies who use it. Sponsorship buys gratitude and nothing else: every emulated service is free for everyone, forever, and no sponsor gets features, priority, or roadmap influence that the rest of the Flock does not.
Large logo with top placement in the emulator READMEs and on floci.io, plus a mention in release notes.
Logo in the emulator READMEs and on floci.io, plus a mention in release notes.
Your logo here. Become a sponsor.
Name in the emulator READMEs, a sponsor badge on GitHub, and our sincere thanks.
Every sponsor, including the Friends of the Flock who support Floci outside these tiers, is listed in THANKS.md.
MIT. Use it however you want.