When to Use one_for_one vs one_for_all in Erlang Supervisors
Match Erlang OTP restart strategies to your failure domain so a crashing worker does not restart unrelated services.
22 Mar 2026, 11:36 UTC

A crashing worker should not take down its siblings
Imagine a supervision tree with three workers: a TCP acceptor, a JSON parser, and an alert publisher. The parser receives a malformed message and exits. If the tree is configured with the wrong restart strategy, the supervisor kills and restarts the acceptor and publisher too. Those services were healthy, but they go down anyway because the restart strategy treats them as a single failure domain.
The useful takeaway is simple: match the restart strategy to how tightly coupled your children are. Independent workers should fail independently; tightly linked services should recover together.
What a restart strategy controls
In Erlang/OTP, a supervisor is a process that monitors other processes, called children, and restarts them when they terminate. Each child is described by a child specification, which includes fields such as restart, shutdown, and type. The supervisor itself is started with supervisor:start_link/2 and declares one of three restart strategies.
OTP is the Open Telecom Platform, the set of libraries and design principles that ship with Erlang. A supervision tree is the hierarchy of supervisors and workers built on top of OTP behaviours.
Choose the strategy by failure domain
| Strategy | What happens when a child exits | Best for |
|---|---|---|
one_for_one | Only the failed child is restarted. | Independent workers that do not share state or depend on each other. |
one_for_all | All children are terminated, then all are restarted. | Tightly coupled children that must start and stop as a unit. |
rest_for_one | The failed child and any children started after it are terminated and restarted. | Ordered start-up dependencies where later children assume earlier ones are running. |
The child specification's restart field further refines this: permanent children are always restarted, transient children are never restarted, and temporary children are restarted only if they exit abnormally. Combine this with the strategy to define exactly which processes come back after a failure.
Worked example: watch a restart in the Erlang shell
Run this in the Erlang shell (erl) as a normal user. No root or special permissions are required.
1> {ok, SupPid} = supervisor:start_link({local, ?MODULE}, ?MODULE, []).
Replace ?MODULE with your supervisor module name. The module must export init/1 and return a supervisor specification with your chosen strategy and child specifications.
To observe the strategy, find the process identifier of a running child and exit it:
2> exit(ChildPid, kill).
Then inspect the supervisor:
3> supervisor:which_children(SupPid).
Expected check: after the exit, the list returned by supervisor:which_children/1 should show the failed child either restarted or absent, depending on its restart type, while unrelated children remain untouched under one_for_one. Under one_for_all, every child in the list will show a new start time or restart count.
Risk: if children crash repeatedly, the supervisor may hit its max_restart intensity limit and terminate itself, taking all children with it. Tune max_restart and max_seconds for the expected failure rate, not for the worst-case burst.
Cleanup: stop the supervisor with exit(SupPid, normal). or leave the shell with q().
Limits: restarts do not repair corrupted state
A supervisor restarts processes, but it does not repair shared state. If a crashed worker left an ETS table or Mnesia schema in an inconsistent state, the restarted worker will inherit that corruption. Clean shared state separately, or design children to rebuild it on start-up.
Also, set the shutdown timeout long enough for a child to drain in-flight work. An overly short timeout kills the process mid-operation, which can leave external resources locked or half-written.
Decide before you deploy
- Use
one_for_oneas the default for stateless, independent workers. - Use
one_for_allonly when children genuinely cannot operate without each other. - Use
rest_for_onewhen start-up order matters but later children can tolerate the earlier ones being restarted. - Verify in a shell session with
supervisor:which_children/1before promoting the configuration.
Restart strategy is a boundary decision, not an implementation detail. Draw the boundary where failures should stop, and let the supervisor enforce it.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.