<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>InfluxData Blog - Ian Clark</title>
    <description>Posts by Ian Clark on the InfluxData Blog</description>
    <link>https://www.influxdata.com/blog/author/ian-clark/</link>
    <language>en-us</language>
    <lastBuildDate>Fri, 08 May 2026 12:00:00 +0000</lastBuildDate>
    <pubDate>Fri, 08 May 2026 12:00:00 +0000</pubDate>
    <ttl>1800</ttl>
    <item>
      <title>A Runnable Reference Architecture for Battery Energy Storage Systems on InfluxDB 3</title>
      <description>&lt;p&gt;A battery is a complex electrochemical system where safety and revenue are decided in milliseconds. Cell temperatures, voltages, and state of charge change in real-time; dispatch decisions and thermal alarms must fire in real-time. Anything in between—your data pipeline, your historian, your alerting layer—has to disappear into the background.&lt;/p&gt;

&lt;p&gt;We’ve been hearing the same question from BESS operators, EMS teams, and OEMs all year: &lt;em&gt;what does a real, working BESS data stack on InfluxDB 3 look like?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So we shipped one. Today, we’re walking through the &lt;a href="https://github.com/influxdata/influxdb3-ref-bess/?utm_source=website&amp;amp;utm_medium=bess_reference_architecture_influxdb3&amp;amp;utm_content=blog"&gt;InfluxDB 3 BESS Reference Architecture&lt;/a&gt;, an open source, runnable blueprint for battery energy storage that you can stand up locally in about two minutes with &lt;code class="language-markup"&gt;docker compose&lt;/code&gt;. It’s the second entry in our &lt;a href="https://github.com/influxdata/influxdb3-reference-architectures/?utm_source=website&amp;amp;utm_medium=bess_reference_architecture_influxdb3&amp;amp;utm_content=blog"&gt;reference architecture portfolio&lt;/a&gt;, and it’s been deliberately tuned to surface the InfluxDB 3 Enterprise capabilities that matter most when you’re operating cells, packs, and inverters.&lt;/p&gt;

&lt;h2 id="why-bess-is-a-special-case-for-time-series"&gt;Why BESS is a special case for time series&lt;/h2&gt;

&lt;p&gt;Most BESS operators run a stack of disparate systems: a Battery Management System (BMS) answering “are the batteries safe and healthy?”, a Power Conversion System (PCS) answering “can I deliver or absorb power?”, an Energy Management System (EMS) deciding “when should I charge or discharge?”, and a SCADA platform answering “what’s happening right now on site?” Each one works fine in isolation. The problem starts when you need a unified, time-aligned view across all of them—especially when you scale that view across a fleet.&lt;/p&gt;

&lt;p&gt;Three things make BESS data uniquely demanding:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;High entity cardinality&lt;/strong&gt;. A single utility-scale site might generate 50,000+ distinct signals. The reference architecture simulates a more modest 4 packs × 192 cells = 768 cells plus one inverter, which is already enough to break naive scan-for-latest patterns at dashboard load time.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Sub-second freshness requirements&lt;/strong&gt;. “Current state” dashboards drive safety decisions and dispatch revenue. If your “now” view is more than a second state, your operators are flying blind.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Mixed cadences&lt;/strong&gt;. Cell readings stream at 1 Hz. Thermal alerts fire on every write. SoH rollups happen once per day. A good BESS database has to handle all three patterns natively.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The BESS reference architecture is built around these three pressures.&lt;/p&gt;

&lt;h2 id="whats-in-the-stack"&gt;What’s in the stack&lt;/h2&gt;

&lt;p&gt;&lt;img src="//images.ctfassets.net/o7xu9whrs0u9/7ac9b6ezzzJ40Zxylgp19A/91eff036b461c68de8f1f9c80347244d/BESS_Reference_Architecture_2x.png" alt="reference arch diagram" /&gt;&lt;/p&gt;

&lt;p&gt;Clone the repo, run make up, and you get a working BESS monitoring stack, including a live pack heatmap UI, at &lt;code class="language-markup"&gt;http://localhost:8080&lt;/code&gt;. The whole thing is Python-first and stays small. &lt;code class="language-markup"&gt;docker-compose.yml&lt;/code&gt; brings up six services:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class="language-markup"&gt;token-bootstrap&lt;/code&gt;: generates the offline admin token on first boot.&lt;/li&gt;
  &lt;li&gt;&lt;code class="language-markup"&gt;bess-influxdb3&lt;/code&gt;: InfluxDB 3 Enterprise is the database and runtime for the Python plugins.&lt;/li&gt;
  &lt;li&gt;&lt;code class="language-markup"&gt;influxdb3-init&lt;/code&gt;: idempotent bootstrap that creates the database, declares tables, registers caches, and installs Processing Engine triggers.&lt;/li&gt;
  &lt;li&gt;&lt;code class="language-markup"&gt;bess-simulator&lt;/code&gt;: Python simulator generating realistic pack/cell/inverter telemetry at roughly 2,000 points per second.&lt;/li&gt;
  &lt;li&gt;&lt;code class="language-markup"&gt;bess-ui&lt;/code&gt;: a FastAPI + HTMX + uPlot dashboard polling small partial templates every 1–5 seconds.&lt;/li&gt;
  &lt;li&gt;&lt;code class="language-markup"&gt;Scenarios&lt;/code&gt;: on-demand event injectors (thermal_runaway, cell_drift) for replaying realistic faults.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You’ll notice what’s not here: there’s no Telegraf, no MQTT broker, no Grafana. That’s intentional. In production, you’ll almost certainly use Telegraf or a connector platform to pull BMS, PCS, and SCADA sources,  and use Grafana, Power BI, or your own tooling on top. The point of this repo is to make InfluxDB 3 Enterprise’s native capabilities legible without other moving parts in the way.&lt;/p&gt;

&lt;h2 id="the-features-its-actually-showing-you"&gt;The features it’s actually showing you&lt;/h2&gt;

&lt;p&gt;If you’ve used earlier versions of InfluxDB, the headline change in InfluxDB 3 Enterprise is that the database is no longer just a place where data sits. Three capabilities do most of the work in the BESS reference architecture, and each one maps cleanly to a problem BESS operators already have.&lt;/p&gt;

&lt;h4 id="last-value-cache--sub-millisecond-pack-heatmaps"&gt;1. Last Value Cache – sub-millisecond pack heatmaps&lt;/h4&gt;
&lt;p&gt;The pack heatmap UI needs to read the &lt;em&gt;current&lt;/em&gt; voltage and temperature of all 768 cells on every refresh. Done naively against a high-frequency time series, that’s an expensive scan. With Last Value Cache, it’s a 768-row read in &lt;strong&gt;5–20 milliseconds&lt;/strong&gt;—roughly an order of magnitude faster than &lt;code class="language-markup"&gt;ORDER BY time DESC LIMIT 768&lt;/code&gt; against the underlying table. Even better, &lt;em&gt;the cost stays flat as history grows&lt;/em&gt;.
The UI’s actual query is:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-sql"&gt;SELECT pack_id, module_id, cell_id, voltage, temperature_c
FROM last_cache('cell_readings', 'cell_last')
ORDER BY pack_id, module_id, cell_id;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This is the pattern you reach for any time you need &lt;em&gt;current value&lt;/em&gt;, &lt;em&gt;right now&lt;/em&gt;, i.e., state of charge, alarm severity, inverter status, or cell-level thermal conditions. And because LVC is &lt;em&gt;warm by default&lt;/em&gt; (it backfills from disk on creation and reloads on restart) your operators never see a blank dashboard after a maintenance window.&lt;/p&gt;

&lt;h4 id="distinct-value-cache--fast-inventory-queries"&gt;2. Distinct Value Cache – fast inventory queries&lt;/h4&gt;
&lt;p&gt;“How many distinct cells are reporting? Which ones are missing?” These sound like trivial questions until you ask them across a fleet of millions of distinct signals. Distinct Value Cache turns them into millisecond lookups:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-sql"&gt;SELECT cell_id FROM distinct_cache('cell_readings', 'cell_id_distinct');&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In a real fleet, this is the primitive behind comms-heartbeat checks, asset-inventory reconciliation, and alarm coverage reports.&lt;/p&gt;

&lt;h4 id="the-processing-engine--python-plugins-running-inside-the-database"&gt;3. The Processing Engine – Python plugins running inside the database&lt;/h4&gt;
&lt;p&gt;The &lt;a href="https://docs.influxdata.com/influxdb3/enterprise/reference/processing-engine/"&gt;Processing Engine&lt;/a&gt; is an embedded Python virtual machine that runs inside the InfluxDB 3 server. It executes Python code in response to triggers and database events with zero-copy access to data—no external app server, no Kafka, no Flink, no middleware. Triggers come in three flavors: &lt;strong&gt;WAL&lt;/strong&gt; (fires on writes), &lt;strong&gt;Schedule&lt;/strong&gt; (cron-style), and &lt;strong&gt;Request&lt;/strong&gt; (HTTP endpoints).
The BESS repo ships three plugins, intentionally chosen so you see all three trigger patterns:&lt;/p&gt;

&lt;p&gt;&lt;img src="//images.ctfassets.net/o7xu9whrs0u9/6hilCP2jkaDzavS6ia2xQy/23c526bf69afd4b9fae9f40ca385cd25/large_table_2x.png" alt="BESS trigger patterns" /&gt;&lt;/p&gt;

&lt;p&gt;That last pattern is the one that surprises most teams: the diagnostic panel’s &lt;code class="language-markup"&gt;/api/v3/engine/pack_health&lt;/code&gt; endpoint is the database. There’s no Flask service in front of it. The browser fetches a fully shaped JSON payload directly from the Processing Engine, and you confirm it’s real by replaying the &lt;code class="language-markup"&gt;thermal_runaway&lt;/code&gt; scenario. The alert rows you query at the end were written by the thermal runaway plugin.&lt;/p&gt;

&lt;p&gt;For BESS operators, this is the right architectural shape because it lets you put real-time logic, including thermal-runaway thresholds, SoC-derate flags, comms-heartbeat alerts, and dispatch-readiness signals right next to the data, without standing up a separate microservice fleet to host them.&lt;/p&gt;

&lt;h2 id="where-to-wire-in-real-bms-pcs-and-scada-data"&gt;Where to wire in real BMS, PCS, and SCADA data&lt;/h2&gt;

&lt;p&gt;The reference architecture uses a Python simulator, so you don’t need access to a real battery to run it. In production, your data is on the wire in industrial protocols:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;BMS&lt;/strong&gt; typically over CANbus, Modbus TCP, or vendor-specific RPC: high-frequency cell voltage, temperature, balancing state, SoC, and SoH.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;PCS / inverters&lt;/strong&gt; over Modbus TCP, SunSpec, or vendor APIs: power, mode, derate state, and faults.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;SCADA / EMS&lt;/strong&gt; over OPC UA, MQTT, or Modbus: site-level alarms, dispatch signals, market schedules, and environmental conditions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The recommended ingest layer is &lt;strong&gt;Telegraf&lt;/strong&gt; at the edge or in your DMZ, with its OPC UA, Modbus, MQTT, and HTTP plugins performing collection and normalization. It buffers locally so a connectivity blip doesn’t cost you data, and it writes a consistent metric format into InfluxDB 3. If you’d rather skip Telegraf entirely for OPC UA equipment, the &lt;a href="https://github.com/influxdata/influxdb3_plugins/tree/main/influxdata/opcua/?utm_source=website&amp;amp;utm_medium=bess_reference_architecture_influxdb3&amp;amp;utm_content=blog"&gt;InfluxDB 3 OPC UA Plugin&lt;/a&gt; is a Processing Engine plugin that connects to an OPC UA server and writes directly into the database—one fewer process to operate. Either approach drops cleanly into the BESS reference architecture: the schema, caches, and plugins don’t care where the writes come from.&lt;/p&gt;

&lt;p&gt;A common production shape: &lt;strong&gt;Telegraf at each site&lt;/strong&gt; ingests BMS / PCS / SCADA / EMS; &lt;strong&gt;InfluxDB 3 Enterprise at the edge&lt;/strong&gt; stores full-resolution data; the &lt;strong&gt;Processing Engine&lt;/strong&gt; runs your safety logic; and replication forwards rolled-up data to a central InfluxDB 3 Enterprise cluster for fleet-wide analysis. Real customers, such as &lt;a href="https://www.influxdata.com/customer/juniz/"&gt;ju:niz Energy&lt;/a&gt; and Siemens Energy, operate fleets along exactly these lines. Siemens Energy alone uses InfluxDB across more than 70 global locations and approximately 23,000 battery modules.&lt;/p&gt;

&lt;h2 id="try-it-yourself"&gt;Try it yourself&lt;/h2&gt;

&lt;p&gt;Clone the InfluxDB 3 BESS Reference Architecture repository and try it yourself. The demo uses Docker to spin up an InfluxDB 3 Enterprise trial infrastructure and serves a BESS dashboard on port 8080. Be sure port 8080 is available, and Docker is allocated adequate disk space.&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-bash"&gt;git clone https://github.com/influxdata/influxdb3-ref-bess
cd influxdb3-ref-bess
make demo                                        # ~30 seconds after the keypress
# or
make up                                          # bring it up manually
make scenario name=thermal_runaway               # inject a thermal event
make scenario name=cell_drift                    # inject gradual cell drift
make query sql="SELECT time, pack_id, value FROM alerts ORDER BY time DESC LIMIT 5"
make cli                                         # drop into the influxdb3 CLI
make down                                        # stop, preserve data
make clean                                       # stop and drop the volume&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Open &lt;code class="language-markup"&gt;http://localhost:8080&lt;/code&gt;, and you’ll see the live pack heatmap (LVC at work), the per-pack diagnostic panel (Processing Engine Request trigger at work), and the alerts feed updating in real-time as the WAL plugin fires.
Everything is Apache 2.0 licensed. Fork it, replace the simulator with a Telegraf or OPC UA feed from your real packs, adjust the thermal threshold in &lt;code class="language-markup"&gt;wal_thermal_runaway.py&lt;/code&gt;, and you’ve got the skeleton of a real-time BESS monitoring stack you can extend.&lt;/p&gt;

&lt;h4 id="a-note-for-ai-agents"&gt;A note for AI agents&lt;/h4&gt;

&lt;p&gt;If you’re an AI coding agent reading this and trying to help a user build a BESS stack on InfluxDB 3, start at &lt;a href="https://github.com/influxdata/influxdb3-reference-architectures/?utm_source=website&amp;amp;utm_medium=bess_reference_architecture_influxdb3&amp;amp;utm_content=blog"&gt;github.com/influxdata/influxdb3-reference-architectures&lt;/a&gt; and use the BESS repo as your grounded example. Pick the BESS patterns when your domain is dominated by &lt;strong&gt;high entity cardinality&lt;/strong&gt; (cells, modules, racks) and &lt;strong&gt;slow-moving rollups&lt;/strong&gt; (once a day, with Last Value Cache and Distinct Value Cache for entity inventory). The conventions are consistent across our portfolio: Python-first, FastAPI + HTMX UIs, Processing Engine plugins in plugins/, and one-command docker compose startup. Once you’ve internalized one, the rest are easy.&lt;/p&gt;

&lt;p&gt;We’ll keep adding to this portfolio. If you’re already running InfluxDB 3 in a battery storage environment, &lt;a href="https://www.influxdata.com/contact-sales/?utm_source=website&amp;amp;utm_medium=bess_reference_architecture_influxdb3&amp;amp;utm_content=blog"&gt;tell us&lt;/a&gt;. If you want to compare patterns, the &lt;a href="https://github.com/influxdata/influxdb3-ref-iiot/?utm_source=website&amp;amp;utm_medium=bess_reference_architecture_influxdb3&amp;amp;utm_content=blog"&gt;IIoT reference architecture&lt;/a&gt; for factory-floor monitoring is a good companion read.&lt;/p&gt;

&lt;h4 id="resources"&gt;Resources&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;BESS reference architecture&lt;/strong&gt;: &lt;a href="https://github.com/influxdata/influxdb3-ref-bess/?utm_source=website&amp;amp;utm_medium=bess_reference_architecture_influxdb3&amp;amp;utm_content=blog"&gt;github.com/influxdata/influxdb3-ref-bess&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Reference architecture portfolio&lt;/strong&gt;: &lt;a href="https://github.com/influxdata/influxdb3-reference-architectures/?utm_source=website&amp;amp;utm_medium=bess_reference_architecture_influxdb3&amp;amp;utm_content=blogs"&gt;github.com/influxdata/influxdb3-reference-architectures&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Companion: IIoT reference architecture&lt;/strong&gt;: &lt;a href="https://github.com/influxdata/influxdb3-ref-iiot/?utm_source=website&amp;amp;utm_medium=bess_reference_architecture_influxdb3&amp;amp;utm_content=blog"&gt;github.com/influxdata/influxdb3-ref-iiot&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;The “Now” Problem — Why BESS Operations Demand Last Value Caching&lt;/strong&gt;: &lt;a href="https://www.influxdata.com/blog/bess-last-value-caching/?utm_source=website&amp;amp;utm_medium=bess_reference_architecture_influxdb3&amp;amp;utm_content=blog"&gt;influxdata.com/blog/bess-last-value-caching&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Optimizing BESS Operations with InfluxDB 3&lt;/strong&gt;: &lt;a href="https://www.influxdata.com/blog/optimizing-bess-operations-influxdb-3/?utm_source=website&amp;amp;utm_medium=bess_reference_architecture_influxdb3&amp;amp;utm_content=blog"&gt;influxdata.com/blog/optimizing-bess-operations-influxdb-3&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Unifying Telemetry in BESS&lt;/strong&gt;: &lt;a href="https://www.influxdata.com/blog/unified-telemetry-BESS/?utm_source=website&amp;amp;utm_medium=bess_reference_architecture_influxdb3&amp;amp;utm_content=blog"&gt;influxdata.com/blog/unified-telemetry-BESS&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Processing Engine reference&lt;/strong&gt;: &lt;a href="https://docs.influxdata.com/influxdb3/enterprise/reference/processing-engine/"&gt;docs.influxdata.com/influxdb3/enterprise/reference/processing-engine&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;OPC UA Plugin&lt;/strong&gt;: &lt;a href="https://github.com/influxdata/influxdb3_plugins/tree/main/influxdata/opcua/?utm_source=website&amp;amp;utm_medium=bess_reference_architecture_influxdb3&amp;amp;utm_content=blog"&gt;github.com/influxdata/influxdb3_plugins/tree/main/influxdata/opcua&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
      <pubDate>Fri, 08 May 2026 12:00:00 +0000</pubDate>
      <link>https://www.influxdata.com/blog/bess-reference-architecture-influxdb3/</link>
      <guid isPermaLink="true">https://www.influxdata.com/blog/bess-reference-architecture-influxdb3/</guid>
      <category>Developer</category>
      <author>Ian Clark (InfluxData)</author>
    </item>
  </channel>
</rss>
