How the TeslaMate Supervisor Tree Works: OTP Supervision in Elixir
The TeslaMate supervisor tree implements a hierarchical OTP application architecture where TeslaMate.Application creates a root supervisor with :one_for_one strategy, spawning static children like the repository and API clients alongside dynamic supervisors for vehicles and optional MQTT connections, ensuring that failures in any single component—such as a specific vehicle's state machine—are isolated and automatically restarted without affecting the rest of the system.
TeslaMate is an open-source Tesla data logger built with Elixir that leverages Erlang/OTP supervision principles to maintain high availability. According to the source code in teslamate-org/teslamate, the application boots through a carefully structured supervisor tree defined in lib/teslamate/application.ex, where each child process—from database connections to per-vehicle state machines—runs under dedicated supervision to guarantee fault tolerance.
Root Supervisor Structure
When the BEAM virtual machine starts the TeslaMate OTP application, it invokes TeslaMate.Application.start/2 in lib/teslamate/application.ex. This function links the root supervisor via Supervisor.start_link/3, passing the children defined in children/0 with a :one_for_one supervision strategy.
The root supervisor, named TeslaMate.Supervisor, manages the following static children:
TeslaMate.Repo– AnEcto.Repoprocess maintaining the PostgreSQL connection poolTeslaMate.Vault– Handles secure storage for encrypted API credentialsTeslaMate.HTTP– HTTP client for Tesla API requestsTeslaMate.Api– High-level wrapper for Tesla API interactionsTeslaMate.Updater– GenServer performing periodic self-update checksPhoenix.PubSub– Event bus for real-time communication between componentsTeslaMateWeb.Endpoint– Phoenix endpoint serving the web interfaceTeslaMate.Terrain– GenServer providing elevation data servicesTeslaMate.Vehicles– A supervisor that dynamically manages one child process per registered vehicleTeslaMate.Mqtt(optional) – A supervisor for MQTT publishing when configuredTeslaMate.Repair– GenServer for background repair of historic data
The :one_for_one strategy ensures that if any single child crashes, only that specific process restarts without affecting siblings.
Dynamic Vehicle Supervision
The Vehicles Supervisor
TeslaMate.Vehicles operates as a dedicated supervisor in lib/teslamate/vehicles.ex. Its init/1 callback dynamically constructs child specifications by calling list_vehicles!/0 or using the :vehicles option from the application environment, filtering out disabled vehicles. For each vehicle, it invokes TeslaMate.Vehicles.Vehicle.child_spec/1 to create the process specification.
This design allows TeslaMate to scale vehicle monitoring horizontally—each Tesla vehicle receives its own supervised process under the Vehicles supervisor tree.
Per-Vehicle State Machines
Each vehicle runs as an independent GenStateMachine defined in lib/teslamate/vehicles/vehicle.ex. The process maintains a %Data{} struct containing the car record, latest API response, streaming state, and fuse values. The state machine drives the vehicle lifecycle through specific states:
{:start, :fetch}– Initial data retrieval upon process startup{:online, …}– Active polling at intervals defined bydriving_interval/0orcharging_interval/0{:asleep, …}and{:offline, …}– Low-frequency polling states when the vehicle is inactive{:suspended, …}– Temporary logging suspension (e.g., during specific charging scenarios){:charging, …},{:updating, …},{:driving, …}– Specialized sub-states handling charge sessions, software updates, and drive recording
State transitions trigger via internal events (:fetch, {:update, …}) or streaming API messages ({:stream, %Stream.Data{}}). The module broadcasts summary updates via Phoenix.PubSub through broadcast_summary/0 and broadcast_fetch/0, enabling the LiveView UI to react to state changes in real-time.
Optional MQTT Supervision
When the :mqtt configuration key is present in config.exs, TeslaMate.Application.children/0 injects TeslaMate.Mqtt into the supervision tree. Located in lib/teslamate/mqtt.ex, this supervisor manages:
Tortoise311.Connection– The low-level MQTT client connectionTeslaMate.Mqtt.Publisher– Wrapper module for publishing vehicle data to MQTT topicsTeslaMate.Mqtt.PubSub– Local PubSub namespace for internal MQTT event coordination
The init/1 function generates a unique client identifier via generate_client_id/0 and constructs connection parameters from the application environment, making MQTT integration completely optional without affecting core functionality.
Background Workers and Utilities
Two GenServer processes handle periodic maintenance tasks without supervising other processes:
TeslaMate.Repair– Started as a root supervisor child, triggers hourly repair runs to correct historic data anomaliesTeslaMate.Updater– Checks for new TeslaMate releases and manages update notifications
Both operate as leaf nodes in the supervisor tree, restarting independently if they encounter errors during background job execution.
Fault Isolation Strategies
The TeslaMate supervisor tree employs :one_for_one strategies at every supervisory level. This hierarchical isolation provides specific failure domains:
- A crash in a specific vehicle's
GenStateMachineonly triggers a restart of that vehicle process viaTeslaMate.Vehicles - Database connection failures in
TeslaMate.Reporemain isolated from vehicle polling logic - Optional MQTT connection drops restart only the MQTT subtree without interrupting data logging to PostgreSQL
Because the root supervisor links directly to the Erlang VM, a catastrophic failure of the root TeslaMate.Supervisor brings down the entire node, adhering to standard OTP application behavior where an unrecoverable root failure requires a full application restart.
Inspecting the Supervisor Tree
You can observe the TeslaMate supervisor hierarchy at runtime using the following Elixir shell commands:
List root supervisor children:
{:ok, pid} = TeslaMate.Application.start(:normal, [])
{:ok, children} = Supervisor.which_children(pid)
IO.inspect(children, label: "Root children")
Enumerate all vehicle processes:
TeslaMate.Vehicles.list()
|> Enum.map(fn %TeslaMate.Vehicles.Vehicle.Summary{car: %TeslaMate.Log.Car{id: id}} ->
{id, :global.whereis_name(:"#{id}")}
end)
|> IO.inspect(label: "Vehicle processes")
Subscribe to vehicle updates via PubSub:
car_id = 1
TeslaMate.Vehicles.Vehicle.subscribe_to_summary(car_id)
receive do
{:summary, summary} -> IO.inspect(summary)
end
Enable MQTT via configuration:
config :teslamate,
mqtt: [
host: "mqtt.example.com",
port: 1883,
username: "user",
password: "pass",
tls: false,
namespace: :teslamate_mqtt
]
Summary
- The root supervisor in
lib/teslamate/application.exinitializes with:one_for_onestrategy, spawning database connections, API clients, and the Phoenix endpoint alongside dynamic supervisors. TeslaMate.Vehiclesacts as a dynamic supervisor creating individual GenStateMachine processes per vehicle viaTeslaMate.Vehicles.Vehicle.child_spec/1, with each state machine handling the full lifecycle from:startthrough:drivingand:chargingstates.- MQTT supervision is optional and conditional on configuration, managing
Tortoise311.Connectionand publishers as a separate subtree. - Fault isolation ensures vehicle crashes, database reconnections, or MQTT failures remain confined to their respective supervision branches without cascading through the entire application.
- Background workers like
TeslaMate.RepairandTeslaMate.Updaterrun as supervised GenServers performing hourly maintenance and update checks.
Frequently Asked Questions
What happens when a vehicle process crashes in TeslaMate?
When a specific vehicle's GenStateMachine crashes in lib/teslamate/vehicles/vehicle.ex, the TeslaMate.Vehicles supervisor detects the exit signal and automatically restarts only that vehicle process using the original child specification created by TeslaMate.Vehicles.Vehicle.child_spec/1. Other vehicles continue polling the Tesla API unaffected, and the crashed vehicle process resumes from its initial :start state, refetching current data via the API.
How does the TeslaMate supervisor tree handle MQTT connection failures?
The optional MQTT supervisor defined in lib/teslamate/mqtt.ex isolates MQTT failures from the core application. If the Tortoise311.Connection process loses network connectivity or encounters authentication errors, only the MQTT subtree restarts according to :one_for_one strategy. Vehicle data continues logging to PostgreSQL via TeslaMate.Repo, and the Web UI remains accessible through TeslaMateWeb.Endpoint because these processes operate under separate supervision branches.
What is the difference between the root supervisor and the Vehicles supervisor?
TeslaMate.Supervisor (the root) manages static, long-lived processes like the database repository, HTTP client, and PubSub system that exist for the application's lifetime. In contrast, TeslaMate.Vehicles is a dynamic supervisor that creates and destroys child processes based on the number of vehicles registered in the database, allowing runtime scaling as vehicles are added or removed without restarting the entire application.
How can I inspect the running supervisor tree in a live TeslaMate instance?
Access the Elixir runtime via remote shell or observer, then use :sys.get_state/1 on the supervisors or Supervisor.which_children/1 to enumerate process IDs. For vehicle-specific debugging, call TeslaMate.Vehicles.list() to retrieve summaries and map them to global process names using :global.whereis_name(:"#{car_id}") to locate specific GenStateMachine PIDs within the supervision hierarchy.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →