GitHubのトレンドを眺めていたら、8位に floci-io/floci が入っていました。説明は「Light, fluffy, and always free - The AWS Local Emulator alternative」。スターは28,178で、その日だけで219増えています。言語は Java、ライセンスは MIT です。
README には、LocalStack のコミュニティ版が2026年3月に終わり、認証トークンが必要になったという一文があり、floci はその「no-strings-attached alternative」だと書かれています。S3 や SQS を使うコードのテストを CI でどう回すかは、私にとってもずっと悩みどころでした。運用を考えると、AWS アカウントも認証トークンも要らずに本物に近い動きをしてくれるなら、それだけで十分に価値があると考えています。
ただ、今回はルールとして、検証は使い捨てのコンテナの中で行い、その中でさらに Docker は動かしません。README の Quick Start は floci start(別リポジトリの CLI)か docker compose up です。そこで「Docker なしでどこまで使えるのか」を確かめることにしました。
試した環境
- 使い捨てのDockerコンテナ(
eclipse-temurin:25-jdk、Ubuntu 26.04.1 LTS)。メモリ4GB・CPU2つに制限し、Docker のソケットは渡していません - Temurin 25.0.5+8 / Maven 3.10.0(リポジトリ同梱の
mvnwが取得したもの) - floci: リリース 2.4.0 のタグ(
9be236e、2026-10-31) - AWS CLI 2.39.3
入れ方の設計判断:Docker を使わない経路はどれか
README には「No Docker?」という一節があり、各リリースに Linux・macOS・Windows 向けの単体バイナリ floci-aws が付く、とあります。インストールの文書にも、releases/download/ から floci-aws-linux-amd64 を curl で取ってくる手順が載っています。最初はこれで行くつもりでした。
ところが、2.4.0 の URL は 404 でした。API で見ると、2.4.0 のリリースに付いているファイルは0件です。2.3.0〜2.1.0 も0件でした。
不思議に思って、公開されているワークフローの実行結果を見に行きました。2.4.0 のリリース処理(release.yml)は failure で終わっていて、ジョブごとに見るとこうなっていました。
| ジョブ | 結果 |
|---|---|
| Build native artifact(amd64 / arm64) | success |
| Push native and compat Docker images | success |
| Build standalone binary(windows-amd64) | failure(Smoke-test the standalone binary の段) |
| Build standalone binary(darwin-arm64 / darwin-amd64) | cancelled |
| Publish standalone binaries | skipped |
Docker イメージは公開まで進み、単体バイナリだけが Windows 版のスモークテストで止まって、Linux 版も含めて公開されなかった、という形です。バイナリを添付する仕組みは10月17日のコミット(#5844)で入ったもので、その2日前に出た 2.3.0 にはもともとありません。つまり 2.4.0 が最初の機会でした。Docker で使う人には影響が無いので、気づかれにくい種類の漏れだと思います。
残る経路は、インストール文書の「Build from Source」です。必要なのは Java 25 以上と Maven 3.9 以上で、手順は2行でした。
最初のつまずき:README どおりのビルドが落ちる
# README の手順: ./mvnw clean package -DskipTests
[INFO] Compiling 2996 source files with javac [debug release 25] to target/classes
[INFO] Compiling 3297 source files with javac [debug release 25] to target/test-classes
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.16.0:testCompile (default-testCompile) on project floci: Compilation failure
[ERROR] unknown enum constant org.immutables.value.Value.Style.ImplementationVisibility.PACKAGE
[ERROR] reason: class file for org.immutables.value.Value$Style$ImplementationVisibility not found
# build rc=1 elapsed=148646 ms
# テストのコンパイルごと飛ばす: ./mvnw clean package -Dmaven.test.skip=true
[INFO] Building jar: /work/floci/target/floci-2.4.0.jar
[INFO] BUILD SUCCESS
# build #2 rc=0 elapsed=38256 ms
96M target/quarkus-app
340M /root/.m2
-DskipTests はテストを「実行しない」だけで、テストのコードはコンパイルします。落ちたのはそのテストのコンパイル(3,297ファイル)でした。本体の2,996ファイルは通っています。上流の CI も ./mvnw test-compile を回しているので、どこでも落ちるわけではないはずです。今回の JDK 25.0.5 なのか、依存の取り合わせなのか、原因までは確かめていません。
回避は -Dmaven.test.skip=true で、テストのコンパイルごと飛ばします。これで38秒で通り、target/quarkus-app にjarが229個並びました。
正直なところ、これは「動かして使うだけ」なら十分ですが、自分で floci に手を入れてテストを回したい人には使えない逃げ道です。ここは使い方次第だと考えています。
起動:2.2秒、そして1行の WARN
java -jar target/quarkus-app/quarkus-run.jar で起動すると、ヘルスチェックが200を返すまで2,487ミリ秒でした。待ち受けは 127.0.0.1:4566 です。
WARN [io.git.hec.flo.lif.DockerAvailabilityReport] Docker is not reachable at unix:///var/run/docker.sock (java.net.SocketException: No such file or directory). Without it these services keep their control plane but run no containers: [rds, elasticache, memorydb, opensearch, neptune, docdb, msk, eks, timestreaminfluxdb, ec2]; and these fail when asked to run one: [lambda, ecs, codebuild, batch, sagemaker, appsync, pipes (Kafka sources), mwaa, amazonmq, kinesisanalytics, redshift, redshift-serverless, rds-data, athena, ecr, codeartifact, verifiedpermissions]. Everything else works without Docker.
INFO [io.quarkus] floci 2.4.0 on JVM (powered by Quarkus 3.40.1) started in 2.164s. Listening on: http://127.0.0.1:4566
# /_floci/health を jq で数えたもの
{"n":129,"byStatus":{"running":129},"lambda":"running","ecs":"running"}
Docker が無いことは、起動時の WARN 1行に全部書いてあります。コンテナを動かさない10サービスと、頼まれたら失敗する17サービスです。親切な書き方だと思います。
一方で、ヘルスチェックの /_floci/health は129サービスすべてを running と返しました。WARN で「失敗する」と名指しされた lambda も ecs も running です。CI で「ヘルスチェックが通ったら次へ進む」と書くのはよくある形なので、Docker が要るサービスを使うテストでは、このヘルスチェックだけでは準備ができたとは言えない、という点は覚えておく必要があります。
メモリは、起動直後の RSS で307MBでした。インストール文書には、Docker イメージ(中身はネイティブ版)について起動約24ms・メモリ約13MiBという数字がありますが、今回の JVM 版とは比べられません。私が測れたのは JVM 版の方だけです。
README の Quick Start と、自分の使い方
README の3つのコマンド(aws s3 mb / aws dynamodb create-table / aws dynamodb list-tables)は、create-table の出力を --query で短くした以外は書かれたとおりに打って、すべて終了コード0でした。環境変数は文書どおり AWS_ENDPOINT_URL=http://localhost:4566 で、キーは test / test です(floci env は CLI 側の機能なので使っていません)。
本当に知りたかったのは、私がよく組む「S3 に置いたら SQS に通知が来て、処理済みの印を DynamoDB に付ける」流れが通るかどうかです。
$ aws s3api put-bucket-notification-configuration --bucket inbox \
--notification-configuration '{"QueueConfigurations":[{"QueueArn":"arn:aws:sqs:us-east-1:000000000000:uploads","Events":["s3:ObjectCreated:*"]}]}'
$ aws s3 cp /tmp/hello.txt s3://inbox/hello.txt
$ aws sqs receive-message --queue-url http://localhost:4566/000000000000/uploads --wait-time-seconds 10 --max-number-of-messages 10
# receive-message took 406 ms, messages=2
# 1件目の Body
{"Service":"Amazon S3","Event":"s3:TestEvent","Time":"2026-11-03T12:11:31.630278407Z","Bucket":"inbox","RequestId":"e027eb9c-6097-4970-9947-548920f2e5b9","HostId":"e0705b7f-9846-4f3b-8733-936d2d96159c"}
# 2件目の Body から jq で項目を抜き出したもの
{"eventSource":"aws:s3","eventName":"ObjectCreated:Put","awsRegion":"us-east-1","bucket":"inbox","key":"hello.txt","size":12,"eTag":"5d41071e084f4ff2d7a809e02a1c2fe5"}
# md5 of the file: 5d41071e084f4ff2d7a809e02a1c2fe5
$ aws dynamodb put-item --table-name demo-table --item '{"pk":{"S":"hello.txt"},"status":{"S":"again"}}' --condition-expression 'attribute_not_exists(pk)'
aws: [ERROR]: An error occurred (ConditionalCheckFailedException) when calling the PutItem operation: The conditional request failed
# rc=254
通知は2件来ました。1件目は s3:TestEvent で、時刻は通知を設定した 12:11:31。ファイルを置いた 12:11:32 より前のものです。この1件には Records がありません。受け側のコードが Records を前提にしていると、ここで落ちます。本番向けに書いた受け側をそのまま当てて、手元で一度つまずけるのは、むしろありがたいと考えています。
2件目が本物の ObjectCreated:Put で、eTag はファイルの MD5 と一致しました。DynamoDB の条件付き書き込みも、2回目は ConditionalCheckFailedException で弾かれ、項目は1回目の status=done のまま残っています。
この3点が通るなら、私の用途のテストはかなりの部分を floci で回せそうです。
Docker が無いときの Lambda は「終了コード0」で失敗する
WARN で「失敗する」と書かれていた Lambda も試しました。ok を返すだけの小さな Python 関数を zip にして作り、そのまま呼んでいます。
$ aws lambda create-function --function-name hello --runtime python3.12 --handler app.handler --role arn:aws:iam::000000000000:role/lambda-role --zip-file fileb:///tmp/fn/fn.zip --query '[FunctionName,State,StateReasonCode]' --output text
hello Active None
# rc=0
$ aws lambda invoke --function-name hello /tmp/out.json
{
"StatusCode": 200,
"FunctionError": "Unhandled",
"ExecutedVersion": "$LATEST"
}
# rc=0
# invoke took 417 ms
$ cat /tmp/out.json
{"errorMessage":"Failed to start Lambda container: Docker is not reachable (No such file or directory). Lambda functions run in Docker containers.","errorType":"Lambda.InitError"}
関数の作成は Active で通り、invoke もコマンドとしては終了コード0、StatusCode は200です。失敗は FunctionError と出力ファイルの中身にだけ出ています。CI のスクリプトで aws lambda invoke の終了コードだけを見ていると、この失敗は素通りします。Lambda を呼ぶテストを floci に載せるなら、FunctionError を必ず見る書き方にしておくことが重要です。
もう一つ気づいたのは、関数を作った時点で awslambda-us-east-1-tasks というバケットが増えたことです(ログにも Created bucket: awslambda-us-east-1-tasks と出ます)。自分では作っていないので、「バケットの一覧が空であること」を確かめるようなテストを書いていると、ここで数が合わなくなったりします。
既定の「ゆるさ」と、それを締める設定
最後に、認証と再起動の扱いを確かめました。
# 既定の設定のまま、秘密鍵をわざと間違える
$ env AWS_SECRET_ACCESS_KEY=definitely-wrong aws s3 ls
2026-11-03 12:11:31 inbox
2026-11-03 12:11:21 my-bucket
2026-11-03 12:11:34 awslambda-us-east-1-tasks
# rc=0
# FLOCI_AUTH_VALIDATE_SIGNATURES=true で起動し直したあと
$ env AWS_SECRET_ACCESS_KEY=definitely-wrong aws s3 ls
aws: [ERROR]: An error occurred (SignatureDoesNotMatch) when calling the ListBuckets operation: The request signature we calculated does not match the signature you provided. Check your key and signing method.
# rc=254
$ env AWS_ACCESS_KEY_ID=AKIAEXAMPLEUNKNOWN AWS_SECRET_ACCESS_KEY=x aws s3 ls
aws: [ERROR]: An error occurred (InvalidAccessKeyId) when calling the ListBuckets operation: The AWS Access Key Id you provided does not exist in our records.
# rc=254
既定では、秘密鍵は何でも通ります。文書にも「the secret access key can be any non-empty string」とあるので仕様どおりです。FLOCI_AUTH_VALIDATE_SIGNATURES=true を付けて起動し直すと、test / test はそのまま通り、間違った鍵だけが弾かれました。SQS に同じことをすると InvalidSignatureException で、サービスごとにそのサービスらしいエラー名で返ってきます。
再起動の扱いも含めて、表にしておきます。
| 確かめたこと | 既定 | 設定を足したとき |
|---|---|---|
| 秘密鍵を間違えて s3 ls | 通る(rc=0) | FLOCI_AUTH_VALIDATE_SIGNATURES=true で SignatureDoesNotMatch(rc=254) |
| 知らないアクセスキーで s3 ls | 通る(rc=0) | 同上で InvalidAccessKeyId(rc=254) |
| 再起動したあとの中身 | 全部消える(s3 ls は空、テーブルは0個) | FLOCI_STORAGE_MODE=persistent で、バケット・オブジェクト・項目が残る(保存先に205ファイル・844KB) |
| ヘルスチェックが200になるまで | 2,357ms(2回目の起動) | 署名検証ありで2,308ms、永続化ありで2,479ms・2,650ms |
永続化の保存先には、今回触っていない EC2 や RDS の分も含めて205個のファイルができていました(名前が ec2 で始まるものが33個、rds が20個)。合計は844KBです。どちらの設定を足しても、起動の時間はほとんど変わりませんでした。
試してみての所感
お金の面では、AWS アカウントも認証トークンも要らないので0円です。代わりに手元のマシンのコストを並べると、今回の JVM 版はビルドに約3分(1回目の失敗148.6秒と2回目の38.3秒)、Maven のキャッシュが340MB、成果物が96MB、起動後の RSS が307〜341MBでした。README の比較表にある約40MBの単体バイナリが届けば、このうちビルドの分はまるごと要らなくなります。今回はそれが手に入らなかった、というのが正直な結果です。
私は、S3・SQS・DynamoDB のように Docker が要らないサービスのテストから floci に載せていくつもりです。その際は FLOCI_AUTH_VALIDATE_SIGNATURES=true を付けて、鍵の取り違えも手元で落ちるようにします。Lambda を呼ぶテストは、FunctionError を見る形に直してからにします。
Docker が要るサービスと単体バイナリは、次のリリースで添付されるのを待って、改めて試したいと考えています。
今思えば、README の「No Docker?」を読んだ時点で、リリースの添付ファイルを一度見に行っておけば、もう少し早く経路を決められたかもしれません。