Skip to navigation

Modules

A module is a directory of HCL files and assets that you call from another configuration. Each call gets its own variables, locals, and resources. A module sees only the arguments you pass it and the host’s facts; it can’t read its caller’s variables or resources. Its caller can read only the module’s declared outputs. File reads and templates inside a module resolve relative to the module’s own directory.

A reusable file module

Create configuration/modules/file/main.hcl:

modules/file/main.hcl
variable "path" {
type = string
nullable = false
}
variable "text" {
type = string
default = "hello\n"
}
file "config" {
path = var.path
content = var.text
mode = "0644"
}
output "handle" {
type = resource("file")
value = file.config
}

Call it from configuration/main.hcl:

module "message" {
source = "./modules/file"
path = "/etc/rivet-message"
text = "managed by a module\n"
}
output "message_path" {
type = string
value = module.message.handle.path
}

The file’s address is module.message.file.config, but the caller can’t refer to it by that name; only declared outputs are visible. Using the handle output, as message_path does, carries the dependency along, so anything that uses it is ordered after the file.

For a complete checkout fixture with external inputs and multiple instances:

rivet init examples/modules
rivet validate examples/modules --vars-file examples/modules/inputs.yaml
rivet compile examples/modules --vars-file examples/modules/inputs.yaml \
--facts examples/modules/target.json --out modules.bundle
rivet graph modules.bundle --format json

target.json is a generic baseline. Compile against facts from a real host before applying.

Source directories and confinement

A local source is a literal relative path. A module can refer to sibling modules with ../, but can’t reach outside the configuration directory, or outside its package for remote modules. Assets must be inside the module’s own directory. path.module is the module’s directory.

Like the root, a module loads only the *.hcl files directly in its directory.

Ordering boundaries

Module metadata supports depends_on, before, noop, tags, literal description, and notify.

  • depends_on gates every descendant behind the module’s entry barrier.
  • before orders later work after the module’s completion barrier.
  • A whole-module dependency waits for all descendants. A dependency on an exported resource handle waits for that resource and its prerequisites.
  • Ordering works the same for nested and empty modules. Module boundaries are ordering points only; they don’t do anything on the host.
  • Module noop inherits with logical OR; children cannot turn it off. Tags inherit by set union.

Resources can also depend on or precede whole modules. graph --format json retains resource order/edges and adds all nodes and complete graph module_edges.

Module notifications

A module may declare meta { notify = [service.app] }; a service or refresh-only exec may subscribe to a module. Every verified descendant change creates durable causal work before mutation. Delivery coalesces per target and waits for successful module completion, including required descendant refresh work. Partial failure preserves obligations for a later successful run. Noop creates no obligations; interrupted delivery remains at least once.

Whole modules cannot be refresh targets. Module subscribe, timeout, and sensitive metadata are unsupported. Notifying a descendant of the same source module creates a completion cycle; use internal resource notifications instead.

Limits

All loaded HCL can total 16 MiB. Modules can nest 32 deep, and a configuration can have at most 1,024 module instances and 10,000 resources. Errors such as missing arguments, references to private resources, ownership conflicts, and cycles are reported before any asset is downloaded.