Using ReductStore in ROS applications
ReductStore is designed for robotics applications. It makes ROS messages, sensor payloads, and recorded robotics data queryable by storing them as timestamped records with labels and attachments.
This guide covers everything you need to know to use ReductStore in a ROS application: collecting data on a robot with ReductBridge, storing it in a local ReductStore instance, selectively replicating it to a cloud or central on-prem instance, and querying, visualizing, and exporting the data from there.
The following sections walk through each step, starting with an overview of the architecture and then diving into setup, replication, querying, and export.
Some features described in this guide are available only in ReductStore Pro, including the ReductROS and ReductSelect extensions. Check the pricing page for more information about available features and extensions.
Overview
ReductStore stores data as binary records and indexes each record by timestamp. It allows you to store a history of the robotics data in original format, while labels provide metadata for filtering and attachments store shared metadata such as ROS message schemas.
The ROS workflow uses the following components:
- ReductBridge subscribes to ROS topics on the robot, writes messages to ReductStore, assigns labels, and stores the schema metadata required to decode raw ROS messages.
- ReductStore stores the message payloads as timestamped records in a bucket. On the robot side, it runs locally on the same robot or edge computer.
- Replication tasks run on the robot-side ReductStore instance and copy only selected entries or records with matching labels to a cloud or central on-prem ReductStore instance.
- Cloud or central on-prem ReductStore stores replicated data from one or more robots and provides the main query and visualization endpoint for operators, dashboards, and analytics tools.
- ReductROS decodes stored ROS data when it is queried. It can extract raw ROS 1 or ROS 2 messages as JSON, read MCAP or rosbag files, and export filtered data back to robotics formats.
The diagram below shows the robot-side pipeline and the central storage layer.
ROS nodes publish topics, ReductBridge subscribes to selected topics, and local
ReductStore stores one entry per topic with the raw payload, labels, and
$schema schema attachment. A replication task on the robot copies only the
selected entries or label-matched records to the cloud or central on-prem
ReductStore instance.
Data Collection with ReductBridge
Let's setup data ingestion on the robot and see how ReductBridge simplifies collecting, labeling, and storing ROS data in ReductStore.
Installing ReductBridge
ReductBridge is an open-source project that can be installed on the robot or edge computer. It is available as a prebuilt binary for Linux, macOS, and Windows, or as a Docker container. You can find the installation instructions in the ReductBridge documentation. This example uses the Docker container with Docker Compose:
reduct-bridge:
image: reduct/bridge:latest-ros2-jazzy
restart: unless-stopped
depends_on:
- reductstore
- ros2publisher
network_mode: host
environment:
ROS_DOMAIN_ID: "0"
ROS_HOME: /tmp/ros
FASTDDS_BUILTIN_TRANSPORTS: UDPv4
volumes:
- ./bridge.toml:/etc/reduct-bridge/config.toml:ro
command: ["reduct-bridge", "/etc/reduct-bridge/config.toml"]
See the full Docker Compose example.
The publisher and ReductBridge containers use the same ROS 2 environment:
ROS_DOMAIN_ID: "0"places both processes in DDS domain 0 so they can discover and communicate with each other. Use a different domain ID to isolate this ROS graph, but keep the value identical in every participating container.ROS_HOME: /tmp/rosgives ROS 2 a writable directory for logs and other runtime files. Using/tmpis suitable here because these files do not need to persist after the container stops.FASTDDS_BUILTIN_TRANSPORTS: UDPv4makes Fast DDS use its UDP/IPv4 transport instead of shared memory. This avoids shared-memory transport errors between isolated containers and allows discovery over the host network.
Configuring ReductBridge
ReductBridge uses one TOML file to connect three layers: an input subscribes to
ROS 2 topics, a pipeline enriches and routes the records, and a remote writes
them to ReductStore. Save the following file as bridge.toml next to
docker-compose.yml. The Compose service mounts it at
/etc/reduct-bridge/config.toml.
# ROS 2 input named "robot".
[inputs.ros2.robot]
# Use the same domain as the publishers.
domain_id = 0
node_name = "reduct_bridge_robot"
queue_size = 128
# Resolve ROS message schemas from this installation.
schema_paths = ["/opt/ros/jazzy"]
# Subscribe to camera images.
[[inputs.ros2.robot.topics]]
name = "/camera/image_raw"
entry_name = "/camera/image_raw"
# Use the message acquisition time.
# timestamp-start
timestamp = { field = "header.stamp", format = "ros_stamp" }
# timestamp-end
# Add labels that are constant for this topic.
labels = [
# camera-static-start
{ static = { source = "ros2", topic = "/camera/image_raw", sensor = "camera" } }
# camera-static-end
]
# Subscribe to GPS fixes.
[[inputs.ros2.robot.topics]]
name = "/gps/fix"
entry_name = "/gps/fix"
timestamp = { field = "header.stamp", format = "ros_stamp" }
# Extract coordinates and add constant labels.
labels = [
# gps-dynamic-start
{ field = "latitude", label = "x" },
{ field = "longitude", label = "y" },
{ field = "altitude", label = "z" },
# gps-dynamic-end
{ static = { source = "ros2", topic = "/gps/fix", sensor = "gps" } }
]
# Subscribe to IMU measurements.
[[inputs.ros2.robot.topics]]
name = "/imu/data"
entry_name = "/imu/data"
timestamp = { field = "header.stamp", format = "ros_stamp" }
labels = [
{ static = { source = "ros2", topic = "/imu/data", sensor = "imu" } }
]
# Route the ROS input to the local remote.
# pipeline-route-start
[pipelines.ros_to_reductstore]
remote = "local"
inputs = ["robot"]
# pipeline-route-end
# Copy the latest GPS coordinates and identify the robot.
labels = [
# gps-propagation-start
{ from = "/gps/fix", labels = ["x", "y", "z"], to = "/camera/image_raw" },
{ from = "/gps/fix", labels = ["x", "y", "z"], to = "/imu/data" },
# gps-propagation-end
# robot-label-start
{ static = { robot = "robot-1" }, to = "*" }
# robot-label-end
]
# Write records to the local ReductStore instance.
[remotes.reduct.local]
url = "http://127.0.0.1:8383"
token_api = "my-token"
bucket = "robot-data"
prefix = ""
# Create a rolling bucket when it is missing.
[remotes.reduct.local.create_bucket]
quota_type = "FIFO"
quota_size = "20GB"
The source is also available as the full bridge.toml example.
At a high level, this configuration subscribes to camera, GPS, and IMU topics;
stores their original CDR payloads and ROS schemas; assigns sensor timestamps
and labels; propagates the latest GPS coordinates to camera and IMU records; and
writes everything to a rolling robot-data bucket.
For a field-by-field explanation of subscriptions, schema attachments, timestamp assignment, static and dynamic labels, cross-topic label propagation, and ReductStore output, use the detailed guide below.
📄️ ReductBridge and ROS Data
Configure ReductBridge to subscribe to ROS 2 topics, preserve schemas, assign timestamps and labels, and write data to ReductStore.
Data Buffering and Replication
ReductBridge handles data collection, but it needs a ReductStore instance for persistent storage. In a ROS application, the robot-side ReductStore has two roles: it buffers robotics data on local disk and continuously replicates selected records to a cloud or central on-prem instance.
Installing ReductStore
The Docker Compose example runs ReductStore on the robot, publishes its HTTP API
on port 8383, and mounts a named volume at /data. The volume keeps records,
bucket settings, and replication progress across container restarts.
Before starting the stack, set the URL and API token for the destination ReductStore instance:
export CENTRAL_REDUCTSTORE_URL=https://reductstore.example.com
export CENTRAL_REDUCTSTORE_TOKEN=replace-with-central-token
docker compose up -d
The destination must already contain a fleet-data bucket, and its token must
have permission to write to that bucket. Use HTTPS for communication outside a
trusted network. The local API token is my-token, matching the ReductBridge
configuration from the previous section.
Configuring buffering and replication
ReductStore supports startup provisioning through environment variables. The
reductstore service provisions the local bucket, replication task, and
compression lifecycle policy when the container starts:
reductstore:
image: reduct/store:latest
restart: unless-stopped
ports:
- "8383:8383"
environment:
RS_API_TOKEN: my-token
# Keep the newest 20 GB of robot data on local disk.
RS_BUCKET_1_NAME: robot-data
RS_BUCKET_1_QUOTA_TYPE: FIFO
RS_BUCKET_1_QUOTA_SIZE: 20GB
# Send selected topics to the central ReductStore instance.
RS_REPLICATION_1_NAME: robot-to-central
RS_REPLICATION_1_SRC_BUCKET: robot-data
RS_REPLICATION_1_DST_BUCKET: fleet-data
RS_REPLICATION_1_DST_HOST: ${CENTRAL_REDUCTSTORE_URL}
RS_REPLICATION_1_DST_TOKEN: ${CENTRAL_REDUCTSTORE_TOKEN}
RS_REPLICATION_1_ENTRIES: "/camera/image_raw,/imu/data"
# Compress local data after one day.
RS_LIFECYCLE_1_NAME: compress-robot-data
RS_LIFECYCLE_1_TYPE: compress
RS_LIFECYCLE_1_BUCKET: robot-data
RS_LIFECYCLE_1_OLDER_THAN: 1d
RS_LIFECYCLE_1_INTERVAL: 1h
volumes:
- reduct-data:/data
The robot-data bucket uses a 20 GB FIFO quota, so the robot can continue
recording without unbounded disk growth. When the bucket reaches its quota,
ReductStore removes its oldest blocks to make room for new data. ReductBridge
also requests the same bucket settings when it connects, making the bucket
available even when the bridge and store start concurrently.
The robot-to-central replication task watches new records in robot-data and
sends /camera/image_raw and /imu/data to the fleet-data bucket. ReductStore
batches transfers and tracks pending records in a transaction log, so a network
interruption does not stop local ingestion and replication resumes when the
destination becomes available. Replication starts with records written after
the task is created; it does not backfill older records or replicate deletions.
The compress-robot-data lifecycle policy checks once per hour and compresses
persisted blocks older than one day with zstd. Compression reduces local disk
usage without deleting records, and reads remain transparent. The FIFO quota is
still the final storage bound, so size it for the longest expected network
outage and data rate to avoid evicting records before they are replicated.
See ReductStore provisioning for all environment variables, data replication for entry and label filters, and lifecycle policies for compression and retention options.
Data Querying and Visualization
Query and visualization tools should normally connect to the cloud or central on-prem ReductStore instance instead of the robot. The central instance is more available for operators and downstream services, can aggregate replicated data from multiple robots, and avoids depending on a robot being online when users inspect historical data.