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 connectionsTeslaMate.Vault— Secure storage for API tokens and secretsTeslaMate.HTTP— HTTP client abstractionsTeslaMate.Api— Wrapper around the official Tesla APITeslaMate.Updater— Background worker polling for vehicle data updatesPhoenix.PubSub— Pub/Sub backbone powering LiveView real-time updatesTeslaMateWeb.Endpoint— Phoenix HTTP endpoint serving the web UITeslaMate.Terrain— Elevation data calculation serviceTeslaMate.Vehicles— Supervisor that dynamically manages a child process per vehicleTeslaMate.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.Terrainwith a disabled stub (elevation data is skipped during import) - Inserts
TeslaMate.Importas a worker to process CSV files sequentially - Starts
TeslaMate.Repairwith 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:
Tortoise311.Connection— Low-level MQTT client handling TCP/TLS socketsTeslaMate.Mqtt.Publisher— Outbound message dispatcherTeslaMate.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.Applicationsits at the root, delegating vehicle-specific concerns to theTeslaMate.Vehiclessupervisor and MQTT concerns toTeslaMate.Mqtt. - Fault Isolation: Each vehicle runs in its own GenServer under a
:one_for_onestrategy, preventing cascade failures. - Conditional Loading: The supervision tree dynamically adjusts its children based on the
IMPORT_DIRenvironment 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →