What Is Configuration Drift in Microservices?
Configuration drift is when the same logical setting holds different values in places that are supposed to agree. It is rarely dramatic. It is a timeout that is 30 seconds in three services and 5 in the fourth, a feature flag enabled in the API but not the worker, a base URL that was updated everywhere except the one cron container nobody remembers. This article explains why it happens, how to find it, and what actually reduces it, whether or not you use OneEnv.
Why drift happens
- Copies, not references. Each service has its own
.env, Helm values file or secret. A change means N edits, and N is eventually remembered as N-1. - Different change paths. One service deploys from a pipeline variable, another reads a mounted file, a third has a default baked into code. There is no single place to look.
- Temporary fixes that stay. An incident override is applied by hand and never rolled back.
- No record of dependency. Nobody can list which services read
PAYMENTS_BASE_URL, so nobody knows the full set to update.
A concrete example
Four services call the payments API: web, mobile-bff, invoicer and refunder. The payments team moves the API to a new host and tells the channel. Three owners update their config. refunder runs only on the first of the month, so the failure shows up three weeks later, during a billing run, as a connection error with no obvious link to a change nobody remembers.
How to detect it
Start with an inventory: for each shared key, the value each service actually receives. If your services are configured through files in Git, a script is enough:
for key in PAYMENTS_BASE_URL REQUEST_TIMEOUT_MS; do
echo "== $key"
grep -H "^$key=" services/*/.env.production | sed 's#services/##; s#/.env.production:# #'
doneIf the values live in a platform (CI variables, a secret store, Kubernetes ConfigMaps), the same idea applies via that platform's API. The harder question is intent: a difference is drift only if the values were supposed to match. That is information the files do not contain, which is why recording the dependency matters as much as comparing values.
What reduces drift
- One source per shared value. Services reference it instead of copying it.
- An explicit consumer list. So a change can name everyone it affects.
- A reviewed change path. The same path for every service, with a record.
- A scheduled comparison. A job that fails when values that should match differ. Cheap, and it catches what process misses.
- Expiring overrides. Any manual override gets an owner and a date.
Trade-offs
- A single source becomes a dependency and a place where one bad edit has wide reach, which is the reason to review it.
- Forcing every service onto one value removes legitimate differences. Allow explicit, visible overrides rather than banning them.
- Tooling cannot tell you which differences are intentional. Someone has to say.
