Skip to content

Docker Compose Quickstart

The minimum useful Pipelet stack runs four containers: a database, the OCPP broker, the OCPP server, and the Headless CPMS gateway. This guide gets you from zero to a fully functioning self-hosted CPMS in about ten minutes.

┌─────────────────────┐
┌───►│ cpms-headless │ REST :8080 → you
│ │ (gateway) │
┌────────┐ OCPP/WS ┌────────────┐ ───┘ └──────────┬──────────┘
│station │◄─────────────►│ ocpp-broker│ │
└────────┘ :9000 │ + ocpp- │ ◄──── shared ────┘
│ server │ MySQL
└──────┬──────┘
┌──────────────┐
│ MySQL 8 │
└──────────────┘
  • Docker 24+ and Docker Compose v2 (docker compose ..., not docker-compose)
  • 2 GB free RAM, 1 GB free disk
  • Ports 3306, 8080, and 9000 available
  1. Create a project directory.

    Terminal window
    mkdir pipelet-stack && cd pipelet-stack
  2. Write docker-compose.yml.

    services:
    mysql:
    image: mysql:8.0
    environment:
    MYSQL_ROOT_PASSWORD: changeme
    MYSQL_DATABASE: pipelet
    MYSQL_USER: pipelet
    MYSQL_PASSWORD: changeme
    volumes:
    - mysql-data:/var/lib/mysql
    ports:
    - "3306:3306"
    healthcheck:
    test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
    interval: 5s
    timeout: 5s
    retries: 10
    ocpp-broker:
    image: ghcr.io/pipelet/ocpp-broker:latest
    depends_on:
    mysql:
    condition: service_healthy
    environment:
    DB_HOST: mysql
    DB_USER: pipelet
    DB_PASSWORD: changeme
    DB_NAME: pipelet
    ports:
    - "9000:9000" # OCPP WebSocket port
    ocpp-server:
    image: ghcr.io/pipelet/ocpp-server:latest
    depends_on:
    - ocpp-broker
    environment:
    DB_HOST: mysql
    DB_USER: pipelet
    DB_PASSWORD: changeme
    DB_NAME: pipelet
    OCPP_BROKER_URL: http://ocpp-broker:9000
    cpms-headless:
    image: ghcr.io/pipelet/cpms-headless:latest
    depends_on:
    - ocpp-server
    environment:
    CPMS_API_KEY: hcpms_live_changeme
    OCPP_SERVER_URL: http://ocpp-server:8000
    DB_HOST: mysql
    DB_USER: pipelet
    DB_PASSWORD: changeme
    DB_NAME: pipelet
    ports:
    - "8080:8080"
    volumes:
    mysql-data:
  3. Start the stack.

    Terminal window
    docker compose up -d
    docker compose logs -f cpms-headless

    Wait for the line Headless CPMS Gateway started on http://0.0.0.0:8080.

  4. Verify.

    Terminal window
    curl -H "X-API-Key: hcpms_live_changeme" \
    http://localhost:8080/api/v1/system/health
    { "status": "ok", "components": { "db": "ok", "broker": "ok", "ocpp_server": "ok" } }
  5. Connect a station. Point real or simulated charge points at:

    ws://your-host:9000/{station-id}

    Or, for the simulator:

    Terminal window
    docker run -d --name chargersim \
    -e OCPP_BACKEND_URL=ws://host.docker.internal:9000 \
    ghcr.io/pipelet/ocpp-chargersim:latest
Terminal window
# Container status
docker compose ps
# Live logs from one service
docker compose logs -f ocpp-broker
# Hop into the MySQL shell
docker compose exec mysql mysql -upipelet -pchangeme pipelet
# Stop everything (preserves data)
docker compose down
# Stop and wipe data
docker compose down -v

The demo key in the compose file (hcpms_live_changeme) is, well, demo. Generate a real one and use a .env file:

.env
CPMS_API_KEY=hcpms_live_$(openssl rand -hex 24)
MYSQL_ROOT_PASSWORD=$(openssl rand -hex 16)
PIPELET_DB_PASSWORD=$(openssl rand -hex 16)

Reference them in docker-compose.yml:

environment:
CPMS_API_KEY: ${CPMS_API_KEY}
DB_PASSWORD: ${PIPELET_DB_PASSWORD}

Don’t commit .env to git. Add it to .gitignore.

This compose file is suitable for staging and small production deployments. For a hardened setup you’ll want:

  • TLS termination via a reverse proxy (Caddy, Traefik, nginx). Don’t expose :8080 and :9000 directly.
  • PostgreSQL instead of MySQL for high-write workloads (Pipelet supports both).
  • An external secrets manager. Don’t store API keys or DB passwords in the compose file.
  • Separate hosts for the OCPP broker (handles long-lived connections) and the gateway (handles bursty REST traffic).
  • Healthcheck-based restarts for every service, not just MySQL.
  • Log shipping to your observability stack — every Pipelet service writes structured JSON logs to stdout.

The 16 Pipelet modules fall into three layers:

  1. Core (this stack): ocpp-broker, ocpp-server, cpms-headless, mysql/postgres.
  2. Optional services: cpms-ui (web dashboard), mcp-server (MCP for AI agents), automation-service + automation-studio (workflow engine), service-energy, service-cardata, service-monitoring.
  3. Portals: admin-portal, fleet-portal, cpms-driver-portal.

Add them one container at a time. See Modules for the full list.