Modules
Grafana Cloud

Modules

A Module is a unit of Alloy configuration that combines all other concepts. It contains a mix of configuration blocks, instantiated components, and custom component definitions. The module you pass as an argument to the run command is called the main configuration.

You can import modules to reuse custom components defined by that module.

Import modules

You can import a module to use its custom components in other modules, called importing modules. Import modules from multiple locations using one of the import configuration blocks:

Warning

You can’t import a module that contains top-level blocks other than declare or import.

Modules are imported into a namespace, exposing the top-level custom components of the imported module to the importing module. The label of the import block specifies the namespace of an import. For example, if a configuration contains a block called import.file "my_module", then custom components defined by that module are exposed as my_module.CUSTOM_COMPONENT_NAME. Namespaces for imports must be unique within a given importing module.

If an import namespace matches the name of a built-in component namespace, such as prometheus, the built-in namespace is hidden from the importing module. Only components defined in the imported module are available.

Warning

If you use a label for an import or declare block that matches an existing component, the component is shadowed and becomes unavailable in your configuration. For example, if you use the label import.file "mimir", you can’t use existing components starting with mimir, such as mimir.rules.kubernetes, because the label refers to the imported module.

Example

This example module defines a component to filter out debug-level and info-level log lines:

alloy
declare "log_filter" {
  // argument.write_to is a required argument that specifies where filtered
  // log lines are sent.
  //
  // The value of the argument is retrieved in this file with
  // argument.write_to.value.
  argument "write_to" {
    optional = false
  }

  // loki.process.filter is our component which executes the filtering,
  // passing filtered logs to argument.write_to.value.
  loki.process "filter" {
    // Drop all debug- and info-level logs.
    stage.match {
      selector = `{job!=""} |~ "level=(debug|info)"`
      action   = "drop"
    }

    // Send processed logs to our argument.
    forward_to = argument.write_to.value
  }

  // export.filter_input exports a value to the module consumer.
  export "filter_input" {
    // Expose the receiver of loki.process so the module importer can send
    // logs to our loki.process component.
    value = loki.process.filter.receiver
  }
}

You can save this module to a file called helpers.alloy and import it:

alloy
// Import our helpers.alloy module, exposing its custom components as
// helpers.COMPONENT_NAME.
import.file "helpers" {
  filename = "helpers.alloy"
}

loki.source.file "self" {
  targets = LOG_TARGETS

  // Forward collected logs to the input of our filter.
  forward_to = [helpers.log_filter.default.filter_input]
}

helpers.log_filter "default" {
  // Configure the filter to forward filtered logs to loki.write below.
  write_to = [loki.write.default.receiver]
}

loki.write "default" {
  endpoint {
    url = LOKI_URL
  }
}

Security

Since modules can load arbitrary configurations from potentially remote sources, carefully consider the security of your solution. The best practice is to ensure attackers can’t modify the Alloy configuration. This includes the main Alloy configuration files and modules fetched from remote locations, such as Git repositories or HTTP servers.