> ## Documentation Index
> Fetch the complete documentation index at: https://ravion-b90c0359-siddharth-eng-4903.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Module builds

> Build a Ravion module's code — container images, static assets, or Lambda packages — with a one-line pipeline step, and feed the artifact to a deploy.

Modules with the build capability know how to build their own code. The module's definition carries the full build config — source repo, builder, and artifact destinations — so you build it from a pipeline with a one-line `build` step instead of copying that config into every pipeline.

Depending on the module, a build produces a **container image** (pushed to ECR), a **directory of static assets** (uploaded to S3), or a **Lambda zip package** (uploaded to S3).

<Note>
  **Builds only run through pipelines.** There is no standalone build command or dashboard button.
  You either add a `build` step to a Ravion pipeline, or [build in an external CI
  system](#building-outside-ravion) and hand Ravion the finished artifact at deploy time.
</Note>

## Build config is part of the module config

You configure a module's build the same way you configure everything else about it: through the module instance's inputs, alongside its runtime config. There is no separate build file. For example, the `web-app` module in a [project config file](/config-as-code/project-config-file) sets its build inputs next to its port and health check:

```yaml theme={null}
- givenId: web-app
  type: rvn-ecs-web
  version: 0.5.0
  input:
    # build config
    build_source: dockerfile
    source_repo: my-org/my-repo
    dockerfile: Dockerfile
    # runtime config
    container_port: 80
    health_check_path: /
```

## Running a build

Add a `build` step to your pipeline and point it at the module instance:

```yaml theme={null}
- id: build_web_app
  type: build
  module_instance: << pipeline.variant.id >>.web-app
  input:
    image_tag: << pipeline.input.commit >>
```

* `module_instance` accepts a unique ID (`mi_…`), `environment.module`, or `project.environment.module`.
* `input` passes build-time values the module accepts, such as a tag, branch, or commit. Check the module's docs or schema (`ravion module schema <module-type>`) for what it takes.

Builds run on temporary EC2 instances in your cloud account, so your source code never enters the Ravion control plane. Image builds reuse [Docker layer cache](#docker-layer-cache) from the destination registry, so repeat builds are fast.

<Note>
  Building something that isn't a module? Use the standalone `build:image` or `build:static` [CI
  steps](/pipelines/step-types#ci-steps), which take the full build config inline.
</Note>

## Docker layer cache

Container image builds reuse Docker layer cache from the module's ECR repo, so repeat builds only rebuild the layers that changed. There is nothing to configure: the module definition points the build at a stable cache image in that repo, and every build also writes a dedicated `buildcache` image holding the layers of each build stage.

Static asset builds and Lambda builds — zip package or container image — don't use registry layer cache.

To get the most out of the cache:

* **Order your `Dockerfile` from least- to most-frequently-changed.** Copy manifest files (`package.json`, `go.mod`, `requirements.txt`) and install dependencies *before* copying the rest of the source. That way a source change doesn't invalidate the dependency layer.
* **Pin base images by digest** (or a specific tag) so the base layer is stable across builds.
* **Keep the build in one ECR repo.** Cache comes from the repo the build pushes to, so a module that pushes elsewhere between builds loses reuse.

### Choosing the cache source yourself

Module builds pin the cache source for you and expose no input for changing it. If you need control over it, use a standalone [`build:image` step](/pipelines/step-types#build%3Aimage) and set `cache_from` on its `builder` block:

```yaml theme={null}
- id: build_image
  type: build:image
  source:
    type: git
    repo: https://github.com/my-org/my-repo
    branch: << pipeline.input.branch >>
  builder:
    type: dockerfile
    dockerfile: Dockerfile
    cache_from:
      tag: release    # or: digest: sha256:...
  destinations:
    - id: ecr
      type: ecr
      repository_arn: arn:aws:ecr:us-east-1:123456789012:repository/my-app
  infrastructure:
    type: ec2
    instance_size: medium
```

Both `tag` and `digest` are optional, accept template tokens, and resolve against the step's first ECR destination — so the tag or digest you name has to exist in that repo. `digest` wins if you set both. Omit `cache_from` entirely and the step falls back to the most recent image in that repo. See [`CacheFromConfig`](/pipelines/config-reference#cachefromconfig) for the full field list.

## Inspecting builds

A build is a pipeline step execution — there's no separate build entity or command. Find it in the pipeline run:

```bash theme={null}
ravion pipeline run get <pipeline-run-id>
ravion logs pipeline-step <step-execution-id>
```

## Feeding the artifact to a deploy

Every build step outputs **`build_ref`** — the canonical reference to the artifact it produced:

| Build kind | `build_ref` value                  |
| ---------- | ---------------------------------- |
| Image      | Digest-pinned image URI            |
| Static     | S3 directory containing the assets |
| Lambda     | S3 key of the uploaded zip         |

Pass it to the [deploy step](/modules/deploy):

```yaml theme={null}
- id: deploy_web_app
  type: deploy
  module_instance: << pipeline.variant.id >>.web-app
  input:
    image_ref: << steps.build_web_app.output.build_ref >>
```

This is the whole build-to-deploy contract: the build step produces a pinned artifact reference, and the deploy step releases exactly that artifact. Type-specific outputs are also available when you need them — `image_uri`, `image_digest`, `s3_bucket`, `s3_directory`, `s3_key`, and more.

## Building outside Ravion

You don't have to build in Ravion at all. Module definitions typically include a prebuilt image or package option for teams that build in GitHub Actions, CircleCI, or another external system. For example, `rvn-ecs-web` supports these `build_source` values:

| Build source              | What you configure on the module                                                                    | What the deploy takes as `image_ref` |
| ------------------------- | --------------------------------------------------------------------------------------------------- | ------------------------------------ |
| `dockerfile` / `railpack` | Git repo and build settings — Ravion builds and pushes to the module's ECR repo                     | Digest from the build step           |
| `image_registry`          | The image repository (Docker Hub, GHCR, ECR, …) without a tag, plus registry credentials if private | The tag or digest to deploy          |

So with an external build, the flow is: your CI builds and pushes the image, then triggers the release with the artifact reference:

```bash theme={null}
ravion deploy create --module-instance-id <mi-id> \
  --description "Release $GITHUB_SHA" \
  --inputs '{"image_ref": "'"$IMAGE_TAG"'"}'
```

Ravion still handles the rollout, deploy history, events, and rollback — it just doesn't do the building.

## For module authors

How a definition declares its build config — builders (`dockerfile`, `railpack`), destinations, build inputs, and templating — is covered in the [module definition schema](/module-definitions/definition-schema).
