Title
Create new category
Edit page index title
Edit category
Edit link
Log Streaming (REST API)
This article walks you through configuring log streaming via REST API.
All configuration is stored in etcd and served by api-service on port 443 with JWT authentication - the same API endpoint you use for storage management. On each server, lightbox-exporter reads the configuration from etcd and renders a pipeline config that Alloy polls via the remotecfg protocol.
xxxxxxxxxxUser ──(HTTPS + JWT)──► api-service:443 ──► etcd │ lightbox-exporter ◄────┘ │ (remotecfg) Alloy ──► Loki / RsyslogPrerequisites
- A running Lightbits cluster.
- The
system:cluster-adminJWT for the cluster.
Step 1: Setting your JWT and Server
Export the server hostname and your system:cluster-admin JWT, so that all
subsequent commands can reference them:
xxxxxxxxxxexport SERVER=server00 # replace with your server hostname or IPexport JWT=eyJhbGciOiJSUzI1NiIsImtpZCI6InN5c3RlbTpyb290IiwidHlwIjoiSldUIn0...# ^ obtain from /etc/cluster-manager/system_jwt on any cluster node, or from# the Ansible output after cluster installation.All curl commands below use $SERVER and $JWT.
Step 2: (Optional) Creating a TLS Bundle
A TLS bundle holds the certificate material used for mTLS connections to an Rsyslog target. Skip this step if you only need a Loki target.
xxxxxxxxxxcurl -sk -XPOST https://$SERVER/api/v2/logs/tls-bundles \ -H "Authorization: Bearer $JWT" \ -H 'Content-Type: application/json' \ -d "$(jq -n \ --arg name dc-rsyslog \ --rawfile ca ./scripts/certs/ca.crt \ --rawfile cert ./scripts/certs/rsyslog-server.crt \ --rawfile key ./scripts/certs/rsyslog-server.key \ '{name:$name, caPem:$ca, certPem:$cert, keyPem:$key}')" | jqList the bundles to verify:
xxxxxxxxxxcurl -sk -XGET https://$SERVER/api/v2/logs/tls-bundles \ -H "Authorization: Bearer $JWT" | jqStep 3: Creating Sources
A source tells Alloy which log stream to collect on each server. You can collect from journald (by syslog identifier) or from a file path.
Control-plane Services (journald)
xxxxxxxxxxfor svc in node-manager cluster-manager lightbox-exporter api-service \ upgrade-manager discovery-service etcd alloy; do curl -sk -XPOST https://$SERVER/api/v2/logs/sources \ -H "Authorization: Bearer $JWT" \ -H 'Content-Type: application/json' \ -d "{\"name\":\"$svc\",\"enabled\":true,\"logFormat\":\"ZAP_JSON\", \"minLevel\":\"INFO\",\"journald\":{\"syslogIdentifier\":\"$svc\"}}" | jq .source.namedoneData-plane Services (journald)
xxxxxxxxxxcurl -sk -XPOST https://$SERVER/api/v2/logs/sources \ -H "Authorization: Bearer $JWT" \ -H 'Content-Type: application/json' \ -d '{"name":"duroslight-0","enabled":true,"minLevel":"INFO", "journald":{"syslogIdentifier":"duroslight-0"}}' | jq curl -sk -XPOST https://$SERVER/api/v2/logs/sources \ -H "Authorization: Bearer $JWT" \ -H 'Content-Type: application/json' \ -d '{"name":"gftl","enabled":true,"minLevel":"INFO", "journald":{"syslogIdentifier":"gftl"}}' | jqEvent Log Files (Optional)
Event logs are written to files only when emit_service_events_to_file is
enabled in the Ansible config.
xxxxxxxxxxfor svc in node-manager cluster-manager api-service upgrade-manager; do curl -sk -XPOST https://$SERVER/api/v2/logs/sources \ -H "Authorization: Bearer $JWT" \ -H 'Content-Type: application/json' \ -d "{\"name\":\"$svc-events\",\"enabled\":true,\"logFormat\":\"ZAP_JSON\", \"minLevel\":\"INFO\", \"file\":{\"paths\":[\"/var/log/$svc-events.log\"], \"labels\":{\"job\":\"$svc-events\"}}}" | jq .source.namedoneList all sources to verify:
xxxxxxxxxxcurl -sk -XGET https://$SERVER/api/v2/logs/sources \ -H "Authorization: Bearer $JWT" | jqExpected output (abbreviated):
xxxxxxxxxx{ "sources": [ { "name": "node-manager", "enabled": true, "logFormat": "ZAP_JSON", "minLevel": "INFO", "journald": { "syslogIdentifier": "node-manager" } }, { "name": "duroslight-0", "enabled": true, "minLevel": "INFO", "journald": { "syslogIdentifier": "duroslight-0" } } ]}Step 4: Creating Targets
A target is a log destination. You can have multiple targets; every collected log line is forwarded to all enabled targets.
Loki Target (No TLS)
xxxxxxxxxxcurl -sk -XPOST https://$SERVER/api/v2/logs/targets \ -H "Authorization: Bearer $JWT" \ -H 'Content-Type: application/json' \ -d '{"name":"loki1","type":"LOKI","enabled":true, "loki":{"url":"http://loki.example.com:3100/loki/api/v1/push"}}' | jqRsyslog Target (mTLS, Requires TLS Bundle from Step 2)
xxxxxxxxxxcurl -sk -XPOST https://$SERVER/api/v2/logs/targets \ -H "Authorization: Bearer $JWT" \ -H 'Content-Type: application/json' \ -d '{"name":"rsyslog1","type":"RSYSLOG","enabled":true, "rsyslog":{"endpoint":"rsyslog.example.com","port":6514, "timeout":"5s","tlsBundle":"dc-rsyslog"}}' | jqList the targets to verify:
xxxxxxxxxxcurl -sk -XGET https://$SERVER/api/v2/logs/targets \ -H "Authorization: Bearer $JWT" | jqExpected output:
xxxxxxxxxx{ "targets": [ { "name": "loki1", "type": "LOKI", "enabled": true, "loki": { "url": "http://loki.example.com:3100/loki/api/v1/push" } }, { "name": "rsyslog1", "type": "RSYSLOG", "enabled": true, "rsyslog": { "endpoint": "rsyslog.example.com", "port": 6514, "timeout": "5s", "tlsBundle": "dc-rsyslog" } } ]}Step 5: Verifying Collector Status
Once the configuration is applied, Alloy polls lightbox-exporter for the rendered pipeline config (via the remotecfg protocol) and begins shipping logs. Use the collectors API to check that each server's Alloy agent is healthy:
xxxxxxxxxxcurl -sk -XGET https://$SERVER/api/v2/logs/collectors \ -H "Authorization: Bearer $JWT" | jqExpected output when everything is healthy:
xxxxxxxxxx{ "collectors": [ { "id": "c00-s00", "lastSeen": "2026-02-24T15:46:29Z", "alloyStatus": "ALLOY_ONLINE", "remotecfgStatus": "REMOTECFG_UP", "pipelineHealth": "OK", "failingExporters": [], "totalExporters": 2 } ]}If pipelineHealth is DEGRADED or ERROR, check failingExporters for
which targets are unreachable.
Modifying the Configuration
Sources and targets can be updated or deleted without restarting any service. Alloy picks up the new pipeline config on its next remotecfg poll (default: 1 minute).
xxxxxxxxxx# Update a source's minimum log levelcurl -sk -XPUT https://$SERVER/api/v2/logs/sources/node-manager \ -H "Authorization: Bearer $JWT" \ -H 'Content-Type: application/json' \ -d '{"name":"node-manager","enabled":true,"logFormat":"ZAP_JSON", "minLevel":"WARN","journald":{"syslogIdentifier":"node-manager"}}' | jq # Disable a target temporarilycurl -sk -XPUT https://$SERVER/api/v2/logs/targets/loki1 \ -H "Authorization: Bearer $JWT" \ -H 'Content-Type: application/json' \ -d '{"name":"loki1","type":"LOKI","enabled":false, "loki":{"url":"http://loki.example.com:3100/loki/api/v1/push"}}' | jq # Delete a sourcecurl -sk -XDELETE https://$SERVER/api/v2/logs/sources/gftl \ -H "Authorization: Bearer $JWT" | jqInspecting the Rendered Alloy Pipeline Config
You can inspect the pipeline config that lightbox-exporter renders for Alloy by querying the remotecfg endpoint on the server directly (no auth required — node-local only):
curl -s --header "Content-Type: application/json" -H "Connect-Protocol-Version: 1" http://localhost:8090/api/v1/remote/config | jq -r .content | base64 -d© 2026 Lightbits Labs™