Fork me on GitHub

OTP Supervisors

Supervisors are specialized processes with one purpose: monitoring other processes. These supervisors enable us to create fault-tolerant applications by automatically restarting child processes when they fail.

Table of Contents


The magic of Supervisors is in the Supervisor.start_link/2 function. In addition to starting our supervisor and children, it allows us to define the strategy our supervisor uses for managing child processes.

Children are defined using a list and the worker/3 function we imported from Supervisor.Spec. The worker/3 function takes a module, arguments, and a set of options. Under the hood worker/3 calls start_link/3 with our arguments during initialization.

Using the SimpleQueue from the OTP Concurrency lesson let’s get started:

import Supervisor.Spec

children = [
  worker(SimpleQueue, [], name: SimpleQueue)

{:ok, pid} = Supervisor.start_link(children, strategy: :one_for_one)

If our process were to crash or be terminated our Supervisor would automatically restart it as if nothing had happened.


There are currently four different restart strategies available to supervisors:

Restart values

There are several approaches for handling child process crashes:

It’s not a required option, by default it’s :permanent.


In addition to worker processes, we can also supervise supervisors to create a supervisor tree. The only difference to us is swapping supervisor/3 for worker/3:

import Supervisor.Spec

children = [
  supervisor(ExampleApp.ConnectionSupervisor, [[name: ExampleApp.ConnectionSupervisor]]),
  worker(SimpleQueue, [[], [name: SimpleQueue]])

{:ok, pid} = Supervisor.start_link(children, strategy: :one_for_one)

Task Supervisor

Tasks have their own specialized Supervisor, the Task.Supervisor. Designed for dynamically created tasks, the supervisor uses :simple_one_for_one under the hood.


Including the Task.Supervisor is no different than other supervisors:

import Supervisor.Spec

children = [
  supervisor(Task.Supervisor, [[name: ExampleApp.TaskSupervisor, restart: :transient]])

{:ok, pid} = Supervisor.start_link(children, strategy: :one_for_one)

The major difference between Supervisor and Task.Supervisor is that its default restart strategy is :temporary (tasks would never be restarted).

Supervised Tasks

With the supervisor started we can use the start_child/2 function to create a supervised task:

{:ok, pid} = Task.Supervisor.start_child(ExampleApp.TaskSupervisor, fn -> background_work end)

If our task crashes prematurely it will be re-started for us. This can be particularly useful when working with incoming connections or processing background work.



Share This Page