How Dependencies Are Configured in the TeslaMate Supervisor Tree
TeslaMate configures its runtime process hierarchy through a single OTP supervisor in lib/teslamate/application.ex that uses the :one_for_one restart strategy, dynamically assembling child specifications based on environment settings for MQTT and import directories.
TeslaMate, the open-source self-hosted data logger for Tesla vehicles, relies on a robust Erlang/OTP supervision tree to manage its concurrent processes. The foundation of this architecture lies in how dependencies are configured in the TeslaMate supervisor tree, specifically within the application startup sequence. By leveraging Elixir's Supervisor behaviour, TeslaMate ensures that critical components like database connections, API clients, and background workers restart independently when failures occur.
The Root Supervisor Architecture
The entry point for the supervision tree is TeslaMate.Application.start/2, defined in lib/teslamate/application.ex. This function initializes the entire runtime by starting a supervisor with the :one_for_one strategy:
# lib/teslamate/application.ex
def start(_type, _args) do
# …
Supervisor.start_link(children(), strategy: :one_for_one, name: TeslaMate.Supervisor)
end
The :one_for_one strategy ensures that if a single child process crashes, only that specific process is restarted, leaving the rest of the system unaffected. This design provides fault isolation between distinct functional areas like database access and vehicle communication.
Dynamic Child Specification in children/0
The children/0 function serves as the dependency manifest, returning a list of child specifications that varies based on runtime configuration. The function checks two environment keys:
Application.get_env(:teslamate, :import_directory)– Determines if the system should run in import modeApplication.get_env(:teslamate, :mqtt)– Controls whether the MQTT bridge starts
Each entry in the returned list follows Elixir's child specification format, either as a module name (for simple start_link functions) or as a tuple {module, opts} for processes requiring specific initialization arguments.
Normal Mode Configuration
When operating in standard mode (no import directory configured), children/0 returns the following list:
[
TeslaMate.Repo,
TeslaMate.Vault,
TeslaMate.HTTP,
TeslaMate.Api,
TeslaMate.Updater,
{Phoenix.PubSub, name: TeslaMate.PubSub},
TeslaMateWeb.Endpoint,
TeslaMate.Terrain,
TeslaMate.Vehicles,
if(mqtt_config != nil, do: {TeslaMate.Mqtt, mqtt_config}),
TeslaMate.Repair
]
|> Enum.reject(&is_nil/1)
The Enum.reject(&is_nil/1) filter at the end removes the optional MQTT child when the configuration is absent, ensuring the supervisor only starts configured dependencies.
The responsibilities of these core processes include:
TeslaMate.Repo– Manages the Ecto database connection poolTeslaMate.Vault– Handles encrypted storage of API tokens using CloakTeslaMate.HTTP– Provides a thin wrapper around the Tesla HTTP client for outbound requestsTeslaMate.Api– Implements the high-level Tesla API client for vehicle communicationTeslaMate.Updater– Manages firmware update tracking and data migrationsPhoenix.PubSub– Enables the Pub/Sub backbone for LiveView real-time updatesTeslaMateWeb.Endpoint– Starts the Phoenix HTTP serverTeslaMate.Terrain– Provides SRTM elevation data lookupsTeslaMate.Vehicles– Supervises individual vehicle process instancesTeslaMate.Repair– Runs background repair and maintenance tasks
Import Mode Configuration
When Application.get_env(:teslamate, :import_directory) returns a path, the supervision tree switches to import mode with a modified dependency list:
[
TeslaMate.Repo,
TeslaMate.Vault,
TeslaMate.HTTP,
TeslaMate.Api,
TeslaMate.Updater,
{Phoenix.PubSub, name: TeslaMate.PubSub},
TeslaMateWeb.Endpoint,
{TeslaMate.Terrain, disabled: true},
{TeslaMate.Repair, limit: 250},
{TeslaMate.Import, directory: import_directory}
]
Key differences from normal mode include:
TeslaMate.Terrainstarts withdisabled: trueto skip elevation lookups during bulk importsTeslaMate.Repairreceives alimit: 250option to throttle background workTeslaMate.Importis added to process CSV files from the specified directory
Optional MQTT Integration
The MQTT dependency demonstrates conditional supervision. Rather than using a static list, the configuration evaluates at runtime:
if(mqtt_config != nil, do: {TeslaMate.Mqtt, mqtt_config})
When the :mqtt key exists in the application environment, the tuple {TeslaMate.Mqtt, mqtt_config} enters the children list. Otherwise, the expression returns nil, and Enum.reject(&is_nil/1) filters it out before the supervisor starts. This pattern allows the same codebase to support both MQTT-enabled and MQTT-disabled deployments without code changes.
Extending the Supervision Tree
To add a new dependency, such as a telemetry collector, you must modify the children/0 function in lib/teslamate/application.ex. For example, adding a TeslaMate.Telemetry GenServer:
# Inside lib/teslamate/application.ex → children/0
[
# … existing children …
TeslaMate.Vehicles,
TeslaMate.Telemetry, # ← new child
if(mqtt_config != nil, do: {TeslaMate.Mqtt, mqtt_config}),
TeslaMate.Repair
]
|> Enum.reject(&is_nil/1)
Because the supervisor uses :one_for_one, the new telemetry process starts automatically during application boot and restarts independently if it crashes.
Summary
- TeslaMate uses a single supervisor in
lib/teslamate/application.exwith the:one_for_onerestart strategy to manage all runtime dependencies. - The
children/0function dynamically builds the child specification list based on environment variables:import_directoryand:mqtt. - Normal mode starts eleven core processes including database connections, API clients, and background workers.
- Import mode modifies the tree to disable terrain lookups, throttle repairs, and add the CSV import processor.
- Optional dependencies use runtime conditionals with
Enum.reject(&is_nil/1)to filter unconfigured processes. - Configuration originates in
config/config.exsand environment-specific files (dev, prod, test).
Frequently Asked Questions
What restart strategy does TeslaMate use for its supervisor tree?
TeslaMate uses the :one_for_one strategy, implemented in lib/teslamate/application.ex. This means if a single child process fails, the supervisor restarts only that specific process while leaving all other sibling processes running. This provides fault isolation between the database pool, API clients, and web interface.
How do I disable the MQTT dependency in TeslaMate?
Remove the :mqtt configuration from your config/config.exs or environment-specific config file. When Application.get_env(:teslamate, :mqtt) returns nil, the children/0 function in lib/teslamate/application.ex passes nil to the MQTT slot, and Enum.reject(&is_nil/1) filters it out before the supervisor starts. No code changes are required.
What happens when an import directory is configured in TeslaMate?
When the :import_directory environment variable is set, TeslaMate switches to import mode. The children/0 function returns a modified list that starts TeslaMate.Terrain with disabled: true, limits TeslaMate.Repair to 250 concurrent tasks, and adds TeslaMate.Import to process CSV files from the specified directory. This optimizes the supervision tree for bulk data ingestion rather than real-time vehicle monitoring.
Where does TeslaMate read its supervision configuration from?
TeslaMate reads configuration from the application environment, specifically the Application.get_env/2 calls in lib/teslamate/application.ex. The relevant keys (:mqtt and :import_directory) are defined in config/config.exs and can be overridden in environment-specific files like config/prod.exs or config/dev.exs. The supervisor tree builds dynamically at runtime based on these values.
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 →