How TeslaMate's Elixir Application Supervision Tree Organizes Dependencies

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, 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 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, 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:

{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. 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:

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

Retrieve vehicle summaries from the Vehicles supervisor:

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):

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:

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 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.

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 →