Skip to main content


Daggerize an example application

The best way to understand how Dagger works is by creating a delivery pipeline using Dagger Functions - a process we call "Daggerizing".

  1. Choose a Dagger SDK and bootstrap a new Dagger module for your application's pipeline with dagger init.
  2. Construct the pipeline by creating and combining one or more Dagger Functions to produce the desired results. Your Dagger Functions can use the core Dagger API and/or call Dagger Functions from third-party Daggerverse modules.
  3. Use dagger call to run and test your pipeline locally. Once you're satisfied, transfer your Dagger module and your dagger call commands to your CI configuration.

Get the example application

The example application is a skeleton Vue framework application that returns a "Hello from Dagger!" welcome page. Clone its repository and set it as the current working directory:

git clone
cd hello-dagger

Initialize a Dagger module

Bootstrap a new Dagger module in Go, Python or TypeScript by running dagger init in the application's root directory, using the --source flag to specify a directory for the module's source code.

dagger init --sdk=go --source=./dagger

This will generate a dagger.json module metadata file, an initial dagger/main.go source code template, as well as a dagger/dagger.gen.go file and dagger/internal/ directory.

Construct a pipeline using Dagger Functions

Dagger Functions are regular code, written in Go, Python or TypeScript using the corresponding Dagger SDK. They consist of a series of method/function calls, such as "pull a container image", "copy a file", "forward a TCP port", and so on, which can be chained together.


Don't worry about how the Dagger Functions shown below work for the moment - it's explained in detail in the next sections!

Replace the generated dagger/main.go file with the following code, which adds four Dagger Functions to your Dagger module:

package main

import (


type HelloDagger struct{}

// Publish the application container after building and testing it on-the-fly
func (m *HelloDagger) Publish(ctx context.Context, source *dagger.Directory) (string, error) {
_, err := m.Test(ctx, source)
if err != nil {
return "", err
return m.Build(source).
Publish(ctx, fmt.Sprintf("", math.Floor(rand.Float64()*10000000))) //#nosec

// Build the application container
func (m *HelloDagger) Build(source *dagger.Directory) *dagger.Container {
build := m.BuildEnv(source).
WithExec([]string{"npm", "run", "build"}).
return dag.Container().From("nginx:1.25-alpine").
WithDirectory("/usr/share/nginx/html", build).

// Return the result of running unit tests
func (m *HelloDagger) Test(ctx context.Context, source *dagger.Directory) (string, error) {
return m.BuildEnv(source).
WithExec([]string{"npm", "run", "test:unit", "run"}).

// Build a ready-to-use development environment
func (m *HelloDagger) BuildEnv(source *dagger.Directory) *dagger.Container {
nodeCache := dag.CacheVolume("node")
return dag.Container().
WithDirectory("/src", source).
WithMountedCache("/src/node_modules", nodeCache).
WithExec([]string{"npm", "install"})

In this Dagger module, each Dagger Function performs a different operation:

  • The Publish() Dagger Function tests, builds and publishes a container image of the application to a registry.
  • The Test() Dagger Function runs the application's unit tests and returns the results.
  • The Build() Dagger Function performs a multi-stage build and returns a final container image with the production-ready application and an NGINX Web server to host and serve it.
  • The BuildEnv() Dagger Function creates a container with the build environment for the application.

Run the pipeline

Call a Dagger Function to run the pipeline:

dagger call publish --source=.

This single command runs the application's tests, then builds and publishes it as a container image to the container registry. Here's what you should see:


  1. Even though you just tested, built and published a Node.js application, you didn't need to install any dependencies like node or npm on your local machine. You only needed the Dagger CLI and the ability to run containers. This is a very powerful feature that eliminates all the variability and dependencies related to the host environment and/or configuration.
  2. Subsequent calls to dagger call publish... are significantly faster than the first run (try it!). Dagger caches every operation by default and automatically generates a Directed Acyclic Graph (DAG) to run your pipeline steps concurrently to maximize pipeline speed and accuracy.