LocalStack Community Editionの代替として登場したFlociを試してみた

LocalStack Community Editionの代替として登場したFlociを試してみた

2026.03.24

こんにちは。サービス開発室の武田です。

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)CognitoElastiCacheRDS は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"
            }
        }
    ]
}

送信したメッセージがそのまま受信でき、ApproximateReceiveCountSentTimestampなどの属性も正しく返されています。

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",
        ...
    }
}

テーブル作成直後にTableStatusACTIVEになっている点がうれしいですね。本物のDynamoDBのようにCREATINGACTIVEを待つ必要がありません。

# アイテム書き込み
$ 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: 2Count: 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.xpython3.13java21provided.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トークンの発行まで、一連のフローが問題なく動作しました。AccessTokenIdTokenRefreshTokenがすべて正しく返されています。

ドキュメントでは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: roottmpfsで回避可能)、Lambda利用時はDockerソケットのパーミッションにも注意が必要です。LocalStackのawslocalのようなラッパーCLIは提供されておらず、コミュニティの規模もまだ小さい(2026年3月時点でスター数約1,200)状況です。

なおLocalStackは現在、非商用限定の無料Hobbyプラン(30+サービス)を提供しています。個人の学習用途であればLocalStackも引き続き選択肢です。ただし商用利用にはBase($39/月〜)以上のプランが必要です。認証不要で商用利用も制限なしというFlociの立ち位置は、特にCI/CDパイプラインや商用プロジェクトのローカル開発環境で強みを発揮しそうですね。

この記事をシェアする

FacebookHatena blogX

関連記事