# How TeslaMate is Deployed: Complete Docker Architecture Guide

> Discover how TeslaMate is deployed using a Docker architecture. Explore the Elixir app, PostgreSQL, Grafana, and Mosquitto services orchestrated by Docker Compose for efficient data logging.

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

---

**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`](https://github.com/teslamate-org/teslamate/blob/main/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`](https://github.com/teslamate-org/teslamate/blob/main/config/prod.exs) and dependencies managed in [`mix.exs`](https://github.com/teslamate-org/teslamate/blob/main/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`](https://github.com/teslamate-org/teslamate/blob/main/docker-compose.yml) configuration demonstrating the architecture:

```yaml
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`](https://github.com/teslamate-org/teslamate/blob/main/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.