# How the TeslaMate Supervisor Tree Works: OTP Supervision in Elixir

> Discover how the TeslaMate supervisor tree works using OTP supervision in Elixir. Learn how its hierarchical architecture ensures system stability and automatic restarts for fault isolation.

- Repository: [TeslaMate/teslamate](https://github.com/teslamate-org/teslamate)
- Tags: internals
- Published: 2026-06-18

---

**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`](https://github.com/teslamate-org/teslamate/blob/main/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`](https://github.com/teslamate-org/teslamate/blob/main/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`](https://github.com/teslamate-org/teslamate/blob/main/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`](https://github.com/teslamate-org/teslamate/blob/main/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`](https://github.com/teslamate-org/teslamate/blob/main/config.exs), `TeslaMate.Application.children/0` injects `TeslaMate.Mqtt` into the supervision tree. Located in [`lib/teslamate/mqtt.ex`](https://github.com/teslamate-org/teslamate/blob/main/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:**

```elixir
{:ok, pid} = TeslaMate.Application.start(:normal, [])
{:ok, children} = Supervisor.which_children(pid)
IO.inspect(children, label: "Root children")

```

**Enumerate all vehicle processes:**

```elixir
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:**

```elixir
car_id = 1
TeslaMate.Vehicles.Vehicle.subscribe_to_summary(car_id)

receive do
  {:summary, summary} -> IO.inspect(summary)
end

```

**Enable MQTT via configuration:**

```elixir
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`](https://github.com/teslamate-org/teslamate/blob/main/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`](https://github.com/teslamate-org/teslamate/blob/main/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`](https://github.com/teslamate-org/teslamate/blob/main/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.