Why does my flag work locally but not in Docker?
Last updated:
Almost always because the SDK key did not reach the container. A key read from a local env file that is not passed through in your compose file or Dockerfile arrives empty, and the SDK serves your default value rather than failing loudly. The second cause is outbound network: a container on an internal-only network cannot reach the evaluation endpoint, so the first payload never arrives.
Your code did not change between the two runs. The difference is in what reaches the process, and two causes cover nearly all of it, both of them failing quietly by design because that is what you want from a flag system that loses its configuration in production.
A missing SDK key and a blocked network both end with an empty flag snapshot, and an empty snapshot means every call returns the default value you passed. That is the fail-safe behaviour you want in production, but it does make local and containerised runs diverge without an obvious error.
Check the key first
The usual version of this is a key that your shell has and your container does not.
# On the host: the key is in your shell or a local env fileecho $FEATUREFLIP_SDK_KEY # sdk_server_...
# In the container: nothing was passed throughdocker compose exec api sh -c 'echo "[$FEATUREFLIP_SDK_KEY]"' # []A local env file that your dev server loads automatically stays invisible to a container unless your compose file passes it through. Log the key’s prefix at startup, never the key itself, and you will catch this in seconds without opening a single configuration file.
While you are there, confirm it is the right kind of key. A server key sees every flag in the project, a client key sees only the flags explicitly marked client-side visible. Container work often involves moving evaluation from a browser to a backend or the reverse, and the key needs to move with it.
Then check outbound access
A container attached only to an internal network cannot reach the evaluation endpoint. The SDK will keep retrying and keep serving defaults in the meantime.
Confirm from inside the container, not from the host, since the container’s namespace is the one that matters. If your environment requires an egress proxy, the SDK’s HTTP client needs to know about it, and a long-lived streaming connection needs a proxy that does not buffer responses.
The third possibility
If both check out, compare which environment each run is pointed at. A key for your development environment and a key for production are different keys, and a flag enabled in one is routinely disabled in the other. That is a different question, but it produces the same symptom.
Key types are covered in can I use one SDK key for every environment, and fallback behaviour in Reliability.
Related questions
Why does my flag work in dev but not production?
Flags are configured per environment, so enabling one in development changes nothing elsewhere. Rules and rollout percentages are per environment too.
Can I use one SDK key for every environment?
No. The SDK key is what selects the environment, so sharing one collapses dev and production onto the same flag configuration.
Why is my feature flag returning the default value?
Six reasons a Featureflip flag falls back to the default you passed, ordered by how often each one is the real cause, with the check for each.
Still stuck?
The docs cover every SDK, and the free Solo plan is enough to reproduce most of these locally.