How TeslaMate is Deployed: Complete Docker Architecture Guide

TeslaMate is deployed as a multi-container Docker stack comprising four services—the Elixir application, PostgreSQL, Grafana, and Mosquitto—built from a multi-stage Dockerfile and orchestrated via Docker Compose with persistent volumes and environment-based configuration.

TeslaMate is a self-hosted Tesla data logger maintained by the teslamate-org community. Understanding how TeslaMate is deployed requires examining its containerized architecture, which packages the Elixir application and its dependencies into a production-ready Docker environment. This approach ensures consistent deployments across any Docker-compatible host while maintaining data persistence and security isolation.

Container Architecture Overview

TeslaMate's deployment consists of four distinct containers that communicate over a private Docker network:

  • TeslaMate: The core Elixir application (teslamate/teslamate:latest) that polls the Tesla API, stores telemetry, and serves a web interface on port 4000.
  • PostgreSQL: Database backend (postgres:18-trixie) storing all vehicle data, events, and configuration in persistent volumes.
  • Grafana: Visualization layer (teslamate/grafana:latest) providing pre-configured dashboards accessible on port 3000.
  • Mosquitto: MQTT broker (eclipse-mosquitto:2) enabling real-time vehicle topic publication for integration with Home Assistant and Node-RED.

Multi-Stage Docker Build Process

The deployment image is constructed using the Dockerfile located in the repository root. This multi-stage build separates compilation from runtime to minimize attack surface and image size.

Builder Stage: Compiles the Elixir Release using mix release, bundles frontend assets, and prepares the application code.

App Stage: Copies only the compiled release to /opt/built, installs runtime dependencies, and creates a non-root user (nonroot) for security. According to the Dockerfile source, the release builds with mix release --path /opt/built during the build process.

Docker Compose Deployment Configuration

Production deployments rely on docker-compose.yml to orchestrate the four services. The configuration defines specific environment variables, network settings, and volume mounts.

Environment Variables

Service connectivity relies on standardized environment variables:

  • ENCRYPTION_KEY: Encrypts stored Tesla API tokens (must be set to a strong, unique value)
  • DATABASE_USER, DATABASE_PASS, DATABASE_NAME, DATABASE_HOST: PostgreSQL connection parameters (typically DATABASE_HOST=database)
  • MQTT_HOST: Points to the Mosquitto container (typically MQTT_HOST=mosquitto)
  • POSTGRES_PASSWORD: Must match DATABASE_PASS for the TeslaMate application to authenticate

Persistent Volumes

Data survives container restarts through named volumes:

  • teslamate-db: PostgreSQL data at /var/lib/postgresql
  • teslamate-grafana-data: Grafana SQLite database and dashboard configurations at /var/lib/grafana
  • mosquitto-conf and mosquitto-data: MQTT broker configuration and retained messages
  • ./import: Host-mounted directory for CSV import/export operations mapped to /opt/app/import

Network Security

All services communicate over Docker's default compose network. The TeslaMate container reaches PostgreSQL via the hostname database and Mosquitto via mosquitto. Only ports 4000 (TeslaMate UI) and 3000 (Grafana) are exposed externally, while PostgreSQL and MQTT remain isolated on the internal network.

Runtime Startup and Entrypoint

When containers start, the entrypoint.sh script initializes the runtime environment before launching the application. This script prepares configuration and executes bin/teslamate start to run the compiled Elixir Release. The release was built during the Docker image creation using mix release, with production-specific settings defined in config/prod.exs and dependencies managed in mix.exs.

Security Hardening

The deployment implements several security best practices:

  • Non-root execution: The container runs as a nonroot user created during the Dockerfile build process
  • Capability dropping: Uses cap_drop: - all to remove unnecessary Linux capabilities
  • No external database exposure: PostgreSQL and MQTT ports are not mapped to the host
  • Reverse proxy requirement: TLS termination and external HTTPS access should be handled by Traefik, Caddy, or Apache as recommended in the official documentation

Deployment Example

A minimal docker-compose.yml configuration demonstrating the architecture:

services:
  teslamate:
    image: teslamate/teslamate:latest
    restart: always
    environment:
      - ENCRYPTION_KEY=super-secret-key
      - DATABASE_USER=teslamate
      - DATABASE_PASS=change-me
      - DATABASE_NAME=teslamate
      - DATABASE_HOST=database
      - MQTT_HOST=mosquitto
    ports:
      - "4000:4000"
    volumes:
      - ./import:/opt/app/import
    cap_drop:
      - all

  database:
    image: postgres:18-trixie
    restart: always
    environment:
      - POSTGRES_USER=teslamate
      - POSTGRES_PASSWORD=change-me
      - POSTGRES_DB=teslamate
    volumes:
      - teslamate-db:/var/lib/postgresql

  grafana:
    image: teslamate/grafana:latest
    restart: always
    environment:
      - DATABASE_USER=teslamate
      - DATABASE_PASS=change-me
      - DATABASE_NAME=teslamate
      - DATABASE_HOST=database
    ports:
      - "3000:3000"
    volumes:
      - teslamate-grafana-data:/var/lib/grafana

  mosquitto:
    image: eclipse-mosquitto:2
    restart: always
    command: mosquitto -c /mosquitto-no-auth.conf
    volumes:
      - mosquitto-conf:/mosquitto/config
      - mosquitto-data:/mosquitto/data

volumes:
  teslamate-db:
  teslamate-grafana-data:
  mosquitto-conf:
  mosquitto-data:

Execute docker compose up -d to start the stack, then access TeslaMate at http://<host-ip>:4000 and Grafana at http://<host-ip>:3000 (default credentials: admin/admin).

Summary

  • TeslaMate is deployed via Docker Compose using four containerized services: the Elixir application (teslamate/teslamate:latest), PostgreSQL (postgres:18-trixie), Grafana (teslamate/grafana:latest), and Mosquitto (eclipse-mosquitto:2).
  • The multi-stage Dockerfile compiles an Elixir Release in a builder stage, then copies it to a minimal runtime image with a non-root user.
  • Environment variables configure database connections (DATABASE_HOST=database), MQTT integration (MQTT_HOST=mosquitto), and API token encryption via ENCRYPTION_KEY.
  • Persistent volumes ensure data survives container restarts, storing PostgreSQL data, Grafana dashboards in teslamate-grafana-data, and MQTT configuration.
  • The entrypoint.sh script initializes the runtime environment and launches the compiled release with bin/teslamate start.
  • Security features include cap_drop: all, non-root execution, and internal-only networking for database and MQTT services.

Frequently Asked Questions

What Docker images are required to deploy TeslaMate?

TeslaMate requires four official images: teslamate/teslamate:latest for the application, postgres:18-trixie for the database, teslamate/grafana:latest for visualization, and eclipse-mosquitto:2 for MQTT messaging. These images are pulled automatically when running docker compose up according to the architecture defined in the repository's Docker installation guide.

How does TeslaMate handle database migrations during deployment?

Database schema migrations are managed through Elixir's Ecto framework. When the TeslaMate container starts via entrypoint.sh, it runs the compiled release which automatically executes pending migrations located in priv/repo/migrations/ against the PostgreSQL database specified in DATABASE_HOST.

Can TeslaMate be deployed without Docker?

While Docker is the officially supported deployment method, TeslaMate can technically run as a standalone Elixir application compiled from source using mix release. However, this requires manual installation of Erlang/Elixir, PostgreSQL, and Node.js, plus configuration of the services defined in config/prod.exs—making Docker the recommended approach for production use.

What ports need to be exposed when deploying TeslaMate?

Only two ports require external exposure: port 4000 for the TeslaMate web interface and port 3000 for Grafana dashboards. PostgreSQL and MQTT ports should remain unexposed and accessible only via the internal Docker network, with TLS termination handled by a reverse proxy like Traefik or Caddy for secure external access.

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 →