TL;DR:

  • Greengrass V3 (the current version, also called AWS IoT Greengrass 2.x) uses a component-based architecture that replaces V1’s Lambda-centric model — components can be Lambda functions, Docker containers, or native processes
  • Local ML inference via the DLR runtime component runs SageMaker-trained models on edge devices without cloud API calls, achieving sub-100ms inference latency for common CV and tabular models
  • The nucleus installer runs on anything from Raspberry Pi (ARM) to industrial gateways (x86), making deployment consistent across heterogeneous fleets
  • Component lifecycle hooks and fleet deployments via Greengrass fleet deployment console make OTA updates to thousands of edge devices manageable

AWS IoT Greengrass bridges the gap between cloud-trained models and on-device execution. The V3 (2.x) release, which has been the production-standard version since 2021 and continues to receive new component library additions in 2026, represents a significant architectural improvement over V1: instead of tying edge workloads tightly to Lambda functions, it introduces a component model that can run arbitrary code alongside Lambda functions and containers, with proper dependency management between components.

This guide focuses on the two highest-value Greengrass use cases for industrial edge environments: running ML inference locally (without cloud round-trips) and deploying edge Lambda functions for local processing and protocol translation.

Architecture Overview

A Greengrass core device is any device running the Greengrass nucleus — the lightweight Java runtime that manages components, connects to AWS IoT Core, and handles deployment updates. Above the nucleus sits a set of components:

┌─────────────────────────────────────────────────────────┐
│                    AWS Cloud                            │
│  IoT Core ← → Greengrass Deployments ← → S3 (artifacts)│
└──────────────────────┬──────────────────────────────────┘
                       │ MQTT / HTTPS
┌──────────────────────▼──────────────────────────────────┐
│                 Edge Device (Core Device)               │
│  ┌────────────────────────────────────────────────────┐ │
│  │            Greengrass Nucleus (JVM)               │ │
│  ├──────────────────────┬─────────────────────────────┤ │
│  │ Component: ML Model  │ Component: Lambda Function  │ │
│  │ (DLR runtime)        │ (sensor data processor)     │ │
│  ├──────────────────────┼─────────────────────────────┤ │
│  │ Component: MQTT      │ Component: Stream Manager   │ │
│  │ Bridge               │ (local data buffering)      │ │
│  └──────────────────────┴─────────────────────────────┘ │
│                                                         │
│  Local sensors / PLCs / cameras (via protocols)        │
└─────────────────────────────────────────────────────────┘

Components communicate through Greengrass IPC (inter-process communication) — a local pub/sub mechanism that allows components to exchange messages without cloud round-trips.

Installing the Greengrass Nucleus

The nucleus installer handles device registration and initial setup. On an industrial gateway or Raspberry Pi running Linux:

# Download installer (example for Linux ARM64)
curl -s https://d2s8p88vqu9w66.cloudfront.net/releases/greengrass-nucleus-latest.zip \
  -o greengrass-nucleus-latest.zip
unzip greengrass-nucleus-latest.zip -d GreengrassInstaller

# Install with automatic provisioning (creates IAM roles, IoT Thing, certificates)
sudo -E java -Droot="/greengrass/v2" -Dlog.store=FILE \
  -jar ./GreengrassInstaller/lib/Greengrass.jar \
  --aws-region eu-west-1 \
  --thing-name ProductionLineGateway01 \
  --thing-group-name FactoryFloor_A \
  --component-default-user ggc_user:ggc_group \
  --provision true \
  --setup-system-service true \
  --deploy-dev-tools true

The --provision true flag creates the required IAM roles, IoT Thing, and certificate infrastructure automatically. For production fleets, you’ll use fleet provisioning with a provisioning template instead.

Minimum requirements:

  • Java 8 or higher (nucleus only; ML components may require more)
  • 500MB RAM for the nucleus (more for ML components)
  • Any Linux distribution (tested on Debian, Ubuntu, RHEL, Amazon Linux 2)
  • ARM32, ARM64, or x86_64

Component Architecture: Beyond Lambda

V3’s component model is the key architectural improvement over V1. A component recipe (YAML) defines everything needed to deploy a workload:

# recipe.yaml for a custom sensor aggregation component
RecipeFormatVersion: "2020-01-25"
ComponentName: com.example.SensorAggregator
ComponentVersion: "1.2.0"
ComponentDescription: "Aggregates temperature and vibration data from OPC-UA sources"
ComponentPublisher: "Example Corp"
ComponentDependencies:
  aws.greengrass.StreamManager:
    VersionRequirement: ">=2.1.0"
    DependencyType: HARD
ComponentConfiguration:
  DefaultConfiguration:
    opcuaEndpoint: "opc.tcp://plc-01.factory.local:4840"
    samplingIntervalMs: 500
    alertThresholdTempC: 85.0
Manifests:
  - Platform:
      os: linux
    Artifacts:
      - URI: "s3://my-gg-artifacts/sensor-aggregator/1.2.0/sensor_aggregator.zip"
        Unarchive: ZIP
    Lifecycle:
      Install:
        Script: "pip3 install -r {artifacts:decompressedPath}/sensor_aggregator/requirements.txt"
      Run:
        Script: "python3 {artifacts:decompressedPath}/sensor_aggregator/main.py"
        Setenv:
          OPCUA_ENDPOINT: "{configuration:/opcuaEndpoint}"
          SAMPLING_INTERVAL_MS: "{configuration:/samplingIntervalMs}"
          ALERT_THRESHOLD: "{configuration:/alertThresholdTempC}"

The ComponentDependencies section handles ordering — Greengrass starts Stream Manager before your component if it depends on it. The ComponentConfiguration section allows per-deployment configuration overrides without rebuilding the component.

Local ML Inference with DLR

The Deep Learning Runtime (DLR) Greengrass component runs Neo-compiled models locally. The workflow:

  1. Train a model in SageMaker (or bring your own)
  2. Compile it with SageMaker Neo for your target hardware
  3. Upload compiled model to S3
  4. Reference it in a Greengrass ML component recipe
# ML component recipe: anomaly detection on vibration data
RecipeFormatVersion: "2020-01-25"
ComponentName: com.example.VibrationAnomalyDetection
ComponentVersion: "2.0.0"
ComponentDependencies:
  variant.DLR:
    VersionRequirement: ">=1.6.0"
  aws.greengrass.SageMakerEdgeManager:
    VersionRequirement: ">=1.3.0"
Manifests:
  - Platform:
      os: linux
      architecture: arm64
    Artifacts:
      - URI: "s3://my-gg-artifacts/models/vibration-anomaly-arm64.tar.gz"
        Unarchive: TAR_GZ
      - URI: "s3://my-gg-artifacts/vibration-detector/inference_handler.py"
    Lifecycle:
      Run:
        Script: "python3 {artifacts:path}/inference_handler.py"

The inference handler loads the compiled model via DLR and subscribes to sensor data from other components:

# inference_handler.py
import dlr
import json
import numpy as np
import awsiot.greengrasscoreipc
import awsiot.greengrasscoreipc.model as model

# Load the compiled model
MODEL_PATH = "/greengrass/v2/packages/artifacts/com.example.VibrationAnomalyDetection/2.0.0"
model_instance = dlr.DLRModel(MODEL_PATH, "cpu")

ipc_client = awsiot.greengrasscoreipc.connect()

class VibrationDataHandler(model.SubscribeToTopicStreamHandler):
    def on_stream_event(self, event):
        payload = json.loads(event.message.payload.decode())
        
        # Extract features from sensor payload
        features = np.array([
            payload["rms_velocity"],
            payload["peak_acceleration"],
            payload["dominant_frequency"],
            payload["spectral_kurtosis"]
        ], dtype=np.float32).reshape(1, -1)
        
        # Run local inference (no cloud round-trip)
        result = model_instance.run({"input": features})
        anomaly_score = float(result[0][0])
        
        # Publish result locally
        alert = anomaly_score > 0.85
        output_payload = json.dumps({
            "device_id": payload["device_id"],
            "anomaly_score": anomaly_score,
            "alert": alert,
            "timestamp": payload["timestamp"]
        }).encode()
        
        publish_request = model.PublishToTopicRequest(
            topic="factory/vibration/anomaly-results",
            publish_message=model.PublishMessage(
                binary_message=model.BinaryMessage(message=output_payload)
            )
        )
        ipc_client.new_publish_to_topic().activate(publish_request).result()

# Subscribe to vibration sensor data topic
subscribe_request = model.SubscribeToTopicRequest(
    topic="factory/sensors/vibration"
)
ipc_client.new_subscribe_to_topic().activate(
    subscribe_request, VibrationDataHandler()
)

# Keep running
import time
while True:
    time.sleep(1)

Inference latency: Neo-compiled models on ARM64 hardware (Raspberry Pi 5, Jetson Nano, most industrial gateways) typically achieve 15-80ms inference time for tabular anomaly detection models, and 50-200ms for small computer vision models. This enables local alerting and control loop decisions that cloud inference (200ms+ round-trip before processing) couldn’t support.

Lambda Functions at the Edge

Lambda functions on Greengrass run the same code as cloud Lambda functions but execute locally. This is most useful for:

  • Protocol translation (Modbus/OPC-UA → MQTT)
  • Local filtering to reduce cloud data transfer costs
  • Low-latency control loop responses
# Lambda function for local Modbus → MQTT translation
# Deployed as a Greengrass Lambda component
import json
import greengrasssdk
import struct
from pymodbus.client import ModbusTcpClient

client = greengrasssdk.client("iot-data")
modbus = ModbusTcpClient("192.168.1.100", port=502)
modbus.connect()

def function_handler(event, context):
    # Read from Modbus register (input: register address and count)
    register = event.get("register", 0)
    count = event.get("count", 1)
    
    result = modbus.read_holding_registers(register, count)
    if result.isError():
        return {"error": str(result)}
    
    values = result.registers
    
    # Publish to local topic (stays on device unless routed to cloud)
    client.publish(
        topic=f"factory/plc01/register/{register}",
        payload=json.dumps({
            "register": register,
            "values": values,
            "timestamp": context.invoked_function_arn
        })
    )
    return {"registers": values}

Lambda components on Greengrass support the same deployment lifecycle as cloud Lambda — you upload a deployment package, define the component in a recipe, and deploy via the fleet console.

Stream Manager: Local Buffering for Unreliable Connectivity

Industrial environments often have intermittent connectivity. Greengrass Stream Manager provides local FIFO queues that buffer data during outages and export to S3, Kinesis, or IoT SiteWise when connectivity resumes:

# Writing sensor data to a local stream
from stream_manager import StreamManagerClient, MessageStreamDefinition, \
    StrategyOnFull, ExportDefinition, KinesisConfig

sm_client = StreamManagerClient()

# Create a persistent local stream
sm_client.create_message_stream(
    MessageStreamDefinition(
        name="FactorySensorStream",
        max_size=268435456,  # 256MB local buffer
        strategy_on_full=StrategyOnFull.OverwriteOldestData,
        export_definition=ExportDefinition(
            kinesis=[KinesisConfig(
                identifier="ToKinesisFirehose",
                kinesis_stream_name="factory-telemetry-stream"
            )]
        )
    )
)

# Sensor data handler writes to local stream
def publish_sensor_reading(device_id: str, readings: dict):
    payload = json.dumps({"device_id": device_id, **readings}).encode()
    sm_client.append_message("FactorySensorStream", payload)

Data accumulates locally in the stream buffer and exports automatically when cloud connectivity is available. For facilities with intermittent connectivity (remote sites, environments with radio interference), this prevents data loss without requiring application-level retry logic.

Fleet Deployments and OTA Updates

Deploying component updates to thousands of edge devices uses Greengrass fleet deployments via the AWS Console or API:

import boto3

greengrassv2 = boto3.client("greengrassv2", region_name="eu-west-1")

# Deploy updated ML model to all devices in a thing group
response = greengrassv2.create_deployment(
    targetArn="arn:aws:iot:eu-west-1:123456789:thinggroup/FactoryFloor_A",
    deploymentName="VibrationModel-v2.0-Production",
    components={
        "com.example.VibrationAnomalyDetection": {
            "componentVersion": "2.0.0",
            "configurationUpdate": {
                "merge": json.dumps({"alertThreshold": 0.80})
            }
        }
    },
    deploymentPolicies={
        "componentUpdatePolicy": {
            "action": "NOTIFY_COMPONENTS",
            "timeoutInSeconds": 300
        },
        "configurationValidationPolicy": {
            "timeoutInSeconds": 60
        },
        "failureHandlingPolicy": "ROLLBACK"  # Auto-rollback on failure
    },
    iotJobConfiguration={
        "jobExecutionsRolloutConfig": {
            "maximumPerMinute": 50  # Rate-limit rollout
        }
    }
)

The ROLLBACK failure handling policy automatically reverts to the previous component version if a deployment fails on any device. The rollout rate limit (maximumPerMinute) prevents a bad deployment from hitting thousands of devices simultaneously.

Monitoring Edge Deployments

Greengrass devices publish health metrics and logs to CloudWatch:

# CloudWatch metric filter for component failures
import boto3

logs_client = boto3.client("logs", region_name="eu-west-1")

# Greengrass publishes to /aws/greengrass/GreengrassSystem/{component}
logs_client.put_metric_filter(
    logGroupName="/aws/greengrass/GreengrassSystem/com.example.VibrationAnomalyDetection",
    filterName="InferenceErrors",
    filterPattern="[level=ERROR, ...]",
    metricTransformations=[{
        "metricName": "EdgeInferenceErrors",
        "metricNamespace": "Factory/EdgeML",
        "metricValue": "1",
        "unit": "Count"
    }]
)

For operational dashboards, Greengrass core devices also publish a $aws/things/{thingName}/greengrasscore/health/json MQTT topic with component health status, making it possible to build fleet-health dashboards without CloudWatch.

When to Use Greengrass vs Alternatives

Use Greengrass when:

  • You’re already in the AWS ecosystem (IoT Core, SageMaker, S3)
  • You need managed OTA deployments across a large heterogeneous fleet
  • Local ML inference is a requirement and Neo compilation for your hardware is available
  • You need local data buffering with guaranteed delivery to cloud services

Consider alternatives when:

  • Your edge hardware runs real-time OS (Zephyr, FreeRTOS) — Greengrass requires Linux and JVM
  • Latency requirements are sub-10ms — the JVM overhead and IPC layer add latency that custom C/C++ inference pipelines avoid
  • You’re in a pure Azure or GCP environment — Azure IoT Operations and Google Distributed Cloud have equivalent functionality that integrates better with their respective cloud services
  • Your team’s edge expertise is in Kubernetes — KubeEdge or K3s may be a more natural fit

Greengrass V3’s component model and fleet deployment capabilities make it the right default for AWS-centric industrial IoT deployments. The learning curve is non-trivial, but the operational benefits — consistent deployment, automatic rollback, local ML inference — justify it for any fleet larger than a handful of devices.