Skip to main content

Quickstart

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".

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 https://github.com/dagger/hello-dagger
cd hello-dagger

Visualize in Dagger Cloud (optional)

important

This step is optional and will create a Dagger Cloud individual plan account. The individual plan is free of charge for a single user. You will require a GitHub account for account setup and identity verification. If you prefer not to sign up for Dagger Cloud, you can skip this section.

Dagger Cloud is an online visualization tool for Dagger pipelines. It provides a web interface to visualize each step of your pipeline, drill down to detailed logs, understand how long operations took to run, and whether operations were cached.

Create a new Dagger Cloud account by running dagger login:

dagger login

The Dagger CLI will invite you to authenticate your device by displaying a link containing a unique key. Click the link in your browser, and verify that you see the same key in the Dagger Cloud Web interface.

$ dagger login
Browser opened to: https://auth.dagger.cloud/activate?user_code=FCNP-SRLM
Confirmation code: FCNP-SRLM

Once you confirm your authentication code, your Dagger CLI will be authenticated and you will get redirected to your newly created Dagger Cloud organization.

After successfully creating your organization, all future dagger call pipelines can be inspected in Dagger Cloud.

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.

note

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 (
"context"
"fmt"
"math"
"math/rand"

"dagger/hello-dagger/internal/dagger"
)

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("ttl.sh/hello-dagger-%.0f", 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"}).
Directory("./dist")
return dag.Container().From("nginx:1.25-alpine").
WithDirectory("/usr/share/nginx/html", build).
WithExposedPort(80)
}

// 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"}).
Stdout(ctx)
}

// Build a ready-to-use development environment
func (m *HelloDagger) BuildEnv(source *dagger.Directory) *dagger.Container {
nodeCache := dag.CacheVolume("node")
return dag.Container().
From("node:21-slim").
WithDirectory("/src", source).
WithMountedCache("/root/.npm", nodeCache).
WithWorkdir("/src").
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.
IDE SUPPORT

If you're writing the code above in an IDE, you can easily configure your IDE to recognize your Dagger module. This can significantly speed things up, by giving you automatic type-checking, intelligent code completion, and other IDE features when writing Dagger module code.

warning

The Dagger module's class name is automatically generated based on the name of the directory where dagger init was executed. If you cloned the example application into a directory other than the default (hello-dagger), or if you used a different TypeScript application altogether, the auto-generated class name will be different and the code samples above will not work until you update them to use the correct name. Alternatively, you can override Dagger's default class name by specifying a name via the --name argument to dagger init.

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 ttl.sh container registry. Here's what you should see:

Publish

If you signed up for Dagger Cloud, the output of the previous command would have also included a link to visualize the pipeline run on Dagger Cloud. Click the link in your browser to see a complete breakdown of the steps performed by the pipeline. Here's what you should see:

login

This is called a "Trace". It represents a single run of a Daggerized pipeline, and shows a detailed log and output of each step in the pipeline. If there are any errors in the run, Dagger Cloud automatically brings you to the first error in the list.

DID YOU NOTICE...
  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.