Deployment
The node runs as a container in either of two modes. Inside Litmus Edge it finds the instance on its own, needing no hostname and no NATS credentials — the normal production deployment. Outside Litmus Edge it reads the same devices and publishes the same Sparkplug messages, but you supply the Litmus Edge address and credentials for its NATS broker.
Quick Start covers the Marketplace path and the verification steps; this page covers everything else.
Inside Litmus Edge
Use these steps when you manage the container yourself rather than through the Marketplace.

- Download the image archive from Litmus Central, or pull it from the registry if you have registry access.
- In Litmus Edge, navigate to Applications > Images, then upload and import the archive.
- Navigate to Applications > Containers, click Run, and enter the command. See Overview of docker run.
docker run -d --name le-sparkplug --restart unless-stopped \
-v le-sparkplug-data:/le-sparkplug/data \
-e EDGE_API_TOKEN="<Litmus Edge API token>" \
-e GROUP_ID="Plant1" \
-e NODE_ID="Line1" \
-e MQTT_URL="tcp://192.168.1.20:1883" \
le-sparkplug:v2.0.1With registry access, use the registry path in place of the local image name: us-docker.pkg.dev/litmus-customer-facing/litmus-solutions/le-sparkplug:v2.0.1. There is no latest tag, so the version is always explicit.
The node reaches Litmus Edge through the Docker gateway, which it detects from the container's default route. Set EDGE_DOCKER_GATEWAY_IP to name that gateway explicitly when detection does not find the right one.
Add -p 9090:9090 to reach the metrics and health endpoints from outside the container.
Outside Litmus Edge
External mode adds two things to the deployment above: the Litmus Edge address, and credentials for its NATS broker. Beyond the Quick Start prerequisites, it needs:
- Litmus Edge reachable from the container host by hostname or IP address
- An API Account, whose API key gives the node access to the Litmus Edge NATS broker. See Create an API Account
- The NATS proxy enabled on the Litmus Edge instance. See Tokens
docker run -d --name le-sparkplug --restart unless-stopped \
-v le-sparkplug-data:/le-sparkplug/data \
-e EDGE_EXTERNAL="true" \
-e EDGE_HOSTNAME="edge-01.plant.example.com" \
-e EDGE_API_TOKEN="<Litmus Edge API token>" \
-e EDGE_ACCESS_ACCOUNT_API_KEY="<API Account API key>" \
-e GROUP_ID="Plant1" \
-e NODE_ID="Line1" \
-e MQTT_URL="tcp://192.168.1.20:1883" \
us-docker.pkg.dev/litmus-customer-facing/litmus-solutions/le-sparkplug:v2.0.1Setting | Effect |
|---|---|
EDGE_EXTERNAL=true | Switches to external mode, where the Litmus Edge address must be given explicitly |
EDGE_HOSTNAME | Hostname or IP address of the Litmus Edge instance. Required in external mode |
EDGE_ACCESS_ACCOUNT_API_KEY | API Account key. The node builds the NATS connection tls://admin:<key>@<EDGE_HOSTNAME>:4222 from it |
The generated connection always uses port 4222, so a port in EDGE_HOSTNAME applies to the API only. Keys containing characters such as / or a space need no escaping — the node encodes them.
Connect with an Explicit NATS URL
Set NATS_URL when the generated connection does not fit, such as a non-default port or a user other than admin. It takes precedence over EDGE_ACCESS_ACCOUNT_API_KEY, which is then unnecessary.
-e NATS_URL="tls://admin:<API Account API key>@edge-01.plant.example.com:4222"The scheme decides encryption: tls:// connects over TLS, nats:// connects without it. EDGE_HOSTNAME is still required, because the node reaches the Litmus Edge API through it — only the NATS connection comes from NATS_URL.
Persist the Data Directory
The node keeps its Sparkplug state in /le-sparkplug/data. Every deployment path mounts the Docker volume le-sparkplug-data there, which is also what the Marketplace application does.
Path in the container | Contents |
|---|---|
seqNum/<group>-<node>_bdSeq.txt | The birth/death sequence number for the next MQTT session |
<group>-<node>_db/ | The store-and-forward buffer, when that feature is enabled |
config/config.json | The configuration file, when you use one |
A configuration file goes at config/config.json inside the volume. See Configuration for the file format.
Upgrade to a New Version
State lives in the volume, not the container, so an upgrade replaces the container and keeps the volume.
- Note the Sparkplug Group ID, Node ID, and volume name of the running container, then stop and remove it.
- Deploy the new version with the same Group ID, Node ID, and volume name.
- Confirm the handover on the broker: NDEATH from the old session, then NBIRTH from the new one, carrying the next birth/death sequence number.
Secure the MQTT Connection with TLS
The node connects over TLS when the broker URL uses the ssl:// or mqtts:// scheme and you supply the certificate authority that signed the broker's certificate. Add a client certificate and key when the broker requires mutual TLS.
Certificates are supplied as Base64-encoded text, not file paths, because the node reads them from settings rather than from disk.
Encode the Certificates
Encode each PEM file, including its -----BEGIN----- and -----END----- lines, as a single line of Base64.
base64 < ca.pem | tr -d '\n'
base64 < client.crt | tr -d '\n'
base64 < client.key | tr -d '\n'[Convert]::ToBase64String([IO.File]::ReadAllBytes("ca.pem"))
[Convert]::ToBase64String([IO.File]::ReadAllBytes("client.crt"))
[Convert]::ToBase64String([IO.File]::ReadAllBytes("client.key"))The output must be one unbroken string. A Base64 value with line breaks in it fails to decode.
Configure the Connection
Purpose | Marketplace parameter | Environment variable |
|---|---|---|
Broker URL, TLS scheme | MQTT Broker URL | MQTT_URL |
CA certificate | MQTT CA Certificate | MQTT_CA_CERT |
Client certificate | MQTT Client Certificate | MQTT_CLIENT_CERT |
Client private key | MQTT Client Key | MQTT_CLIENT_KEY |
Skip verification | not exposed | MQTT_SKIP_CERT_VERIFY |
The equivalent config.json fields sit on each broker object — see Configuration.
The URL scheme decides whether the connection is encrypted at all: ssl:// and mqtts:// use TLS, tcp:// and mqtt:// do not. No other schemes are accepted, so a tls:// broker URL is rejected at startup.
MQTT_SKIP_CERT_VERIFY turns off verification of the broker's certificate. The connection stays encrypted, but the node accepts any certificate the broker presents. It is deliberately absent from the Marketplace form, so setting it takes a configuration file or a self-managed container.
Multiple Brokers with Different Authorities
A configuration file carries one certificate set per broker, which is the reason to prefer a file when brokers have different certificate authorities.
{
"edge_api_token": "<Litmus Edge API token>",
"group_id": "Plant1",
"node_id": "Line1",
"mqtt": {
"servers": [
{
"url": "ssl://broker-a.example.com:8883",
"user": "sparkplug",
"password": "<password>",
"ca_cert": "<Base64 CA certificate>",
"client_cert": "<Base64 client certificate>",
"client_key": "<Base64 client key>"
},
{
"url": "ssl://broker-b.example.com:8883",
"ca_cert": "<Base64 CA certificate>"
}
]
}
}The node publishes to one broker at a time and moves to the next in the list when the session ends.
Certificate Errors
A successful TLS connection logs connected from the mqtt component with the ssl:// URL in the server field, followed by birth set published. Certificate problems are logged and do not stop the node from starting:
Message from mqtt | Meaning |
|---|---|
decode CA_CERT base64 | The CA value is not valid Base64. The node continues with the system trust store, and the client certificate is skipped |
decode CLIENT_CERT base64 | The client certificate value is not valid Base64 |
decode CLIENT_KEY base64 | The client key value is not valid Base64 |
load client certificate and key | The pair decoded but do not form a usable key pair, so the certificate and key do not match |
The first three are most often caused by line breaks surviving the encoding.
Verify the Deployment
Follow Quick Start steps 2 and 3 — the log messages and broker topics are the same in both modes. Outside Litmus Edge, read the logs with docker logs le-sparkplug and add -p 9090:9090 to reach the health endpoint:
curl -i http://127.0.0.1:9090/healthz200 ok means the MQTT session is up. 503 unavailable means it is not, or that host application gating is enabled and the host has not reported online yet.
In external mode, if the nats component never logs connected, the API Account key or the NATS proxy is the place to look. If nats connects but the birth set reports no devices, the node reached the NATS broker while the API returned nothing — check the API token and the hostname.
The generated NATS connection is encrypted, but the node does not verify the Litmus Edge certificate, because Litmus Edge presents a self-signed certificate. The same applies to its calls to the Litmus Edge API.Sparkplug requires the birth/death sequence number to keep advancing from one session to the next. Lose this volume and the node restarts that counter, so host applications read the new session as out of order and mark the node stale. Keep the volume, and its name, across upgrades.Never run two nodes with the same Sparkplug Group ID and Node ID, and never point two containers at the same volume. Either one puts two Sparkplug identities on the same topics and corrupts the sequence-number state.The client certificate and key are read only when a CA certificate is also set. Supplying a client pair on its own leaves the connection without client authentication, and nothing in the configuration is rejected.Skipping verification removes protection against an attacker impersonating your broker. Use it only to isolate a certificate problem in testing, and never in production. The fix in production is to supply the correct CA certificate.