Configuration gets harder to trust as the distance grows between a service and the facts required to run it. The distance isn't measured only in directories. It shows up in extra repositories, dashboard-only settings, shell history, copied environment files, and instructions that say “remember to change this over there too.”
My choice is to keep non-secret configuration beside the service definition that consumes it, with secret references and machine-specific values clearly separated. That usually means the service's versioned directory contains its compose or startup definition, a documented example of required variables, small policy files, and the commands used to check the assembled configuration.
This doesn't make every deployment identical, and it doesn't put credentials into version control. It gives the service a readable neighborhood. Someone looking at how it starts can also discover what it expects without touring several interfaces and my memory.
Decision
For services I operate, the local service directory is the home for configuration that describes behavior: enabled features, mounted paths by role, network attachments, resource policy, routing metadata, and references to secrets. Changes to those facts travel with changes to the service definition.
The important word is references. A configuration file can say that a credential must be supplied under a particular variable or secret name. The credential value still belongs in an appropriate protected store. Keeping configuration close isn't permission to turn a repository into a junk drawer for tokens.
I also keep a checked-in example for values that vary by environment. The example explains shape and purpose without carrying private values. If a variable is required, the startup path should fail clearly when it's absent. A blank default that quietly selects surprising behavior is convenient only until I have to explain it.
The assembled result needs to be inspectable. Tools that merge a base file, an environment file, and command-line overrides can produce a configuration no single source displays. I use the runtime's render or validation command before applying a change, and I scan the output for the service, mounts, networks, and variable names I intended. Sensitive values shouldn't be printed into logs just to satisfy this habit.
Why proximity wins
Close configuration reduces a particular kind of error: changing the implementation while forgetting the operational assumption beside it. If a new component requires a volume, route, or setting, the review can show both changes together. If the setting lives in a separate manual control panel, the code change looks complete while the deployment remains a scavenger hunt.
Proximity also improves deletion. Removing a service should reveal the files, scheduled hooks, and configuration that exist only for it. Central piles of configuration are much better at accumulation than retirement. An old variable can survive for years because nobody can prove which service still reads it.
There is a debugging benefit too. When a process behaves differently from its definition, I can compare the nearby intended configuration with the rendered runtime configuration. The gap is bounded. If the intended facts are distributed across a wiki page, a web form, and an untracked export, diagnosis begins with archaeology.
I don't expect proximity to eliminate drift. A person can still edit a live system. A runtime can still retain state after the file changes. That's why the directory includes the read-back command or a short note describing how to inspect effective configuration. The file is a proposal until the running service agrees.
Tradeoffs and exceptions
The tradeoff is duplication. Several services may repeat the same logging, update, or network settings. A central abstraction could express them once. Sometimes that's worth doing, especially when a policy must change consistently across many services. But sharing a setting also couples the consumers. A harmless edit to a common fragment can become a wide deployment change.
My default is a little repetition with clear ownership. I extract a shared mechanism only after the repetition is stable and its consumers really need to move together. Even then, the service directory should make the dependency obvious and pin or identify what it consumes. “Inherited from somewhere” isn't documentation.
Machine-specific configuration is another exception. Device paths, hardware capabilities, and local mount locations may properly live with the host. The service should refer to a named capability instead of pretending every machine has the same shape. I keep the boundary explicit: the service declares what it needs; the host mapping says how this machine supplies it.
Secrets require the strongest exception. Their values stay out of the nearby versioned files. What stays close is the contract: required name, expected scope, rotation consequence, and the safe method by which the runtime receives it. A secret nobody can discover is secure in the same sense that a lost key is secure.
There is operational cost in maintaining these neighborhoods. Examples need updates. Validation commands need to keep working. A directory can become cluttered. I accept that cost because it is visible and local. The alternative cost arrives during a change, when the person doing the work has to discover an invisible dependency under pressure.
The decision has a simple test. Starting from the service definition, can I determine what non-secret behavior is configured, what external values are required, and how to inspect the effective result? If the answer depends on a tour of unrelated systems or on me being available, the configuration is too far away. I move the contract closer before adding another layer of instructions that explains the distance.