Tutorial  on  CI/CD

Running Dagger Pipelines on GitHub Actions

You've successfully "daggerized" your project, and it runs perfectly on your machine. The next step is to get your pipeline running on GitHub Actions.

While the initial steps are straightforward, there are a few considerations that can affect the structure and performance of your pipeline.

In this tutorial, we'll dive into these in detail.

💡 You can follow this tutorial on your own machine or use the provided online playground.

Preparations

If you’re running the tutorial locally, we recommend installing the GitHub CLI first.

Copy the template repository

This tutorial comes with a demo repository that includes a basic Dagger pipeline.

Throughout this tutorial you will migrate the existing GitHub Actions workflow to run Dagger pipelines.

To get started, create a new repository named tutorial-dagger-gha from this template.

Using GitHub CLI (on your machine)
gh repo create --private \
  --template sagikazarmark/template-tutorial-dagger-gha \
  tutorial-dagger-gha

Authenticate with GitHub

This is only required when using the online playground.

Create a new Fine-grained access token with the following parameters:

  • Repository access: Only select repositories
  • Select your tutorial-dagger-gha repository
  • Repository permissions
    • Contents: Read and write
    • Workflows: Read and write
Hint

Check out this tutorial for a more detailed guide.


Finally, clone the repository:

gh repo clone tutorial-dagger-gha ~/tutorial

Examine the tutorial repository

Enter the tutorial directory and list the available Dagger functions:

cd ~/tutorial
dagger functions

Feel free to explore or run the functions.

Next, go to the IDE tab and open the tutorial/.github/workflows/ci.yaml file. Examine the jobs in the GitHub Actions workflow.

💡 You can always compare your changes with the tutorial/.github/workflows/ci-dagger.yaml file that serves as a reference.

Running Dagger on GitHub Actions

As a first step, update the tutorial/.github/workflows/ci.yaml file and replace the Go test steps in the test job with a Dagger pipeline call:

      - name: Run pipeline
        uses: dagger/dagger-for-github@main
        with:
          version: latest
          verb: call
          args: test

⚠️ In production, avoid floating tags—pin both the GitHub Action and Dagger to explicit version tags instead of using main or latest.

Hint 1 💡
-     - name: Set up Go
-       uses: actions/setup-go@v5
-
-     - name: Test
-       run: go test -race -v -shuffle=on ./...
+     - name: Run pipeline
+       uses: dagger/dagger-for-github@main
+       with:
+         version: latest
+         verb: call
+         args: test

Do the same with the lint job.

Hint 2 💡
-     - name: Set up Go
-       uses: actions/setup-go@v5
-
-     - name: Lint
-       uses: golangci/golangci-lint-action@v7
+     - name: Run pipeline
+       uses: dagger/dagger-for-github@main
+       with:
+         version: latest
+         verb: call
+         args: lint

Commit and push the changes, then check out the Actions tab of your repository:

git commit -am "Add Dagger pipeline"
git push

Pipeline or Pipelines?

In the previous section, you replaced existing workflow steps with calls to Dagger functions while preserving the overall workflow structure (i.e., jobs).

Although this is a solid first step toward adopting Dagger, you can often simplify your workflows further by consolidating multiple functions into a single pipeline.

A key advantage of Dagger is that it organizes steps into a directed acyclic graph (DAG), allowing most independent steps to run in parallel. GitHub Actions can run jobs in parallel but not individual steps, which often leads us to split workflows into multiple jobs to optimize performance.

In many cases, however, you can achieve similar performance by merging several functions into one pipeline. This also makes local execution easier, since you no longer need to invoke each function separately.

The demo repository includes a check function that orchestrates all of the other functions.

In your ci.yaml, add a new check job that invokes this check function.

Hint 3 💡
  check:
    name: Check
    runs-on: ubuntu-latest

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Run pipeline
        uses: dagger/dagger-for-github@main
        with:
          version: latest
          verb: call
          args: check

Commit and push the changes, then check out the Actions tab of your repository:

git commit -am "Add a new job running the check function"
git push

The caveat of this approach is that you lose the ability to view individual steps in pull requests. If something fails, you'll have to dig into the logs to find out what went wrong. A potential solution is to use Dagger Cloud traces to drill down into the failed step in the pipeline.

tl;dr Experiment with consolidating separate functions into a single pipeline. If the performance is comparable, consider replacing the separate function calls.

To checkout or not to checkout?

A common question is whether to rely on actions/checkout to clone your repository or let Dagger do it via its built-in mechanism. The answer depends on several factors.

As a general rule, keeping your pipelines self‑contained is a good practice. When Dagger pulls the source, the workflow is no longer coupled to GitHub Actions, so it’s easier to port to other CI systems—and your Actions YAML stays leaner.

Practically, you can have Dagger clone the repository by applying the defaultPath pragma to a constructor function parameter that loads your source directory:

  • When the module runs locally, the source directory is loaded from the filesystem.
  • When the module runs remotely, the source directory is loaded from the repository root.

Below is the module constructor from the demo repository:

func New(
    // Project source directory.
    //
    // +defaultPath="/"
    source *dagger.Directory,
) *Tutorial {
    return &Tutorial{
        Source: source,
    }
}

Alternatively, you can use Dagger's Git feature to clone a repository, but using the defaultPath pragma is recommended for most cases.

Add a new without_checkout job to your workflow file, pass the module and ref to Dagger (you can use the check job as example, but remove any actions/checkout steps):

    steps:
      - name: Run pipeline
        uses: dagger/dagger-for-github@main
        with:
          module: github.com/${{ github.repository }}@${{ github.ref }}
          verb: call
          args: ...
Hint 4 💡
  without_checkout:
    name: Without Checkout
    runs-on: ubuntu-latest

    steps:
      - name: Run pipeline
        uses: dagger/dagger-for-github@main
        with:
          version: latest
          module: github.com/${{ github.repository }}@${{ github.ref }}
          verb: call
          args: check

Commit and push the changes, then check out the Actions tab of your repository:

git commit -am "Checkout repository using Dagger"
git push

Sometimes, however, the platform’s native checkout is more convenient. For example, cloning private repositories is often easier with GitHub’s built‑in authentication. Likewise, if you plan to use Dagger Cloud, the CLI reads source information from the local repository (that actions/checkout populates), helping it map traces to the correct commit or pull request.

tl;dr If you use Dagger in private repositories, or plan to use Dagger Cloud, stick with actions/checkout.

Using secrets

Dagger provides first‑class support for secrets, so you can use sensitive data (API keys, passwords, etc.) securely in your pipelines.

You can source secrets from different providers:

  • from the host
    • environment variables
    • files
    • command execution
  • external managers (e.g., 1Password, Vault)

You can then inject them into your pipeline as secret arguments.

GitHub Actions also offers a way to manage secrets: store your secrets securely in the repository settings, expose them to your workflow via environment variables, and then pass them into Dagger as arguments.

Here is an example:

  check:
    name: Check
    runs-on: ubuntu-latest

    steps:
      # checkout

      - name: Run pipeline
        uses: dagger/dagger-for-github@main
        with:
          version: latest
          verb: call
          args: check --github-token GITHUB_TOKEN
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Feel free to experiment with injecting a secret into the pipeline!

Hint 5 💡

You can quickly create a secret in GitHub with the following CLI command:

gh secret set MYSECRET --body "VALUE" [--repo <owner/repo>]

Integrate with Dagger Cloud

Dagger Cloud gives you rich, interactive pipeline visualizations—complete with detailed traces—so you can pinpoint failures and troubleshoot performance bottlenecks in seconds. Its clean, graphical interface is far easier to navigate (and prettier to look at) than going through raw GitHub Actions logs.

Dagger Cloud is free for individuals (at the moment), you can sign up for a free account here.

After completing the setup, you can get an API token and add it to your repository as a secret.

💡 Be sure to check out the documentation on configuring Dagger Cloud.

From there, it's only a matter of passing the token into the Dagger action:

  check:
    name: Check
    runs-on: ubuntu-latest

    steps:
      # checkout

      - name: Run pipeline
        uses: dagger/dagger-for-github@main
        with:
          version: latest
          verb: call
          args: check
          cloud-token: ${{ secrets.DAGGER_CLOUD_TOKEN }}

Improve build times by better caching

One hurdle with Dagger on GitHub Actions is caching: GitHub’s hosted runners are ephemeral, so anything you cache won’t survive between workflow runs unless you’re on self‑hosted runners. While the Dagger team is actively enhancing built‑in caching, you’ll need a stopgap to speed up your builds today.

Enter Depot. Depot’s first‑class Dagger integration persists caches across runs, often yielding dramatic reductions in build times.

💡 Check out this case study from OpenMeter, where they sped up their CI pipeline by 5× and cut costs in half.

Depot offers a 7-day free trial to get started which is ample time to try their features.

First, sign up for Dagger Cloud (required to use the special Dagger runners in Depot).

Then sign up for Depot and follow the instructions here to connect Depot with your GitHub account and Dagger Cloud.

⚠️ Depot does NOT work with personal GitHub accounts. You must use a GitHub organization account.

Finally, update your workflow to use the Dagger runner:

  check:
    name: Check
    runs-on: depot-ubuntu-latest,dagger=0.18.3

    steps:
      # checkout

      - name: Run pipeline
        run: dagger call check

Notice that you need pin the Dagger version via the runs-on field and since the runner comes with the Dagger binary preinstalled, you can drop the Dagger Action and invoke the CLI directly.

For I/O‑intensive pipelines with cacheable steps, you should see a significant reduction in average build times.

💡 Read more about Depot and Dagger here.

Summary

In this tutorial, you learned how to use Dagger in GitHub Actions, including how to use secrets and how to integrate with Dagger Cloud.

You also learned how running Dagger pipelines on GitHub Actions can affect the structure of your pipelines.

Discussion:  Discord
Categories: CI/CD

Level up your Server Side game — Join 10,000 engineers who receive insightful learning materials straight to their inbox