Accelerate Your Development and CI/CD Pipelines with Dagger

Márk Sági-Kazár


2025-10-09 @ KCD Warsaw

whoami

Márk Sági-Kazár

Cloud Native Ambassador

Dagger Commander


YAML engineer for life 😭



@sagikazarmark

https://sagikazarmark.com

hello@sagikazarmark.com

CI/CD is broken… 🔥

…and we don’t care anymore

🔀 Environment drift

Developer environment

brew install go
nix-env -i gotestsum
sudo apt-get install golangci-lint

make build
just test
task lint

What version of Go did you just install?

CI/CD environment

- uses: actions/setup-go@v5
  with:
    go-version: "1.24.0"

- run: go build .
- run: go test -race -v ./…

- uses: golangci/golangci-lint-action@v8
  with:
    version: "v2.1.0"

Artifacts

FROM golang:1.23 AS go

RUN go build -o app .


FROM alpine

COPY --from=go /app /usr/local/bin/

Why is this a problem?

  • Maintenance burden
  • Hidden bugs
  • Undermines trust in processes 1
  • Onboarding friction

💡 Portable environments

Dev + CI/CD + Artifacts = software delivery pipelines/workflows

I wish there was…

Is docker run the answer?

It gives you containers…

…but not workflows.

🙏 Push’n’Pray

💡 Portable workflows

Run the same workflow:

  • in dev
  • in CI
  • anywhere

✍️ YAML

Day 1…

name: CI

on:
  push:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - run: |
          build
          test
          lint

Day 101

700+ lines of YAML & Bash chaos

# Foobar pipeline
# Include the Common CI pipeline parameters.
include:
  - project: 'foo/bar/foobarproject/cicdtemplate'
    file: '/Common.gitlab-ci.yml'
  #- /force-app/ui-tests/pipeline.yml

stages:
  - build-metadata-package
  - run-js-tests
  - validate-package
  - deploy-package
  - run-unit-tests
  - run-api-tests
  - run-ui-tests
  - integration

####################################################
# Builds the Metadata Package, builds the Package
# files and Destructive changes
####################################################
build-metadata-package:
  stage: build-metadata-package
  except:
    ...

🤔 How did we end up in this mess?

  • YAML is a data serialization language
  • Workflows are not strictly declarative

YAML…

  • is not reusable 1
  • is not composable
  • provides poor control flow
  • is not portable (vendor lock-in)

YAML is a poor choice of language for software delivery workflows.

How about using a real programming language?

How about using a programming language you already know?

Example (pseudo code)

func build() Binary {
    return go.Build(".")
}

func push() {
    ctr := newContainer().
        copy("/app", build())

    pushContainer(ctr)
}

🤖 AI

How can AI help?

  • Fix tests and open PRs
  • Find vulnerable dependencies and update them
  • Make sure your code is in line with coding standards

But how?

  • GitHub Actions?
  • Custom scripts?

💡 AI Native workflows

  • Integrate AI into your workflow code
  • Once again: portable workflows are king

👍 Ideal solution

🔀 Environment drift

🙏 Push’n’Pray

✍️ YAML

🤖 AI

→ Portable environments

→ Portable workflows

→ Programmable workflows

→ AI Native workflows

Dagger Functions

  • Write your workflows as code…
  • …in a programming language you already know
  • Orchestrate containers
  • Run them anywhere 1
  • Use them as AI agents

Dagger Function Example

func (m *Module) Build() *dagger.File {
    return dag.Container().
        From("golang").
        WithExec([]string{"go", "build", "-o", "app", "."}).
        File("/app")
}
@function
def build(self) -> dagger.Container:
    return dag.container().
        from("golang").
        with_exec(["go", "build", "-o", "app", "."]).
        file("/app")
  @func()
  build(): Container {
    return dag.container().
      from("golang").
      withExec(["go", "build", "-o", "app", "."]).
      file("/app")
  }

Dagger AI Example

func (m *Module) Test() error {
    err := m.runTests()
    if err != nil {
        env := dag.Env().
            WithStringInput("error", err.Error(), "error message for the test")
            WithDirectoryInput("source", source, "source code").
            WithDirectoryOutput("fixed", "fixed code")

        work := dag.LLM().
            WithEnv(env).
            WithPrompt("My tests failed with $error. Fix them!")

        // Contains the fixed code
        work.Env().Output("fixed").Directory()
    }

    return nil
}

More features

  • Reusable modules
  • Caching
  • Integration with secret providers
  • Tracing

Demo

🔭 Dagger in the Wild

github.com/openmeterio/openmeter

github.com/twirphp/twirp

github.com/sagikazarmark/daggerverse

Any questions?

👋 Thank you

Stay in touch!



@sagikazarmark

https://sagikazarmark.com

hello@sagikazarmark.com

🎮 Game

Guess which image is AI generated.

AI generated

Original

AI generated

AI generated

Original

AI generated

Original

Human generated

🎮 Thanks for playing!