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:
For background on the module registry, versioning, and cloud builds, see the overview.
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 | No | List of resource models the module provides. Each has api, model, and optionally short_description and markdown_link. Models can be auto-detected with viam module update-models --binary. |
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
Document your module and its models so users know how to configure and use them. If you plan to make your module public, a good README is essential.
You can point your module’s registry page to a README by setting the
markdown_link field in meta.json to a file path (for example, README.md) or a
section anchor (for example, README.md#my-sensor).
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.
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
The generated build.sh uses PyInstaller
to compile your module into a standalone executable containing the Python
interpreter and all dependencies.
.). If you get "ImportError: attempted relative import with no known parent package", see the
PyInstaller workaround.
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!