LocalStack Community Editionの代替として登場したFlociを試してみた
こんにちは。サービス開発室の武田です。
2026年3月、LocalStackがCommunity Editionを廃止して統合イメージへ移行し、認証トークンを必須化しました。その後コミュニティの反応を受けてプランが見直され、現在は非商用限定の無料プラン(Hobbyプラン)が用意されています。とはいえアカウント登録と認証トークンは必須です。認証なしで気軽に使えていた従来の環境とは違いますね。
そんな中、LocalStackの代替として Floci というオープンソースプロジェクトが登場しました。MITライセンスで完全無料、認証不要という、かつてのLocalStack Community Editionのポジションを引き継ぐツールです。
今回はFlociの導入から主要サービス(S3、SQS、DynamoDB、Lambda、Cognito)の動作検証までをひととおり試してみました。
Flociとは
Flociは、ローカル環境でAWSサービスをエミュレートするオープンソースツールです。名前の由来は「floccus(ポップコーンのように見える雲の形状)」から来ているようです。
LocalStackとの比較
| 項目 | Floci | LocalStack(Hobbyプラン) |
|---|---|---|
| 認証トークン | 不要 | 必須(アカウント登録も必要) |
| 対応サービス数 | 20+ | 30+(商用は有料プラン必須) |
| CI/CDサポート | 無制限 | CIクレジット付き(非商用のみ) |
| 商用利用 | 可(MIT) | 有料プラン必須(Base $39/月〜) |
| 起動時間 | 約24ms | 約3.3秒 |
| アイドル時メモリ | 約13MiB | 約143MiB |
| Dockerイメージサイズ | 約90MB | 約1.0GB |
※ Floci側の数値はFlociのREADMEからの引用です(筆者の環境での実測起動時間は0.012〜0.016秒)。LocalStack側の対応サービス数・CI/CDサポート・料金はLocalStack公式サイトを参照しています(いずれも2026年3月時点)。
起動時間やメモリ使用量の差が際立ちます。FlociはJava(Quarkus)で実装されており、GraalVMによるネイティブビルドで高速な起動を実現しています。
対応サービス
READMEによると、20以上のAWSサービスに対応しており、408件のSDKテストがすべてパスしているとのことです。主な対応サービスは次のとおりです。
- S3、SQS、SNS、DynamoDB、Lambda
- API Gateway(v1 / v2)、Cognito、KMS
- Kinesis、Secrets Manager、SSM
- CloudFormation、Step Functions、IAM、STS
- ElastiCache、RDS、EventBridge、CloudWatch
API Gateway v2(HTTP API) 、 Cognito 、 ElastiCache 、 RDS はLocalStack Community EditionではPro限定でした。Flociではこれらが対応サービスに含まれています。今回はCognitoの動作を確認しました(RDS・ElastiCacheの検証は行っていません)。
環境構築
前提条件
- Docker 20.10+
- docker compose v2+
- AWS CLI v2
docker-compose.ymlの作成
LambdaやRDSなどのDockerコンテナを内部で起動するサービスを使う場合は、Dockerソケットのマウントとuser: rootの指定が必要です。
# docker-compose.yml
services:
floci:
image: hectorvent/floci:latest
user: root
ports:
- "4566:4566"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
tmpfs:
- /app/data
environment:
FLOCI_STORAGE_MODE: memory
FLOCI_SERVICES_DOCKER_NETWORK: <プロジェクト名>_default
latestタグはネイティブイメージで、1秒未満で起動できます。プラットフォーム互換性が必要な場合はlatest-jvmタグも利用できます。
ハマりポイント: dataディレクトリのパーミッション
Flociのネイティブイメージはコンテナ内でuid=1001として動作します。S3サービスはストレージモードをmemoryにしていても/app/data/s3ディレクトリを作成しようとします。ホスト側の./dataをボリュームマウントするとパーミッションエラーの発生する場合があります。
Caused by: java.nio.file.AccessDeniedException: /app/./data/s3
回避方法はいくつかあります。
user: rootでコンテナを起動するtmpfsでデータディレクトリをマウントする(上記の例)- ホスト側で
chown 1001:0 ./dataを実行してからマウントする
起動
$ docker compose up -d
$ docker compose logs
floci-1 | 2026-03-23 14:08:11,049 INFO [io.quarkus] (main) floci 1.0.4 native (powered by Quarkus 3.32.3) started in 0.016s. Listening on: http://0.0.0.0:4566
起動時間0.016秒。LocalStackの約3秒と比べると圧倒的ですね。
起動ログには有効なサービス一覧も表示されます。
Enabled services: [ssm, sqs, s3, dynamodb, sns, lambda, apigateway, iam, elasticache, rds, events, logs, monitoring, secretsmanager, apigatewayv2, kinesis, kms, cognito-idp, states, cloudformation]
環境変数の設定
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=test
Flociはどんなクレデンシャルでも受け付けるため、ダミーの値で問題ありません。
サービスごとの検証
S3: バケット操作とオブジェクトの読み書き
まずはもっとも基本的なS3の操作を試します。
# バケット作成
$ aws s3 mb s3://my-bucket --endpoint-url $AWS_ENDPOINT_URL
make_bucket: my-bucket
# ファイルアップロード
$ echo '{"hello":"world"}' | aws s3 cp - s3://my-bucket/data.json --endpoint-url $AWS_ENDPOINT_URL
# ファイル一覧
$ aws s3 ls s3://my-bucket --endpoint-url $AWS_ENDPOINT_URL
2026-03-23 23:06:06 18 data.json
# ファイルダウンロード
$ aws s3 cp s3://my-bucket/data.json ./data.json --endpoint-url $AWS_ENDPOINT_URL
download: s3://my-bucket/data.json to ./data.json
$ cat data.json
{"hello":"world"}
問題なく動作しました。バージョニングも試してみます。
# バージョニング有効化
$ aws s3api put-bucket-versioning \
--bucket my-bucket \
--versioning-configuration Status=Enabled \
--endpoint-url $AWS_ENDPOINT_URL
# 同じキーで上書き
$ aws s3 cp data2.json s3://my-bucket/data.json --endpoint-url $AWS_ENDPOINT_URL
upload: ./data2.json to s3://my-bucket/data.json
# バージョン一覧
$ aws s3api list-object-versions --bucket my-bucket --endpoint-url $AWS_ENDPOINT_URL
{
"Versions": [
{
"ETag": "\"8127eccb9204d5d416014be2208e0acc\"",
"Size": 30,
"StorageClass": "STANDARD",
"Key": "data.json",
"VersionId": "68d3483e-2251-458c-9de8-637961d26d7d",
"IsLatest": true,
"LastModified": "2026-03-23T14:06:16.430129+00:00"
}
]
}
Pre-signed URLの生成も動作確認できました。
$ aws s3 presign s3://my-bucket/data.json --expires-in 3600 --endpoint-url $AWS_ENDPOINT_URL
http://localhost:4566/my-bucket/data.json?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=...&X-Amz-Signature=...
S3はpath-style URL(http://localhost:4566/my-bucket/my-key)を使用します。SDKで利用する場合はforcePathStyle: true(Node.js)やpathStyleAccessEnabled(true)(Java)を指定してください。
SQS: キューの作成とメッセージの送受信
# 標準キュー作成
$ aws sqs create-queue --queue-name orders --endpoint-url $AWS_ENDPOINT_URL
{
"QueueUrl": "http://localhost:4566/000000000000/orders"
}
# メッセージ送信
$ QUEUE_URL="$AWS_ENDPOINT_URL/000000000000/orders"
$ aws sqs send-message \
--queue-url $QUEUE_URL \
--message-body '{"event":"order.placed","id":"abc123"}' \
--endpoint-url $AWS_ENDPOINT_URL
{
"MD5OfMessageBody": "f7e26fc383c796babd2dc7b200c0a364",
"MessageId": "67e92be4-b2ac-403c-b655-708cbf13ef0b"
}
# メッセージ受信
$ aws sqs receive-message \
--queue-url $QUEUE_URL \
--max-number-of-messages 10 \
--endpoint-url $AWS_ENDPOINT_URL
{
"Messages": [
{
"MessageId": "67e92be4-b2ac-403c-b655-708cbf13ef0b",
"ReceiptHandle": "95b5472f-295e-4526-abf8-78ac9a92c6bd",
"MD5OfBody": "f7e26fc383c796babd2dc7b200c0a364",
"Body": "{\"event\":\"order.placed\",\"id\":\"abc123\"}",
"Attributes": {
"ApproximateReceiveCount": "1",
"SentTimestamp": "1774274792772"
}
}
]
}
送信したメッセージがそのまま受信でき、ApproximateReceiveCountやSentTimestampなどの属性も正しく返されています。
FIFOキューも問題なく作成できました。
$ aws sqs create-queue --queue-name orders.fifo --attributes FifoQueue=true --endpoint-url $AWS_ENDPOINT_URL
{
"QueueUrl": "http://localhost:4566/000000000000/orders.fifo"
}
$ aws sqs list-queues --endpoint-url $AWS_ENDPOINT_URL
{
"QueueUrls": [
"http://localhost:4566/000000000000/orders",
"http://localhost:4566/000000000000/orders.fifo"
]
}
ドキュメントによると、デッドレターキュー(DLQ)の設定やStartMessageMoveTask(DLQリドライブ)にも対応しています。
DynamoDB: テーブル作成とCRUD操作
# テーブル作成
$ aws dynamodb create-table \
--table-name Users \
--attribute-definitions AttributeName=userId,AttributeType=S \
--key-schema AttributeName=userId,KeyType=HASH \
--billing-mode PAY_PER_REQUEST \
--endpoint-url $AWS_ENDPOINT_URL
{
"TableDescription": {
"TableName": "Users",
"TableStatus": "ACTIVE",
"TableArn": "arn:aws:dynamodb:us-east-1:000000000000:table/Users",
...
}
}
テーブル作成直後にTableStatusがACTIVEになっている点がうれしいですね。本物のDynamoDBのようにCREATING → ACTIVEを待つ必要がありません。
# アイテム書き込み
$ aws dynamodb put-item --table-name Users \
--item '{"userId":{"S":"u1"},"name":{"S":"Alice"},"age":{"N":"30"}}' \
--endpoint-url $AWS_ENDPOINT_URL
$ aws dynamodb put-item --table-name Users \
--item '{"userId":{"S":"u2"},"name":{"S":"Bob"},"age":{"N":"20"}}' \
--endpoint-url $AWS_ENDPOINT_URL
# アイテム取得
$ aws dynamodb get-item --table-name Users \
--key '{"userId":{"S":"u1"}}' \
--endpoint-url $AWS_ENDPOINT_URL
{
"Item": {
"userId": { "S": "u1" },
"name": { "S": "Alice" },
"age": { "N": "30" }
}
}
# Scan with filter(age > 25 のユーザーを検索)
$ aws dynamodb scan --table-name Users \
--filter-expression "age > :min" \
--expression-attribute-values '{":min":{"N":"25"}}' \
--endpoint-url $AWS_ENDPOINT_URL
{
"Items": [
{
"userId": { "S": "u1" },
"name": { "S": "Alice" },
"age": { "N": "30" }
}
],
"Count": 1,
"ScannedCount": 2
}
2件のアイテムのうち、フィルター条件に一致する1件だけが返され、ScannedCount: 2、Count: 1と正しく動作しています。
ドキュメントによると、GSI(グローバルセカンダリインデックス)、TTL、DynamoDB Streamsにも対応しています。トランザクション(TransactWriteItems / TransactGetItems)もサポートされています。
Lambda: 関数のデプロイと実行
LambdaはDockerコンテナ内で関数コードを実行します。Dockerソケットのマウントが必要です。
ハマりポイント: Dockerソケットのパーミッション
LambdaはDockerソケットマウント経由でホストのDockerデーモンを利用してコンテナを起動するため、FlociコンテナからDockerソケットへのアクセス権が必要です。デフォルトのuid=1001ではBindException: Permission deniedが発生しました。
Failed to start Lambda container: java.net.BindException: Permission denied
user: rootを指定することで解決できます。
# Node.jsのLambda関数を作成
$ cat > index.mjs << 'EOF'
export const handler = async (event) => {
console.log("Event:", JSON.stringify(event));
return { statusCode: 200, body: JSON.stringify({ hello: "world", received: event }) };
};
EOF
$ zip function.zip index.mjs
# デプロイ
$ aws lambda create-function \
--function-name my-function \
--runtime nodejs22.x \
--role arn:aws:iam::000000000000:role/lambda-role \
--handler index.handler \
--zip-file fileb://function.zip \
--endpoint-url $AWS_ENDPOINT_URL
{
"FunctionName": "my-function",
"FunctionArn": "arn:aws:lambda:us-east-1:000000000000:function:my-function",
"Runtime": "nodejs22.x",
"Handler": "index.handler",
"State": "Active",
...
}
# 同期実行
$ aws lambda invoke \
--function-name my-function \
--payload '{"key":"value"}' \
--cli-binary-format raw-in-base64-out \
response.json \
--endpoint-url $AWS_ENDPOINT_URL
{
"StatusCode": 200,
"ExecutedVersion": "$LATEST"
}
$ cat response.json
{"statusCode":200,"body":"{\"hello\":\"world\",\"received\":{\"key\":\"value\"}}"}
初回実行時はLambdaランタイムのDockerイメージ(public.ecr.aws/lambda/nodejs:22)をpullするため少し時間がかかります。2回目以降はキャッシュ済みなので高速です。
ドキュメントによると、公式のAWS Lambdaコンテナイメージがあるランタイムすべてに対応しています(nodejs22.x、python3.13、java21、provided.al2023など)。今回はnodejs22.xで動作を確認しました。Event Source Mappingにも対応しており、SQS・Kinesis・DynamoDB Streamsをトリガーとして自動実行できるとのことです。
Cognito: ユーザープール作成と認証フロー
LocalStack Community EditionではPro限定だったCognitoが、Flociでは無料で利用できます。
# ユーザープール作成
$ POOL_ID=$(aws cognito-idp create-user-pool \
--pool-name MyApp \
--query UserPool.Id --output text \
--endpoint-url $AWS_ENDPOINT_URL)
$ echo "Pool ID: $POOL_ID"
Pool ID: us-east-1_a833221dc
# アプリクライアント作成
$ CLIENT_ID=$(aws cognito-idp create-user-pool-client \
--user-pool-id $POOL_ID \
--client-name my-client \
--explicit-auth-flows ALLOW_USER_PASSWORD_AUTH ALLOW_REFRESH_TOKEN_AUTH \
--query UserPoolClient.ClientId --output text \
--endpoint-url $AWS_ENDPOINT_URL)
$ echo "Client ID: $CLIENT_ID"
Client ID: 906c434dde374468aa2b4d34ed
# ユーザー作成
$ aws cognito-idp admin-create-user \
--user-pool-id $POOL_ID \
--username alice@example.com \
--temporary-password Temp1234! \
--endpoint-url $AWS_ENDPOINT_URL
{
"User": {
"Username": "alice@example.com",
"Enabled": true,
"UserStatus": "FORCE_CHANGE_PASSWORD"
}
}
# パスワードを恒久的に設定
$ aws cognito-idp admin-set-user-password \
--user-pool-id $POOL_ID \
--username alice@example.com \
--password Perm1234! \
--permanent \
--endpoint-url $AWS_ENDPOINT_URL
# 認証
$ aws cognito-idp initiate-auth \
--auth-flow USER_PASSWORD_AUTH \
--client-id $CLIENT_ID \
--auth-parameters USERNAME=alice@example.com,PASSWORD=Perm1234! \
--endpoint-url $AWS_ENDPOINT_URL
{
"AuthenticationResult": {
"AccessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"ExpiresIn": 3600,
"TokenType": "Bearer",
"RefreshToken": "ed42bd19-23e7-4724-a535-86b41b41c552",
"IdToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
}
ユーザープールの作成からユーザー認証、JWTトークンの発行まで、一連のフローが問題なく動作しました。AccessToken、IdToken、RefreshTokenがすべて正しく返されています。
ドキュメントではOIDCのWell-Knownエンドポイントにも対応しているとされています。具体的には/.well-known/openid-configurationと/.well-known/jwks.jsonです。ただし筆者の検証環境ではS3のエラーが返され、動作を確認できませんでした。パスがバケット名として解釈されているようです。
ストレージモード
ドキュメントによると、Flociは4つのストレージモードを提供しています。用途に応じて使い分けが可能です。
| モード | 再起動後のデータ | 書き込み性能 | ユースケース |
|---|---|---|---|
memory |
消失 | 最速 | ユニットテスト、CI |
persistent |
保持 | 同期書き込み | 永続性重視の開発 |
hybrid |
保持 | メモリ読み取り、非同期ディスク書き込み | 一般的なローカル開発 |
wal |
保持 | WAL(追記型) | 高書き込みワークロード |
サービスごとに個別のストレージモードを設定可能です。
environment:
FLOCI_STORAGE_MODE: hybrid
FLOCI_STORAGE_PERSISTENT_PATH: /app/data
FLOCI_STORAGE_SERVICES_DYNAMODB_MODE: persistent
FLOCI_STORAGE_SERVICES_SQS_MODE: memory
CI/CDでの利用
Flociのドキュメントを参考にしたGitHub Actionsでの利用例です(今回CIでの動作検証は行っていません)。認証トークン不要ですので、シンプルに書けます。
# .github/workflows/test.yml
name: Test
on: [push]
jobs:
test:
runs-on: ubuntu-latest
services:
floci:
image: hectorvent/floci:latest
ports:
- "4566:4566"
steps:
- uses: actions/checkout@v4
- name: Run tests
env:
AWS_ENDPOINT_URL: http://localhost:4566
AWS_DEFAULT_REGION: us-east-1
AWS_ACCESS_KEY_ID: test
AWS_SECRET_ACCESS_KEY: test
run: npm test
LocalStackのように認証トークンの管理やアカウント登録を気にする必要がありません。MITライセンスのため、商用プロジェクトのCIでも追加費用なしで利用できます。
まとめ
Flociをひととおり試してみましたが、LocalStack Community Editionの代替として十分に実用的だと感じました。
認証不要・完全無料・MITライセンスで導入のハードルが低く、起動が圧倒的に速い(実測で0.012〜0.016秒)のがよいですね。メモリ消費も少なく(アイドル時約13MiB)、S3、SQS、DynamoDB、Lambda、Cognitoのいずれも基本的な操作は問題ありませんでした。CognitoなどLocalStackでは有料プラン限定だったサービスが無料で使えるのもうれしいポイントです。RDSやElastiCacheも対応サービスに含まれています。商用プロジェクトのCI/CDでも認証やライセンスを気にせず使えるのも助かります。
一方で気になる点もあります。まだ新しいプロジェクト(2026年2月開始)なので、エッジケースでの互換性は未知数です。dataディレクトリのパーミッション周りでハマるケースがありますし(user: rootやtmpfsで回避可能)、Lambda利用時はDockerソケットのパーミッションにも注意が必要です。LocalStackのawslocalのようなラッパーCLIは提供されておらず、コミュニティの規模もまだ小さい(2026年3月時点でスター数約1,200)状況です。
なおLocalStackは現在、非商用限定の無料Hobbyプラン(30+サービス)を提供しています。個人の学習用途であればLocalStackも引き続き選択肢です。ただし商用利用にはBase($39/月〜)以上のプランが必要です。認証不要で商用利用も制限なしというFlociの立ち位置は、特にCI/CDパイプラインや商用プロジェクトのローカル開発環境で強みを発揮しそうですね。






