Context
A playbook updates one server at a time, yet two tasks still overwrite the same load-balancer file. The server rollout is serial, but delegated work and shared resources need their own concurrency analysis. A reliable Ansible deployment describes both which hosts are processed together and where each task actually executes. Those are separate parts of the execution model.
Distinguish target hosts from execution hosts
Ansible documents delegation as running a task on another host while retaining a relationship to the original inventory host. Delegating to one machine does not automatically serialise concurrent task execution against that machine. The documentation discusses controlling shared-file access with approaches such as loops, serial batches, or task throttling. Review these semantics alongside the strategy used by your playbook.
Write a small execution map for the rollout. Identify application hosts, control-node tasks, load-balancer operations, database actions, and shared files. Mark which steps may run concurrently and which require one authoritative writer. This exposes races that remain invisible when looking only at the hosts line.
An illustrative load-balancer race
Imagine a playbook draining each web server before updating it. Several inventory hosts delegate a configuration-file edit to one load-balancer host. Their edits race, and the final file omits one intended backend. The application updates succeed, but traffic distribution is now wrong.
Use a supported API or a single rendered desired configuration where possible instead of independent competing file edits. If multiple hosts must contribute data, gather the desired state and apply it through one controlled task. Validate the resulting configuration before reloading the load balancer.
Choose a batch size from capacity
The serial setting should reflect how much serving capacity can be removed safely. Include peak traffic, startup time, cache warm-up, and dependency capacity. A batch of one may still be too disruptive if only one instance exists or the remaining instances cannot carry the workload.
Test the chosen batch with representative traffic. Confirm the remaining pool stays within its operating objective while the selected host drains and restarts. Keep an abort condition based on application health, not merely whether the package-install command returned successfully.
Review run_once scope
Tasks intended to run once need scrutiny when the play is divided into batches or uses a non-default strategy. Review the documented interaction rather than assuming run_once always means once for the entire release in every arrangement.
A database migration, global notification, or shared configuration update may need a separate play or explicit ownership rule. Keep these actions out of a per-host update loop unless repeating them is deliberate and safe. Record the release identifier so global work can recognise an already completed attempt.
Handle failure and rescue carefully
If an update fails after draining a server, decide whether the host should remain drained or return to traffic. Returning a broken instance automatically can be worse than leaving reduced capacity. The rescue path should evaluate the application's verified state.
Record both the failed action and current traffic membership. A generic always block that re-enables every host can erase the safety gained by draining. Use explicit postconditions and stop the next batch when serving capacity or correctness falls below the defined threshold.
Verify each host before rejoining
Check application version, configuration adoption, dependency access, and a representative request. A local health endpoint may be necessary but insufficient if the user path depends on a proxy or shared session store. Include the relevant path in acceptance.
After rejoining the pool, observe the host under real routing before continuing. Look for elevated errors or unexpected latency. The rollout should progress because the updated host is useful, not only because the process is running.
Make shared state reproducible
Keep load-balancer desired state, application release, and host inventory associated with the same change. Avoid retaining an intermediate configuration that only existed during the deployment as the new permanent baseline.
Re-running the playbook should converge from a partially completed batch. Test a failure after one host succeeds and another remains drained. The next attempt should identify their different states and avoid repeating global side effects unnecessarily.
Quick diagnostic map
| Evidence | Interpretation | Next check |
|---|---|---|
| Shared file loses an edit | Delegated writers compete | Use single-writer update |
| Global action repeats | Batch scope misunderstood | Inspect run_once semantics |
| Broken host rejoins traffic | Rescue condition too broad | Require application verification |
A focused operator exercise
Use a test pool with enough capacity to remove one application host safely. Record initial traffic membership, application version, and shared configuration. Run the chosen serial batch while tracing where each task executes. Inspect delegated operations as a separate population so shared-host writes are not assumed safe merely because the application hosts update in sequence.
Introduce a failure after drain and before application verification. Confirm the rescue path leaves the host in a state justified by evidence and stops the next batch when required. Then resume the playbook and verify already completed global actions are recognised. Include a test showing how run_once behaves with the selected batch and strategy configuration.
Finally, compare load-balancer desired state with actual membership and each host's observed release. Retain a per-host result plus a separate record for shared actions. The exercise passes when partial completion, rerun, and failed rejoin remain understandable, and the rollout never relies on concurrent file edits accidentally producing the correct final shared configuration.
Hand over a readable deployment receipt
Report each host's previous version, selected action, drain result, application verification, and final pool membership. Include global tasks separately so the operator can see whether they ran once, per batch, or per host.
Clouvenix can help review Ansible execution semantics and load-balancer coordination together. The outcome is a rolling update that controls shared resources as carefully as it controls the application servers.
Frequently asked questions
Does delegate_to localhost make a task sequential?
No. Several inventory hosts can delegate work to the same execution host concurrently. Protect shared resources through the appropriate play structure, single-writer design, or documented concurrency control.
Should a failed host always be added back to the pool?
Only if application verification shows it is safe to serve. Keep the host's state and remaining capacity visible, then follow the defined rescue or rollback procedure instead of unconditionally restoring traffic.
Explore Clouvenix ansible automation for implementation and operational handover.