# How TeslaMate's Elixir Application Supervision Tree Organizes Dependencies

> Discover how TeslaMate's Elixir application supervision tree organizes dependencies between database connections, API clients, and vehicle telemetry streaming for robust application management.

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

---

**TeslaMate implements a hierarchical OTP supervision tree where a root application supervisor bootstraps database connections, API clients, and a dedicated vehicle supervisor that isolates each car's telemetry streaming in its own GenServer process.**

TeslaMate is a self-hosted data logger for Tesla vehicles built with Elixir and the Phoenix framework. Understanding how its **Elixir application supervision tree** structures dependencies reveals the system's fault-tolerance design, ensuring that a crashing vehicle process or network hiccup cannot bring down the entire application.

## Root Application Supervisor (`TeslaMate.Application`)

The entry point resides in [`lib/teslamate/application.ex`](https://github.com/teslamate-org/teslamate/blob/main/lib/teslamate/application.ex), where the `start/2` callback initializes the system. According to the teslamate-org/teslamate source code, this function logs system information, detaches unwanted Phoenix telemetry handlers, validates the PostgreSQL version, and ultimately invokes `Supervisor.start_link/3` to launch the top-level supervision tree.

The supervisor's children are dynamically assembled by the `children/0` function. When running in standard mode (no import directory configured), the tree consists of:

- `TeslaMate.Repo` — Ecto repository managing database connections
- `TeslaMate.Vault` — Secure storage for API tokens and secrets
- `TeslaMate.HTTP` — HTTP client abstractions
- `TeslaMate.Api` — Wrapper around the official Tesla API
- `TeslaMate.Updater` — Background worker polling for vehicle data updates
- `Phoenix.PubSub` — Pub/Sub backbone powering LiveView real-time updates
- `TeslaMateWeb.Endpoint` — Phoenix HTTP endpoint serving the web UI
- `TeslaMate.Terrain` — Elevation data calculation service
- `TeslaMate.Vehicles` — **Supervisor** that dynamically manages a child process per vehicle
- `TeslaMate.Mqtt` — Conditional MQTT bridge (started only when configured)
- `TeslaMate.Repair` — Periodic maintenance and cleanup worker

All children are supervised with the standard `:one_for_one` strategy, meaning a failed child restarts independently without affecting siblings.

## Conditional Import Mode Adjustments

When the `IMPORT_DIR` environment variable is set, the supervision tree morphs to accommodate bulk CSV ingestion. As implemented in [`lib/teslamate/application.ex`](https://github.com/teslamate-org/teslamate/blob/main/lib/teslamate/application.ex) lines 22-52, the `children/0` function performs the following swaps:

- Replaces `TeslaMate.Terrain` with a disabled stub (elevation data is skipped during import)
- Inserts `TeslaMate.Import` as a worker to process CSV files sequentially
- Starts `TeslaMate.Repair` with rate-limiting to prevent database contention during bulk writes

This conditional branching demonstrates how Elixir supervision trees adapt to runtime configuration while maintaining clear dependency boundaries.

## Vehicle Process Isolation via `TeslaMate.Vehicles`

Vehicle management follows the **supervisor-of-supervisors** pattern. Declared in [`lib/teslamate/vehicles.ex`](https://github.com/teslamate-org/teslamate/blob/main/lib/teslamate/vehicles.ex), the `TeslaMate.Vehicles` module is itself a `Supervisor` that receives a list of vehicle specifications from either the live Tesla API or a fallback cache.

For each enabled vehicle, it spawns a child specification defined at lines 53-57:

```elixir
{TeslaMate.Vehicles.Vehicle, car: create_or_update!(vehicle)}

```

Here, `TeslaMate.Vehicles.Vehicle` is a GenServer responsible for streaming telemetry, persisting state changes to the database, and exposing a summary API. The supervisor strategy is explicitly set to `:one_for_one` at lines 59-63, ensuring that a crashing vehicle process (due to a flaky API response, for example) restarts individually without disrupting telemetry collection for other cars.

## MQTT Bridge Supervision (Optional)

The MQTT integration is encapsulated in its own supervisor, `TeslaMate.Mqtt`, defined in [`lib/teslamate/mqtt.ex`](https://github.com/teslamate-org/teslamate/blob/main/lib/teslamate/mqtt.ex). When enabled via configuration, this supervisor starts three distinct children:

1. `Tortoise311.Connection` — Low-level MQTT client handling TCP/TLS sockets
2. `TeslaMate.Mqtt.Publisher` — Outbound message dispatcher
3. `TeslaMate.Mqtt.PubSub` — Inbound message router

Connection parameters including TLS settings, IPv6 support, and credentials are constructed in `connection_config/1` (lines 29-58), allowing the entire MQTT subsystem to be toggled via application configuration without modifying the core supervision logic.

## Inter-Process Communication with Phoenix PubSub

Despite the hierarchical isolation, components communicate through `TeslaMate.PubSub`, the Phoenix PubSub instance registered in the root supervisor. LiveView components subscribe to topics like `"vehicle:#{car.id}"` to receive real-time updates without direct process coupling, while the MQTT publisher broadcasts to external brokers asynchronously.

## Code Examples

Start the application and its entire supervision tree:

```elixir
{:ok, _pid} = Application.ensure_all_started(:teslamate)

```

Retrieve vehicle summaries from the Vehicles supervisor:

```elixir
summaries = TeslaMate.Vehicles.list()

Enum.each(summaries, fn %TeslaMate.Vehicles.Vehicle.Summary{car: car} ->
  IO.puts("Vehicle #{car.id}: #{car.name}")
end)

```

Publish a message via the MQTT bridge (only if configured):

```elixir
payload = Jason.encode!(%{event: "charge_start", vehicle: vid})
:ok = TeslaMate.Mqtt.Publisher.publish("teslamate/vehicle/#{vid}", payload)

```

Subscribe a LiveView component to vehicle-specific events:

```elixir
def mount(_params, _session, socket) do
  if connected?(socket) do
    TeslaMate.PubSub.subscribe("vehicle:#{socket.assigns.car.id}")
  end
  
  {:ok, socket}
end

```

## Summary

- **Hierarchical Design**: `TeslaMate.Application` sits at the root, delegating vehicle-specific concerns to the `TeslaMate.Vehicles` supervisor and MQTT concerns to `TeslaMate.Mqtt`.
- **Fault Isolation**: Each vehicle runs in its own GenServer under a `:one_for_one` strategy, preventing cascade failures.
- **Conditional Loading**: The supervision tree dynamically adjusts its children based on the `IMPORT_DIR` environment variable, disabling terrain services and adding import workers when needed.
- **Decoupled Communication**: Phoenix PubSub (`TeslaMate.PubSub`) enables loose coupling between the web interface, vehicle processes, and MQTT publishers.

## Frequently Asked Questions

### What supervision strategy does TeslaMate use for vehicle processes?

TeslaMate uses the `:one_for_one` strategy in `TeslaMate.Vehicles`, as defined in [`lib/teslamate/vehicles.ex`](https://github.com/teslamate-org/teslamate/blob/main/lib/teslamate/vehicles.ex) lines 59-63. This means if a single vehicle's GenServer crashes due to an API timeout or parsing error, only that specific process restarts, leaving other vehicle processes and system components unaffected.

### How does TeslaMate handle MQTT connections in its supervision tree?

MQTT functionality is wrapped in a dedicated supervisor, `TeslaMate.Mqtt`, which starts three children: a `Tortoise311.Connection` for socket management, a `Publisher` for outgoing messages, and a `PubSub` process for routing incoming data. This supervisor is only started when MQTT is enabled in the application configuration, keeping the core tree lean when the feature is unused.

### What happens to the supervision tree when import mode is enabled?

When an import directory is configured, `TeslaMate.Application` modifies its `children/0` function to replace the `TeslaMate.Terrain` service with a disabled stub, inserts a `TeslaMate.Import` worker to handle CSV processing, and rate-limits the `TeslaMate.Repair` worker. This adjustment prevents elevation API calls during bulk imports and reduces database contention.

### How do LiveView components receive real-time vehicle updates without direct process coupling?

Components subscribe to the `TeslaMate.PubSub` instance via `TeslaMate.PubSub.subscribe("vehicle:#{car_id}")`. When a vehicle GenServer updates its state, it broadcasts to this topic, allowing the Phoenix PubSub infrastructure to push updates to connected LiveView sockets without those processes needing to know the vehicle process's PID or location in the supervision tree.