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 – An Ecto.Repo process maintaining the PostgreSQL connection pool
  • TeslaMate.Vault – Handles secure storage for encrypted API credentials
  • TeslaMate.HTTP – HTTP client for Tesla API requests
  • TeslaMate.Api – High-level wrapper for Tesla API interactions
  • TeslaMate.Updater – GenServer performing periodic self-update checks
  • Phoenix.PubSub – Event bus for real-time communication between components
  • TeslaMateWeb.Endpoint – Phoenix endpoint serving the web interface
  • TeslaMate.Terrain – GenServer providing elevation data services
  • TeslaMate.Vehicles – A supervisor that dynamically manages one child process per registered vehicle
  • TeslaMate.Mqtt (optional) – A supervisor for MQTT publishing when configured
  • TeslaMate.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 by driving_interval/0 or charging_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:

  1. Tortoise311.Connection – The low-level MQTT client connection
  2. TeslaMate.Mqtt.Publisher – Wrapper module for publishing vehicle data to MQTT topics
  3. TeslaMate.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 anomalies
  • TeslaMate.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 GenStateMachine only triggers a restart of that vehicle process via TeslaMate.Vehicles
  • Database connection failures in TeslaMate.Repo remain 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.ex initializes with :one_for_one strategy, spawning database connections, API clients, and the Phoenix endpoint alongside dynamic supervisors.
  • TeslaMate.Vehicles acts as a dynamic supervisor creating individual GenStateMachine processes per vehicle via TeslaMate.Vehicles.Vehicle.child_spec/1, with each state machine handling the full lifecycle from :start through :driving and :charging states.
  • MQTT supervision is optional and conditional on configuration, managing Tortoise311.Connection and 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.Repair and TeslaMate.Updater run 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →