A container management screen can make one small change feel harmless. Select a running service, adjust an environment value, click redeploy, and the new behavior appears. The repository remains untouched. No syntax to remember, no file to open, no review needed.
The convenience is real. So is the second system it just created.
The first system is the declared one: Compose files, environment templates, mounted configuration, and the version history around them. The second is the effective state stored by the management tool or container runtime after somebody changed it through the interface. Both can start a service. Only one may contain the value that made the current service work.
This isn’t an argument against administrative interfaces. I use them to inspect state and to perform bounded operations. The problem begins when a convenient mutation becomes configuration without joining the system that configuration is supposed to live in.
The runtime fork
The fork occurs when an interface reconstructs or replaces a workload from values it controls. A changed environment variable may be stored in the tool’s database. A network attachment may exist only on the current runtime object. A volume mapping may be altered without changing the declarative file that another operator expects to rebuild from.
Everything can look healthy after the change. That’s what makes the fork durable. The effective container has the right value, traffic flows, and the person who made the adjustment moves on. The repository is now an incomplete description of the system, but it doesn’t emit an error to announce that fact.
The discrepancy usually appears during recreation. A host restarts, an image update rebuilds the container, or someone runs the declared configuration on another machine. The service returns to the repository’s older state. It feels like the update caused a regression. In reality, the update removed an undocumented overlay.
Two lifecycles, one name
Once the fork exists, ordinary words become ambiguous. “Redeploy” might mean apply the checked-in definition, repeat the management tool’s stored settings, or recreate whatever the runtime currently reports. “Current configuration” might mean the intended file or the observed container inspection. Even “rollback” depends on which history contains the change.
That ambiguity spreads into networking quickly. A manually attached network can make one service name resolve from a container that the declared topology never placed there. A published port can bypass the route everyone assumes is required. A convenient hostname entry can survive in one runtime while remaining absent from the rebuilt environment. The network diagram hasn’t changed, but the packets have found a second draft.
Secrets make the situation worse. An interface may allow a value to be entered safely without displaying it later. That can be appropriate, but if the corresponding secret-management path isn’t updated, the runtime depends on a value nobody can reproduce from the declared system. Copying it back into a repository would be the wrong correction. The right correction is to put it in the approved secret source and reference it from the definition.
Drift hides in successful inspection
Runtime inspection is useful, but it can encourage the wrong recovery method. If I inspect a working container and copy every observed field into a new definition, I may preserve defaults, generated values, and transient attachments as if they were all intended architecture.
I compare at the level of decisions instead. Which image identity should run? Which configuration keys are intentionally set? Which networks and storage mounts are required? Which ports should be reachable, and through what path? The runtime supplies evidence. The declared files supply intent. A difference needs classification, not automatic synchronization.
Some runtime differences are normal. Platforms add labels, internal identifiers, generated names, and implementation details. Treating every difference as drift creates busywork and teaches people to ignore comparison output. The useful comparison is narrow enough to identify operator-controlled fields.
The management interface should also reveal provenance when it can. If a workload came from a stack definition, changes should return to that definition. If the tool creates its own durable application object, that object needs an export and an explicit owner. A screen isn’t disqualified from being authoritative. It simply can’t become authoritative by surprise.
Choose where changes become real
My rule is that durable changes become real in one declared path. For containerized services I maintain, that’s usually the versioned service definition plus the appropriate external secret source. The administrative interface may help me inspect, stop, start, or test a hypothesis. If a hypothesis becomes the fix, I reproduce it in the declared path and rebuild from there.
That last rebuild is the proof. I don’t accept the interface-modified container as evidence that the definition is correct. I remove the hand-edited instance through the normal mechanism, create it from the declared inputs, and verify the required behavior. If I’m unwilling to do that, I should admit that the interface is currently the source and document it accordingly.
There is a tradeoff. A repository-centered path is slower during a small adjustment. It asks for an edit, a diff, and a deliberate recreation where a screen offered one click. I accept that friction because the next restart will perform the same reconstruction whether I’m ready or not.
Convenience is most useful when it shortens an operation without creating a second memory of the system. I’ll keep the management screen. I just won’t let a successful click become the only place the working configuration exists.