Previous
Write a Logic Module
A module that only runs locally on your development machine is useful for testing but limits what you can do. Deploying through the Viam module registry lets you:
The Viam module registry is a package manager for modules. It stores versioned
module packages and serves them to machines on demand. When you configure a
module on a machine, viam-server downloads the correct version for the
machine’s platform (OS and architecture).
Modules can be:
Every module has a meta.json file that describes it to the registry:
namespace:module-name.private or public.Cloud build uses GitHub Actions to compile your module for multiple platforms
automatically. When you push a version tag (for example, v0.1.0), a workflow builds
your module for each target architecture, packages it, and uploads it to the
registry.
The registry uses semantic versioning. Machines can be configured to:
The generator creates a meta.json file in your module directory. Open it and
review each field:
{
"module_id": "my-org:my-sensor-module",
"visibility": "private",
"url": "https://github.com/my-org/my-sensor-module",
"description": "A custom sensor module that reads temperature and humidity from an HTTP endpoint.",
"models": [
{
"api": "rdk:component:sensor",
"model": "my-org:my-sensor-module:my-sensor"
}
],
"entrypoint": "run.sh",
"build": {
"setup": "./setup.sh",
"build": "./build.sh",
"path": "dist/archive.tar.gz",
"arch": ["linux/amd64", "linux/arm64"]
}
}
| Field | Required | Purpose |
|---|---|---|
$schema | No | JSON Schema URL for editor validation. Set to https://dl.viam.dev/module.schema.json. |
module_id | Yes | Unique ID in the registry. Format: namespace:name. |
visibility | Yes | Who can see and install the module: private, public, or public_unlisted. |
url | No | Link to the source code repository. Required for cloud builds. |
description | Yes | Shown in the registry UI and search results. |
models | Yes | List of resource models the module provides. Each has api, model, and optionally description and markdown_link. |
entrypoint | Yes | The path to the command that starts the module inside the archive. |
first_run | No | Path to a setup script that runs once after first install (default timeout: 1 hour). |
markdown_link | No | Path to a README file (or README.md#section anchor) used as the registry description. |
build.setup | No | Script that installs build dependencies (runs once). |
build.build | No | Script that compiles and packages the module. |
build.path | No | Path to the packaged output archive (default: module.tar.gz). |
build.arch | No | Target platforms to build for (default: ["linux/amd64", "linux/arm64"]). |
build.darwin_deps | No | Homebrew dependencies for macOS builds (for example, ["go", "pkg-config"]). |
Visibility options:
private – only your organization can see and use the module.public – all Viam users can see and use it. Requires your organization
to have a public namespace.public_unlisted – any user can use the module if they know the ID, but
it does not appear in registry search results.Common api values:
rdk:component:sensor for sensorsrdk:component:camera for camerasrdk:component:motor for motorsrdk:component:generic for generic componentsrdk:service:vision for vision servicesThe generator creates build and setup scripts for your module. Review them and customize if needed:
| File | Purpose |
|---|---|
setup.sh | Installs Python dependencies from requirements.txt |
build.sh | Packages the module into a .tar.gz archive |
run.sh | Entrypoint script that starts the module |
If your module has additional build steps (for example, compiling native extensions),
add them to build.sh.
| File | Purpose |
|---|---|
setup.sh | Installs build dependencies (Go modules are typically self-contained) |
build.sh | Cross-compiles the binary and packages it into a .tar.gz archive |
Makefile | Local build targets |
The generated build.sh uses GOOS and GOARCH environment variables to
cross-compile for the target platform. Cloud build sets these automatically.
Make sure all scripts are executable:
chmod +x setup.sh build.sh run.sh
Cloud build is the recommended way to deploy modules. It uses GitHub Actions to
compile your module for every target platform automatically, so you don’t need
to cross-compile locally. This eliminates a common class of errors where a
binary built for one architecture (for example, linux/amd64) is uploaded for a
different one (for example, linux/arm64), resulting in exec format errors on the
target machine.
The generator creates the workflow file at
.github/workflows/deploy.yml. To use it:
Push your code to GitHub:
cd my-sensor-module
git init
git add .
git commit -m "Initial module code"
git remote add origin https://github.com/my-org/my-sensor-module.git
git push -u origin main
Add Viam credentials as GitHub secrets:
VIAM_KEY_ID – your API key IDVIAM_KEY_VALUE – your API keyTag a release to trigger the build:
git tag v0.1.0
git push origin v0.1.0
The GitHub Action runs automatically. Monitor progress in the Actions tab of your GitHub repository. When it completes, your module is in the registry and ready to install on any machine.
You can also trigger a cloud build from the CLI:
viam module build start
Cloud build expects your repository’s default branch to be main. If your
repository uses a different default branch (for example, master), use the --ref
flag:
viam module build start --ref master
If you cannot use cloud build, you can build and upload from the command line.
When you upload manually, the binary in your archive must already be compiled
for the target machine’s OS and architecture. If you build on an x86 laptop
and upload for linux/arm64 without cross-compiling, the module will fail
with an exec format error on ARM machines (like Raspberry Pi).
Cloud build handles this automatically. If you deploy manually, you must cross-compile yourself or build on a machine with the target architecture.
Build locally:
cd my-sensor-module
bash build.sh
Python modules don’t require cross-compilation since they run in an interpreter, but native dependencies (C extensions) may need to be built for the target platform.
cd my-sensor-module
# Cross-compile for the target platform
GOOS=linux GOARCH=arm64 go build -o dist/module cmd/module/main.go
tar -czf dist/archive.tar.gz -C dist module
Set GOARCH to match your target machine: amd64 for x86_64, arm64 for
ARM (Raspberry Pi 4, Jetson, etc.).
Upload to the registry:
viam module upload \
--version=0.1.0 \
--platform=linux/arm64 \
dist/archive.tar.gz
To support multiple platforms, cross-compile and upload once per platform:
# Build and upload for amd64
GOOS=linux GOARCH=amd64 go build -o dist/module cmd/module/main.go
tar -czf dist/archive.tar.gz -C dist module
viam module upload --version=0.1.0 --platform=linux/amd64 dist/archive.tar.gz
# Build and upload for arm64
GOOS=linux GOARCH=arm64 go build -o dist/module cmd/module/main.go
tar -czf dist/archive.tar.gz -C dist module
viam module upload --version=0.1.0 --platform=linux/arm64 dist/archive.tar.gz
Once your module is in the registry, any machine in your organization can use it.
{
"source_url": "https://api.example.com/sensor/data"
}
viam-server downloads the module from the registry, starts it, and makes the
component available. Test it from the CONTROL tab.
Release a new version:
git add .
git commit -m "Add humidity calibration offset"
git tag v0.2.0
git push origin main v0.2.0
If using cloud build, the workflow runs automatically. For manual upload:
viam module upload --version=0.2.0 --platform=linux/amd64 dist/archive.tar.gz
Automatic updates: By default, machines track the latest version. When you
upload v0.2.0, all machines update automatically within a few minutes.
Pin to a specific version:
0.1.0).View module details:
You can view version history and details for your module in the Viam registry.
As you add models to your module, you can auto-detect them from a built binary
instead of editing meta.json by hand:
viam module update-models --binary ./bin/module
This inspects the binary, discovers registered models, and updates the models
array in meta.json.
Then push the updated metadata to the registry:
viam module update
To download a module from the registry (for testing or inspection):
viam module download --id my-org:my-sensor-module --version 0.1.0 --platform linux/amd64 --destination ./downloaded-module
When uploading, you can attach platform constraint tags that restrict which machines can use a particular upload. For example, to require Debian:
viam module upload \
--version=0.1.0 \
--platform=linux/amd64 \
--tags=distro:debian \
dist/archive.tar.gz
The machine must report matching platform tags for the constrained upload to be selected. If no constraints are specified, the upload is available to all machines on that platform.
The registry enforces these size limits:
| Limit | Value |
|---|---|
Compressed package (.tar.gz) | 50 GB |
| Decompressed contents | 250 GB |
| Single file within package | 25 GB |
Before uploading, the CLI validates that:
Use --force to skip these checks (not recommended for production uploads).
meta.json and build scripts in your module directory.v0.1.0) to trigger a cloud build.v0.2.0) and verify the machine picks it up
automatically within a few minutes.Was this page helpful?
Glad to hear it! If you have any other feedback please let us know:
We're sorry about that. To help us improve, please tell us what we can do better:
Thank you!