
The story of Jest hanging in CI, and the story of migrating to Floci because LocalStack became paid
This page has been translated by machine translation. View original
Introduction
"All tests are passing, but CI won't finish."
Have you ever experienced this? I ran into exactly this issue recently. On GitHub Actions, after all 44 server tests (Jest) passed, the Jest process would not exit and the job kept hanging.
While investigating, I tried to reproduce the issue locally and discovered that LocalStack had discontinued its community edition in March 2026. Searching for an alternative tool, I came across Floci.
This article summarizes the entire investigation and the steps taken to address it.
The Problem of Jest Hanging in CI

Symptoms
Looking at the GitHub Actions logs, the following message appeared.
Tests: 44 passed, 44 total
Ran all test suites.
Jest did not exit one second after the test run has completed.
'This usually means that there are asynchronous operations that weren't stopped
in your tests. Consider running Jest with `--detectOpenHandles` to troubleshoot
this issue.'
All tests passed. However, the Jest process would not exit, and the job kept waiting for a timeout or cancellation without executing subsequent steps.
Cause: Open Handles
When Jest cannot exit, the cause is usually asynchronous connections or timers that have not been closed somewhere. Common examples include:
- AWS SDK clients (DynamoDB / S3, etc.) holding open connections
- HTTP servers that have not been
close()d setInterval/setTimeoutthat have not been cleared withclearInterval/clearTimeout
Investigation: --detectOpenHandles
Jest has an option for identifying the cause.
jest --detectOpenHandles
Running with this option outputs which handles remain open, along with a stack trace showing which file and line they were created on.
● Jest has detected the following 1 open handle potentially keeping Jest from exiting:
● TCPWRAP
17 | const client = new DynamoDBClient({ region: "ap-northeast-1" });
| ^
This makes it immediately clear which AWS SDK client is the cause.
Temporary Fix: --forceExit
Before applying a root-cause fix (such as calling destroy() on clients in afterAll), you can use --forceExit as a temporary measure to keep CI from stopping.
Simply add it to the test script in package.json.
{
"scripts": {
"test": "jest --runInBand --forceExit"
}
}
--forceExit forcibly terminates Jest after the tests complete. Open handles will remain, but CI will no longer hang. Combining it with --detectOpenHandles allows you to output diagnostic information to CI logs while still exiting normally.
"test": "jest --runInBand --detectOpenHandles --forceExit"
Key Point: When the Issue Does Not Reproduce Locally

In this case, the issue did not reproduce locally even with --detectOpenHandles, and Jest exited normally.
The reason is that locally, a mock tool (Floci, described later) is used to emulate AWS, so all AWS API calls are contained locally. In CI, it is believed that connections to actual AWS services (such as Kendra) remained open, causing the hang.
In this situation, a practical approach is to use --forceExit to allow CI to exit normally while using the --detectOpenHandles logs to identify the root cause.
LocalStack Had Become a Paid Service
When I tried to reproduce the issue locally, I hit a wall when attempting to use LocalStack.
What Happened
License activation failed! 🔑❌
Reason: No credentials were found in the environment.
Please make sure to either set the LOCALSTACK_AUTH_TOKEN variable to a valid auth token.
Previously, it should have been possible to start with just docker run localstack/localstack, but now it requires an auth token and will not start.
Discontinuation of the LocalStack Community Edition
Upon investigation, it turned out that LocalStack discontinued its community edition (free, no license required) on March 23, 2026.
| Period | Status |
|---|---|
| Up to March 2026 | The localstack/localstack image was available for free |
| After March 2026 | An auth token is required for all images |
An image of older versions called localstack/localstack:community-archive exists, but commercial use requires separate verification. The free tier for free accounts is explicitly stated to be for "personal, non-commercial" use.
For use in business projects, you either need to obtain a commercial license or migrate to an alternative tool.
Floci: A LocalStack-Compatible Tool with MIT License
When searching for a LocalStack alternative, Floci emerged as a strong candidate.
What is Floci?
- MIT license (commercial use allowed, free)
- Drop-in replacement for LocalStack Community (same port
4566, same credential format) - Emulates 50+ AWS services (DynamoDB, S3, SQS, Lambda, etc.)
- No auth token required
Since it uses the same endpoint as LocalStack (http://localhost:4566), you can switch over without changing any existing code.
Setup Instructions
1. Start Floci
docker run -d -p 4566:4566 floci/floci
Verify it is running:
curl -s http://localhost:4566/_localstack/health
You are good to go if "dynamodb":"running" and "s3":"running" are displayed.
2. Set Up AWS Resources
Since Floci does not persist data, you need to create tables and buckets every time it starts. If you have an existing setup script for LocalStack, you can use it as-is.
It works simply by setting dummy AWS credentials (Floci does not validate credentials).
AWS_ACCESS_KEY_ID=test AWS_SECRET_ACCESS_KEY=test node scripts/setup.js
3. Run the Tests
AWS_ACCESS_KEY_ID=test AWS_SECRET_ACCESS_KEY=test jest
If you are using Colima instead of Docker Desktop, start Colima first.
colima start
docker run -d -p 4566:4566 floci/floci
Comparison with LocalStack
| Item | LocalStack (after 2026) | Floci |
|---|---|---|
| License | Paid plan required for commercial use | MIT (commercial use allowed) |
| Auth token | Required | Not required |
| Port | 4566 | 4566 (compatible) |
| Number of supported services | Many (expanded with Pro plan) | 50+ |
| Startup speed | Somewhat slow | Fast (24ms) |
Summary
To summarize the actions taken:
- Jest hanging → Identify the cause with
--detectOpenHandles, apply a temporary fix with--forceExit. If it does not reproduce locally, the real AWS connections in CI may be the cause. - LocalStack becoming paid → After March 2026, business use requires a paid plan or an alternative tool.
- Migration to Floci → MIT license, LocalStack-compatible, no auth token required, and can be used as a drop-in replacement.
"CI hanging only in CI but not locally" is a tricky situation, but adding --detectOpenHandles --forceExit to CI and reviewing the logs is a reliable, if methodical, way to investigate. Regarding LocalStack's move to paid plans, alternatives like Floci are increasingly available, so I recommend choosing a tool that fits your licensing requirements.