Skip to main content

Module Structure

It's essential to understand a few key concepts about Dagger modules, for a better fit with your normal development workflow.

Runtime container

Dagger modules run in a runtime container that's bootstrapped by the Dagger Engine, with the necessary environment to run the Dagger module's code.

The runtime container is currently hardcoded to run in Go 1.21 (although this may be configurable in future).

Module layout

A new Dagger module is initialized by calling dagger init. This creates a new dagger.json configuration file in the current working directory. The configuration file will default the name of the module to the current directory name, unless an alternative is specified with the --name argument.

Once a module is initialized, dagger develop --sdk=... sets up or updates all the resources needed to develop the module locally. By default, the module source code will be stored in the current working directory, unless an alternative is specified with the --source argument.

info

At any point, running dagger develop regenerates the module's code based on dependencies and the current state of the module.

The template from dagger develop creates the following structure:

.
├── LICENSE
├── dagger
│ ├── dagger.gen.go
│ ├── go.mod
│ ├── go.sum
│ ├── internal
│ │ ├── dagger
│ │ ├── querybuilder
│ │ └── telemetry
│ └── main.go
└── dagger.json

In this structure:

  • dagger.json is the Dagger module configuration file.
  • go.mod/go.sum manage the Go module and its dependencies.
  • main.go is where your Dagger module code goes.
  • internal contains automatically-generated types and helpers needed to configure and run the module:
    • dagger contains definitions for the Dagger API that's tied to the currently running Dagger Engine container.
    • querybuilder has utilities for building GraphQL queries (used internally by the dagger package).
    • telemetry has utilities for sending Dagger Engine telemetry.
note

While you can use the utilities defined in the automatically-generated code above, you cannot edit these files. Even if you edit them locally, any changes will not be persisted when you run the module.

Multiple files

You can split your Dagger module into multiple files, not just main.go. To do this, you can just create another file beside main.go (for example, utils.go):

.
├── dagger
│ ├── ...
| ├── internal
│ └── ...
│ ├── main.go
│ └── utils.go
└── dagger.json

This file should be inside the same package as main.go, and as such, can access any private variables/functions/types inside the package.

Additionally, you can also split your Dagger module into Go subpackages (for example, utils):

.
├── dagger
│ ├── ...
| ├── internal
│ └── ...
│ ├── main.go
| └── utils
│ └── utils.go
└── dagger.json

Because this is a separate package, you can only use the variables/functions/types that are exported from this package in main.go (you can't access types from main.go in the utils package).

note

Only types and functions in the top-level package are part of the public-facing API for the module.

You can access other Dagger types from a sub-package by importing the generated sub-package dagger/<module>/internal/dagger:

// utils/utils.go

import "dagger/<module>/internal/dagger"

func DoThing(client *dagger.Client) *dagger.Directory {
// we need to pass *dagger.Client in here, since we don't have access to `dag`
...
}

Language-native packaging

The structure of a Dagger module mimics that of each language's conventional packaging mechanisms and tools.

Dagger modules written for use with the Go SDK are automatically created as Go modules. At module creation time, a go.mod and go.sum file will automatically be created that import the necessary dependencies. Dependencies can be installed and managed just as for any standard Go environment.

After using new dependencies in your code, update your go.mod/go.sum with the newly imported dependencies by using go mod tidy.

Go workspaces

Since it's common to have a sub-directory inside your main project containing your Dagger module code, you can manage your modules using Go workspaces.

When a new Dagger module is created, Dagger attempts to add it to a root go.work if it exists. If not, it can be added manually later with go work use ./path/to/mymodule.

// go.work
go 1.21.7

use (
./path/to/mymodule
)

Go vendor

Go vendor directories are not currently supported. See https://github.com/dagger/dagger/issues/6076 for more information.