Building and Deploying Greengrass Components in a Dockerized Environment
Develop AWS IoT Greengrass components locally using the Greengrass Core Docker image.
The Greengrass Core Docker image provides a local environment for developing AWS IoT Greengrass components without physical edge hardware. See the official documentation for details.
This guide builds a Greengrass component that publishes an MQTT message to AWS IoT Core every second and deploys it locally to a Docker container with the Greengrass CLI.

By the end of this example, your project directory will look like this:
components/├── mqtt_publisher/│ ├── .gitignore│ ├── gdk-config.json│ ├── main.py│ ├── recipe.yaml│ ├── requirements.txtdocker/├── greengrass-v2-credentials/│ ├── credentials├── .env├── docker-compose.ymlDeveloping a Custom Greengrass Component
Installing Greengrass Development Kit (GDK)
Install the Greengrass Development Kit (GDK):
Running pip install gdk installs a library unrelated to the Greengrass Development Kit.
Starting Development
Initialize your Greengrass component by running gdk component init:
mkdir ./componentsgdk component init \ --language python \ --template HelloWorld \ --name components/mqtt_publisherThis command generates a basic component structure with the following files and directories:
components/├── mqtt_publisher/│ ├── src/│ │ ├── greeter.py│ ├── tests/│ │ ├── test_greeter.py│ ├── .gitignore│ ├── gdk-config.json│ ├── main.py│ ├── README.md│ ├── recipe.yaml
The src and tests directories will not be used in this example.
Configuring Component Metadata
Update the gdk-config.json file with the component metadata. If the component is not being published to an S3 bucket using gdk component publish, the publish.bucket field (line 10) does not need to be set.
Here’s an example of the updated gdk-config.json:
{ "component": { "com.example.MqttPublisher": { "author": "wasabee.dev", "version": "0.0.1", "build": { "build_system": "zip" }, "publish": { "bucket": "<PLACEHOLDER_BUCKET>", "region": "ap-northeast-1" } } }, "gdk_version": "1.0.0"}For additional details, refer to the official documentation on the GDK CLI configuration file.
Do not use NEXT_PATCH as the value for version. It will cause errors when deploying the component using greengrass-cli deployment create.
Writing the Python Script
Create a main.py script for your component. This script will publish MQTT messages to the /mqtt-publisher topic every second.
import jsonimport randomfrom datetime import datetimefrom time import sleep
import boto3
client = boto3.client('iot-data')
def main(): payload = { "value": random.randint(1, 10000), "datetime": datetime.now().strftime('%Y-%m-%d %H:%M:%S'), } while True: client.publish( topic='/mqtt-publisher', payload=json.dumps(payload).encode(), qos=1, contentType='application/json', ) print(f'Message was sent successfully: {payload}') sleep(1)
if __name__ == "__main__": main()Create requirements.txt to list the component’s dependencies. The install lifecycle defined in recipe.yaml installs them when the component is deployed.
boto3==1.26.65Component Recipe
Create recipe.yaml to define the component’s metadata, dependencies, artifacts, and lifecycle commands. See the official documentation for the recipe specification.
Here’s an example:
---RecipeFormatVersion: "2020-01-25"ComponentName: "{COMPONENT_NAME}"ComponentVersion: "{COMPONENT_VERSION}"ComponentDescription: "This is an mqtt publisher written in Python."ComponentPublisher: "{COMPONENT_AUTHOR}"ComponentDependencies: aws.greengrass.TokenExchangeService: VersionRequirement: '^2.0.0'Manifests: - Platform: os: all Artifacts: - URI: "s3://BUCKET_NAME/COMPONENT_NAME/COMPONENT_VERSION/mqtt_publisher.zip" Unarchive: ZIP Lifecycle: Install: "pip3 install --user -r {artifacts:decompressedPath}/mqtt_publisher/requirements.txt" Run: "python3 -u {artifacts:decompressedPath}/mqtt_publisher/main.py"Component Dependencies
Because the script uses boto3 to communicate with AWS IoT Core, add the aws.greengrass.TokenExchangeService component to ComponentDependencies. The token exchange service exposes a local endpoint that supplies AWS credentials to the custom component.
For more details, refer to the official documentation.
AWS IoT Greengrass provides a public component, the token exchange service component, that you can define as a dependency in your custom component to interact with AWS services. The token exchange service provides your component with an environment variable, AWS_CONTAINER_CREDENTIALS_FULL_URI, that defines the URI to a local server that provides AWS credentials.
Lifecycle Hooks
The Lifecycle section specifies commands to execute during component installation and at runtime:
- Install: Installs Python libraries listed in
requirements.txt. - Run: Executes the
main.pyscript when the component starts.
Recipe Placeholders
Placeholders in the recipe (e.g., {COMPONENT_NAME}) are replaced with values from gdk-config.json during the build process. These placeholders include:
{COMPONENT_NAME}{COMPONENT_VERSION}{COMPONENT_AUTHOR}- Artifacts URI (
BUCKET_NAME,COMPONENT_NAME, andCOMPONENT_VERSION)
Building the Component
Use gdk component build to build the component with the Greengrass Development Kit:
cd components/mqtt_publishergdk component buildThe build places the recipes and artifacts in greengrass-build. A local Docker deployment does not require gdk component publish.
Using NEXT_PATCH as the version value in gdk-config.json will cause deployment failures when running bin/greengrass-cli deployment create.
Greengrass Core in Docker
This section explains how to set up Greengrass Core in a Docker container, configure credentials, and deploy components.
For the remaining steps, work in the <PROJECT_ROOT>/docker directory.
Security Credentials
Greengrass Core requires AWS credentials for automatic resource provisioning. Use temporary credentials from sts get-session-token instead of long-term credentials.
The following AWS resources will be provisioned:
- AWS IoT
- Greengrass Core Device
- IoT Thing
- IoT Thing Group
- Certificate
- Policies (two)
- Token Exchange Role Alias
- AWS IAM
- Token Exchange Role
- Token Exchange Role Policy
Generate temporary credentials:
aws sts get-session-tokenSave the credentials to a file:
mkdir ./greengrass-v2-credentialsnano ./greengrass-v2-credentials/credentialsExample content for credentials:
[default]aws_access_key_id = AKIAIOSFODNN7EXAMPLEaws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEYaws_session_token = AQoEXAMPLEH4aoAH0gNCAPy...truncated...zrkuWJOgQs8IZZaIv2BXIa2R4OlgkEnvironment File
Create a .env file to configure environment variables for the Greengrass Core installer. Refer to the official documentation for more details.
Example .env file:
GGC_ROOT_PATH=/greengrass/v2AWS_REGION=ap-northeast-1PROVISION=trueTHING_NAME=MyGreengrassCoreTHING_GROUP_NAME=MyGreengrassCoreGroupTES_ROLE_NAME=GreengrassV2TokenExchangeRoleTES_ROLE_ALIAS_NAME=GreengrassCoreTokenExchangeRoleAliasCOMPONENT_DEFAULT_USER=ggc_user:ggc_groupRunning Greengrass Core
Create a docker-compose.yml file to run Greengrass Core in Docker. Refer to the documentation for further information.
Example docker-compose.yml:
version: '3.7'
services: greengrass: init: true container_name: aws-iot-greengrass image: amazon/aws-iot-greengrass:latest volumes: - ./greengrass-v2-credentials:/root/.aws/:ro - ../components:/root/components env_file: .env ports: - '8883:8883'Run the container:
docker-compose up -ddocker-compose logs -f greengrassThe logs should show that the Nucleus launched successfully:
aws-iot-greengrass | Launching Nucleus...aws-iot-greengrass | Launched Nucleus successfully.Deploying AWS-provided Components
Greengrass CLI
Install the Greengrass CLI component (aws.greengrass.Cli) for local deployments. After installation, it can be found in /greengrass/v2/bin.
docker-compose exec greengrass bashcd /greengrass/v2ls binDo not use Greengrass CLI in production environments.
We recommend that you use this component in only development environments, not production environments. This component provides access to information and operations that you typically won’t need in a production environment. Follow the principle of least privilege by deploying this component to only core devices where you need it.
Token Exchange Service
Deploy aws.greengrass.TokenExchangeService so the custom component can call AWS services through credentials supplied by its local endpoint.
https://docs.aws.amazon.com/greengrass/v2/developerguide/interact-with-aws-services.html
Greengrass core devices use X.509 certificates to connect to AWS IoT Core using TLS mutual authentication protocols. These certificates let devices interact with AWS IoT without AWS credentials, which typically comprise an access key ID and a secret access key.
Deploying from AWS IoT Greengrass Console
Use the AWS IoT Greengrass console to deploy the AWS-provided components, including Greengrass Nucleus.







After the deployment succeeds, /greengrass/v2/logs/greengrass.log contains entries like the following:
[INFO] (Thread-4) com.aws.greengrass.deployment.IotJobsHelper: Job status update was accepted. {Status=SUCCEEDED, ThingName=MyGreengrassCore, JobId=}[INFO] (pool-2-thread-11) com.aws.greengrass.status.FleetStatusService: fss-status-update-published. Status update published to FSS. {trigger=THING_GROUP_DEPLOYMENT, serviceName=FleetStatusService,[INFO] (pool-2-thread-11) com.aws.greengrass.deployment.DeploymentDirectoryManager: Persist link to last deployment. {link=/greengrass/v2/deployments/previous-success}[INFO] (Thread-4) com.aws.greengrass.deployment.IotJobsHelper: Received empty jobs in notification . {ThingName=MyGreengrassCore}Updating Token Exchange Role
Update the GreengrassV2TokenExchangeRole IAM policy to grant permissions for MQTT publishing:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "iot:Connect", "Resource": "*" }, { "Effect": "Allow", "Action": "iot:Publish", "Resource": "arn:aws:iot:*:<AWS_ACCOUNT_ID>:topic//mqtt-publisher*" } ]}Attach the policy:
aws iam put-role-policy \ --role-name GreengrassV2TokenExchangeRole \ --policy-name IoTPolicy \ --policy-document file://policy.jsonDeploying the Custom Component Locally
Deploy your custom component using the Greengrass CLI greengrass-cli deployment create inside the Docker container.
cd /greengrass/v2bin/greengrass-cli deployment create \ --recipeDir /root/components/mqtt_publisher/greengrass-build/recipes \ --artifactDir /root/components/mqtt_publisher/greengrass-build/artifacts \ --merge "com.example.MqttPublisher=0.0.1"Check the deployment status using greengrass-cli deployment status:
bin/greengrass-cli deployment status -i <DEPLOYMENT_ID>The command returns a successful deployment status:
INFO: Connection established with event stream RPC server<DEPLOYMENT_ID>: SUCCEEDEDMonitor logs to ensure the component is running:
cd /greengrass/v2/logstail -f com.example.MqttPublisher.logExpected log output:
[INFO] (Copier) com.example.MqttPublisher: stdout. Message was sent successfully: {'value': 31, 'datetime': '2023-02-27 12:31:35'}. {scriptName=services.com.example.MqttPublisher.lifecycle.Run, serviceName=com.example.MqttPublisher, currentState=RUNNING}Testing with AWS IoT Test Client
Use the MQTT test client in the AWS IoT console to verify messages published to the /mqtt-publisher topic.
- Open the MQTT test client.
- Enter
/#or/mqtt-publisherinTopic filter. - Click
Subscribe.
You should see the published messages from your custom component.

Conclusion
The GDK and the Greengrass Core Docker image provide a local workflow for building an MQTT-publishing component and verifying its messages in AWS IoT Core without physical edge hardware.
The GDK build, local greengrass-cli deployment create command, and component logs provide a short development feedback loop. The same recipe and artifact structure can later be used with a physical core device.
Do not deploy the Greengrass CLI component to production devices. It exposes local deployment and debugging operations intended only for development environments.
Related posts
Streaming OPC UA Data to Kinesis via SiteWise Edge Gateway
Bridging OPC UA telemetry to Kinesis Data Streams through SiteWise Edge Gateway and a custom Greengrass component.
Configuring Record Separators for Kinesis Firehose in AWS IoT Core
Configure a record separator in an IoT Core topic rule's Firehose action so that records stored in S3 are separated by newlines.
Partitioning Kinesis Records with AWS IoT Payload Values
Use a customer ID from an IoT topic payload as the Kinesis partition key to preserve record order for each customer.
Sign in with Slack Using Cognito User Pools and OIDC
Federating Cognito user pools with Slack over OIDC and wiring "Sign in with Slack" into a Next.js app with Amplify.
Deploying FastAPI on AWS Lambda with Lambda Web Adapter
Containerizing a FastAPI backend and deploying it to a single Lambda function with Lambda Web Adapter and AWS CDK.
