By Sergey Gernyak, Back-end Engineer.
Mostly when you create your new Elixir application you’ll run the following command:
mix new my_app --sup
It generates a blank project with a root supervisor. For example:
defmodule SomeApp do
use Application
import Supervisor.Spec
def start(_type, _args) do
import Supervisor.Spec, warn: false
children = [
# worker(DynamicProcesses.Worker, [arg1, arg2, arg3]),
]
opts = [strategy: :one_for_one, name: SomeApp.Supervisor]
Supervisor.start_link(children, opts)
end
end
In the children array you can specify a list of workers (or supervisors) that will be supervised by the root supervisor.
All those settings are static. But you’re also able do that in runtime. In order to do this you’ll need several functions from the Elixir’s core: Supervisor.start_child/2, Supervisor.Spec.supervisor/3 and Supervisor.Spec.worker/3. Let’s go through some examples. All of these examples are taken from this project on github.
Start supervisor in runtime
The algorithm is very simple:
- Create the specification
- Consider what the process should be for the root of a new one
- Start a new child
To create the specification Supervisor.Spec.supervisor/3 function is used. Let’s take a look at the example:
defmodule DynamicProcesses do
use Application
def start(_type, _args) do
import Supervisor.Spec, warn: false
children = [
]
opts = [strategy: :one_for_one, name: DynamicProcesses.Supervisor]
Supervisor.start_link(children, opts)
end
end
defmodule DynamicProcesses.Examples do
alias DynamicProcesses.SomeSupervisor
import Supervisor.Spec
def add_single_supervisor(id \\ "1") do
{:ok, supervisor_spec} = build_supervisor_spec(SomeSupervisor, [], id)
Supervisor.start_child(DynamicProcesses.Supervisor, supervisor_spec)
end
defp build_supervisor_spec(module, args, id) do
supervisor_spec = supervisor(module, args, [id: "supervisor" <> id])
{:ok, supervisor_spec}
end
end
defmodule DynamicProcesses.SomeSupervisor do
use Supervisor
def start_link do
Supervisor.start_link(__MODULE__, [])
end
def init([]) do
children = []
supervise(children, strategy: :one_for_all)
end
end
After running DynamicProcesses.Example.add_single_supevisor you will get a new supervisor which is supervised by the application root one.
Run observer by running :observer.start in the iex session. After starting dynamic_processes application the processes tree looks like this:

And after adding a new supervisor it looks like this:

Double click on the <0.151.0> (it could be different in your case) and check that it is the right process (I mean that the right module is its base ):

Start workers in runtime
The algorithm for starting supervisors is the same however, for building specifications a different function is used. Lets consider an example:
defmodule DynamicProcesses.Examples do
alias DynamicProcesses.{SomeSupervisor, SomeWorker}
import Supervisor.Spec
def add_single_supervisor(id \\ "1") do
{:ok, supervisor_spec} = build_supervisor_spec(SomeSupervisor, [], id)
Supervisor.start_child(DynamicProcesses.Supervisor, supervisor_spec)
end
def add_supervisor_with_workers do
{:ok, supervisor_pid} = add_single_supervisor
{:ok, worker_spec1} = build_worker_spec(SomeWorker, [], "1")
{:ok, worker_spec2} = build_worker_spec(SomeWorker, [], "2")
Supervisor.start_child(supervisor_pid, worker_spec1)
Supervisor.start_child(supervisor_pid, worker_spec2)
end
defp build_supervisor_spec(module, args, id) do
supervisor_spec = supervisor(module, args, [id: "supervisor" <> id])
{:ok, supervisor_spec}
end
defp build_worker_spec(module, args, id) do
worker_spec = worker(module, args, [id: "worker" <> id])
{:ok, worker_spec}
end
end
defmodule DynamicProcesses.SomeWorker do
use GenServer
def start_link do
GenServer.start_link(__MODULE__, %{})
end
end
Take a look at DynamicProcesses.Examples.add_supervisor_with_workers/0 function. Here we do the following:
- Create a supervisor
- Create 2 workers specifications
- Start 2 workers and attach them to the supervisor created in step 1
- After that the processes tree looks like this:

Here <0.114.0> is our new supervisor, <0.115.0> and <0.116.0> are its child workers.
Why do we need to pass some id parameter?
As I understand it, by default you can only create one single process for a module. Perhaps it’s because Elixir uses the name of a module as a process identifier. So if you try to create several processes without specifying an id you will get a result like this:

After you have specified ids you will get a successful result:

And that’s all folks! Thanks for reading!