# Build and deploy modules with the CLI

Scaffold a new module, iterate on it locally with hot-reload, upload it to the registry, and manage versions and cloud builds.

##### Prerequisites

You need the Viam CLI installed and authenticated.
See [Viam CLI overview](https://docs.viam.com/cli/overview/) for installation and authentication instructions.

## Find your IDs

To find the part ID for a running machine (needed for reload and restart):

```sh
viam machines list --organization=<org-id> --location=<location-id>
```

Copy

```sh
viam machines part list --machine=<machine-id>
```

Copy

To find your organization and location IDs:

```sh
viam organizations list
```

Copy

```sh
viam locations list
```

Copy

## Scaffold a new module

Generate a module project with boilerplate code, a `meta.json` manifest, and a build script.
This command does not require authentication, so you can scaffold a module before logging in.

```sh
viam module generate
```

Copy

The generator first asks whether you want to generate a **module** or an **app**.
Choose **Module**, then follow the interactive prompts to choose:

- Module name
- Programming language (Python, Go, or C++)
- Namespace and visibility
- Resource type (component or service) and API

To generate a hosted web application instead of a module, choose **App** or see [Deploy a Viam application](https://docs.viam.com/build-apps/hosting/deploy/).

You can also pass flags to skip the interactive prompts:

```sh
viam module generate \
  --generate-type=module \
  --name=my-sensor-module \
  --language=python \
  --visibility=public
```

Copy

To add a new resource model to an existing module, use [`viam module add-model`](https://docs.viam.com/cli/reference/#module-add-model) from within the module directory.
To add a web application to an existing Go module, use [`viam module add-app`](https://docs.viam.com/cli/reference/#module-add-app).

## Iterate during development

After making code changes, reload your module on a running machine without restarting the entire machine. By default, the CLI builds the module in the cloud and syncs the new binary to the target:

```sh
viam module reload --part-id=<part-id>
```

Copy

If your development machine is also running the module (for example, developing a macOS module on the Mac that runs `viam-server`), use `reload-local` to build and reload without a cloud build:

```sh
viam module reload-local --part-id=<part-id>
```

Copy

If a reload is not sufficient, restart the module process:

```sh
viam module restart --part-id=<part-id>
```

Copy

## Update model definitions

After adding or changing models in your module, update the model definitions in `meta.json`.
This command runs the module’s executable (binary or script) in a sandbox, queries it for the API-model pairs it advertises, and updates the manifest.
It also auto-detects markdown documentation files named `namespace_module_model.md`.

If you omit `--binary`, the CLI uses the entrypoint declared in `meta.json`:

```sh
viam module update-models
```

Copy

To point at a specific executable, pass `--binary`:

```sh
viam module update-models --binary=./bin/module
```

Copy

Then push the updated `meta.json` to the registry:

```sh
viam module update
```

Copy

## Upload to the registry

Upload a module version for a specific platform.
The CLI validates the tarball before uploading: it checks for an executable at the declared entrypoint, verifies file permissions, and warns about platform mismatches or symlinks escaping the archive.
Pass `--force` to skip validation.

```sh
viam module upload \
  --version=1.0.0 \
  --platform=linux/amd64 \
  dist/archive.tar.gz
```

Copy

On success, the CLI prints a link to your module in the registry:

```sh
Version successfully uploaded! you can view your changes online here: https://app.viam.com/module/my-org/my-module
```

Copy

Upload for multiple platforms by running the command once per platform:

```sh
viam module upload --version=1.0.0 --platform=linux/amd64 dist/archive-amd64.tar.gz
```

Copy

```sh
viam module upload --version=1.0.0 --platform=linux/arm64 dist/archive-arm64.tar.gz
```

Copy

## Cloud builds

For CI/CD workflows, use cloud builds to compile your module on Viam’s build infrastructure.

Start a cloud build:

```sh
viam module build start --version=1.0.0
```

Copy

Build for multiple platforms in one command:

```sh
viam module build start --version=1.0.0 --platforms=linux/amd64,linux/arm64
```

Copy

Build from a specific git ref:

```sh
viam module build start --version=1.0.0 --ref=main
```

Copy

Build locally to test before pushing:

```sh
viam module build local
```

Copy

List recent builds (the output includes build IDs you need for `build logs`):

```sh
viam module build list
```

Copy

View build logs:

```sh
viam module build logs --id=<build-id>
```

Copy

Wait for a build to complete and stream logs:

```sh
viam module build logs --id=<build-id> --wait
```

Copy

## Download a module

Download a module from the registry for local testing or inspection.
The `--id` flag takes the format `org-namespace:module-name`:

```sh
viam module download \
  --id=my-org:my-sensor-module \
  --version=1.0.0 \
  --platform=linux/amd64 \
  --destination=./downloaded-module
```

Copy

## Create a module

If you need to register a module in the registry before uploading (for example, to reserve a name), use `create`:

```sh
viam module create --name=my-new-module
```

Copy

Most users should use `viam module generate` instead, which handles both creation and scaffolding.

## Convert xacro files to URDF

If your module works with a robot described in [xacro](https://wiki.ros.org/xacro) format (the ROS XML macro language), convert it to URDF with the CLI.
The conversion runs in a Docker container with the specified ROS distribution.

```sh
viam xacro convert \
  --input-file=./robot.xacro \
  --output-file=./robot.urdf
```

Copy

If the xacro file uses `<xacro:arg>` tags, pass the required arguments:

```sh
viam xacro convert \
  --input-file=./robot.xacro \
  --output-file=./robot.urdf \
  --args name:=ur20
```

Copy

To collapse fixed joint chains (useful when the URDF must have a single end-effector):

```sh
viam xacro convert \
  --input-file=./robot.xacro \
  --output-file=./robot.urdf \
  --collapse-fixed-joints
```

Copy

By default, the conversion uses the `osrf/ros:humble-desktop` Docker image.
To use a different ROS distribution or a custom image:

```sh
viam xacro convert \
  --input-file=./robot.xacro \
  --output-file=./robot.urdf \
  --docker-image=osrf/ros:jazzy-desktop
```

Copy

Use `--dry-run` to print the Docker command without running it.
