← Back to posts
comparison guide self-hosted · · 9 min read

Dagger Self-Hosted CI/CD Pipeline Engine: Portable DevOps Guide 2026

Complete guide to self-hosting Dagger, the portable CI/CD pipeline engine. Learn how to run Dagger locally and on your own infrastructure with Docker, CUE configuration, and real-world pipeline examples.

OS
Editorial Team

Continuous integration and continuous deployment pipelines are the backbone of modern software development. Every code change needs to be built, tested, scanned, and deployed reliably. The traditional approach ties your pipeline logic to a specific platform — Jenkins, GitHub Actions, GitLab CI — meaning your build scripts are locked into that provider’s syntax, runtime, and ecosystem.

Dagger takes a fundamentally different approach. It is an open-source engine that lets you define your CI/CD pipelines as portable programs that run anywhere: on your local laptop, inside a self-hosted server, or as a step inside any existing CI platform. This guide walks you through self-hosting Dagger, writing your first pipelines, and understanding when this portable paradigm makes sense for your infrastructure.

Why Self-Host Your CI/CD Pipeline Engine

Running your own pipeline infrastructure offers advantages that go beyond simple cost savings:

No build minute limits or queue times. Commercial CI platforms throttle concurrent jobs, cap monthly build minutes, and charge premium rates for parallel runners. A self-hosted Dagger engine on a dedicated server runs as many pipelines as your hardware can handle, with no artificial constraints.

Full pipeline portability. Dagger pipelines are defined as executable programs, not YAML declarations bound to a specific platform. The same pipeline runs identically on a developer’s laptop, a staging server, and your production CI infrastructure. You can test pipeline changes locally before pushing them — something that is nearly impossible with traditional platform-specific CI configs.

Data sovereignty and compliance. Build artifacts, test results, and deployment logs never leave your infrastructure. For organizations handling regulated data, this eliminates the risk of third-party CI platforms storing sensitive build outputs or leaking metadata about your codebase.

Reduced CI/CD vendor lock-in. If you define your pipelines in Dagger and your CI platform goes down, changes pricing, or introduces breaking changes, you are not stranded. Dagger runs independently — you can point it at a new CI provider, a bare-metal server, or a Kubernetes cluster with zero pipeline rewrites.

Cost efficiency at scale. Managed CI services charge per build minute, with rates climbing as you add concurrency. A self-hosted server running Dagger costs a flat monthly rate regardless of how many pipelines execute. For teams running hundreds of builds daily, this difference is significant.

What Makes Dagger Different

Most CI/CD tools define pipelines as static configuration files. Jenkins uses Groovy-based Jenkinsfiles. GitHub Actions uses YAML workflows. GitLab CI uses .gitlab-ci.yml. Each is interpreted by a specific platform with its own runner architecture, environment assumptions, and plugin ecosystem.

Dagger treats pipelines as programs. You write pipeline logic in familiar languages (Go, Python, TypeScript, or CUE) using the Dagger SDK. The engine executes your code inside containers, giving you deterministic, reproducible builds by default. The key architectural differences:

FeatureDaggerJenkinsGitHub ActionsTekton
Pipeline formatPrograms (Go, Python, TS, CUE)Groovy (Jenkinsfile)YAML workflowsYAML CRDs
ExecutionContainer-nativeAgent-basedCloud runnersKubernetes pods
Local testingNative (dagger run)LimitedVia act (imperfect)Via tkn (complex)
PortabilityRun anywhereJenkins-specificGitHub-specificKubernetes-only
CachingAutomatic content-addressedManual configLimitedManual volumes
ParallelismBuilt-in (concurrent modules)Parallel stagesMatrix strategyParallel tasks
LicenseApache 2.0MIT / BSLProprietaryApache 2.0

Dagger’s content-addressed caching system is particularly powerful. Every file, directory, and container image is identified by a cryptographic hash. If nothing has changed since the last run, Dagger reuses cached results instantly — no manual cache key configuration required.

Installing Dagger on Your Server

Dagger is distributed as a single binary. Installation is straightforward on any Linux server:

1
2
3
4
5
6
7
8
9
# Download the latest release
curl -L https://dl.dagger.io/dagger/install.sh | sh

# Verify installation
dagger version
# Output: dagger 0.19.x (linux/amd64)

# Move to system path
sudo mv bin/dagger /usr/local/bin/

Dagger requires a container runtime. Docker is the default, but Podman and containerd are also supported. For a self-hosted production server, Docker is recommended due to its stability and broad compatibility.

Installing Docker

1
2
3
4
5
6
7
# Install Docker (Debian/Ubuntu)
curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker $USER
newgrp docker

# Verify
docker run --rm hello-world

Writing Your First Dagger Pipeline

Here is a complete example of a Dagger pipeline written in CUE that builds, tests, and deploys a Go application:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package main

import (
    "dagger.io/dagger"
    "dagger.io/dagger/core"
)

dagger.#Plan & {
    // Source: pull from git
    client: filesystem: ".": read: contents: _

    // Build container
    build: core.#Run & {
        input: core.#Scratch & {
            source: client.filesystem.".".read.contents
            image: "golang:1.23-alpine"
        }
        script: """
            cd /src
            go mod download
            CGO_ENABLED=0 go build -o /bin/app ./cmd/server
        """
        mounts: [
            {source: client.filesystem.".".read.contents, destination: "/src"}
        ]
    }

    // Test: run the test suite
    test: core.#Run & {
        input: build.output
        script: "cd /src && go test -v ./..."
        never: false
    }

    // Deploy: push container image
    deploy: core.#Push & {
        input: build.output
        address: "registry.example.com/myapp:latest"
    }
}

Run this pipeline locally to verify it works before deploying:

1
dagger run .

The dagger run command executes the pipeline on your local machine, showing real-time logs. If it passes locally, it will pass identically on your CI server — no “works on my machine” surprises.

Self-Hosting Dagger with Docker Compose

For a production self-hosted setup, you can run Dagger’s engine alongside a container registry for storing build artifacts. Here is a Docker Compose configuration:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
services:
  dagger-engine:
    image: registry.dagger.io/engine:v0.19.0
    privileged: true
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - dagger-cache:/var/lib/dagger
    environment:
      - _EXPERIMENTAL_DAGGER_RUNNER_HOST=docker-container://dagger-engine
    restart: unless-stopped
    networks:
      - cicd

  registry:
    image: registry:2
    volumes:
      - registry-data:/var/lib/registry
    ports:
      - "5000:5000"
    restart: unless-stopped
    networks:
      - cicd

  # Optional: Dagger Cloud UI for pipeline visualization
  dagger-cloud:
    image: dagger/dagger-cloud:latest
    ports:
      - "8080:8080"
    environment:
      - DAGGER_CLOUD_TOKEN=your-token-here
    restart: unless-stopped
    networks:
      - cicd

volumes:
  dagger-cache:
  registry-data:

networks:
  cicd:
    driver: bridge

Deploy this stack on any server with Docker:

1
2
3
4
5
6
7
mkdir -p ~/dagger-cicd
cd ~/dagger-cicd
# Save the compose file above as docker-compose.yml
docker compose up -d

# Verify the engine is running
docker ps | grep dagger

The local registry at localhost:5000 stores all container images built by your pipelines. This gives you a complete, self-contained CI/CD infrastructure with no external dependencies.

Real-World Pipeline Examples

Multi-Stage Build with Linting and Testing

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// Build with lint, test, and security scan stages
dagger.#Plan & {
    client: filesystem: ".": read: contents: _

    base: core.#Image & {
        source: "node:22-alpine"
        source: client.filesystem.".".read.contents
    }

    lint: core.#Exec & {
        input: base
        script: """
            npm ci
            npm run lint
            npm run typecheck
        """
    }

    test: core.#Exec & {
        input: lint.output
        script: "npm run test -- --coverage"
    }

    build: core.#Exec & {
        input: test.output
        script: "npm run build"
    }
}

Python Application with Database Integration Tests

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#!/bin/bash
# Run a Dagger pipeline that spins up a PostgreSQL container for integration tests
dagger run -e DATABASE_URL=postgresql://test:test@postgres:5432/testdb \
    python -c "
import dagger

async def main():
    client = await dagger.Connection()
    src = client.host().directory('.')
    db = client.container().from_('postgres:17-alpine').with_env_var('POSTGRES_PASSWORD', 'test')
    test = client.container().from_('python:3.13-slim').with_directory('/app', src).with_mounted_service('/var/run/postgres', db).with_exec(['python', '-m', 'pytest', '-v'])
    result = await test.stdout()
    print(result)

import asyncio
asyncio.run(main())
"

Cross-Platform Container Build

Building multi-architecture images is straightforward with Dagger’s built-in platform support:

1
2
3
4
5
# Build for linux/amd64 and linux/arm64 simultaneously
dagger run dagger call build \
    --platform linux/amd64 \
    --platform linux/arm64 \
    --output type=image,name=registry.example.com/myapp:latest,push=true

Dagger vs Traditional CI/CD: When to Use Each

Dagger does not replace your CI platform — it replaces the pipeline logic inside it. The right approach depends on your team’s needs:

Choose Dagger when:

  • Your team uses multiple CI platforms (some repos on GitHub Actions, others on GitLab CI) and wants a single pipeline definition
  • Developers need to debug pipeline failures locally instead of pushing commits to trigger CI
  • You want content-addressed caching without manual cache key configuration
  • You are building a self-hosted CI infrastructure from scratch and want a portable foundation
  • Your pipelines involve complex logic that is awkward to express in YAML

Stick with traditional CI/CD YAML when:

  • Your team uses a single CI platform exclusively
  • Your pipelines are simple: build, test, deploy in linear steps
  • Your team is not comfortable writing pipeline code in Go, Python, or TypeScript
  • You need deep integration with platform-specific features (GitHub Environment protections, GitLab review apps)

For most self-hosted infrastructure teams, the best approach is hybrid: define your core pipeline logic in Dagger for portability, then invoke it from a lightweight CI runner (like Woodpecker CI or a cron job on your server). This gives you local debuggability and cross-platform portability while still using your existing CI infrastructure for triggering.

For related reading, see our guide to self-hosted CI/CD platforms for runner options, and our comparison of container build tools for alternative approaches to container-based pipelines. If you are deploying to Kubernetes, check our Kubernetes CNI guide for networking setup.

FAQ

Is Dagger free for self-hosted use?

Yes. Dagger is open source under the Apache 2.0 license. The engine runs entirely on your own infrastructure with no licensing fees. Dagger Cloud offers optional hosted features (remote caching, pipeline analytics, team management), but the core engine and SDK are fully self-contained and free.

Can Dagger replace Jenkins entirely?

Dagger replaces the pipeline definition and execution layer, not the scheduling and orchestration layer. Jenkins handles job scheduling, queue management, user authentication, and plugin integrations. Dagger handles what happens inside each pipeline step. You can run Dagger pipelines from Jenkins, from cron, or from any script — but you would still need a scheduler to trigger them.

What programming languages does Dagger support?

Dagger SDKs are available for Go, Python, TypeScript, and CUE (its own configuration language). The CUE SDK is the most mature and is recommended for most pipeline definitions. The Go SDK gives you full programmatic control for complex logic. Python and TypeScript SDKs are ideal for teams already working in those ecosystems.

How does Dagger handle secrets?

Dagger provides a #Secret type that stores sensitive values encrypted in memory. Secrets are never written to disk or included in the content-addressed cache. You can pass secrets into pipeline steps as environment variables or mounted files. For self-hosted deployments, integrate with Vault, SOPS, or any secrets manager by loading values into Dagger’s secret store before pipeline execution.

Does Dagger require Docker?

Dagger defaults to Docker as its container runtime, but it also supports Podman, containerd, and nerdctl. For environments where Docker is not available (such as restricted enterprise servers), you can configure Dagger to use an alternative runtime via the _EXPERIMENTAL_DAGGER_RUNNER_HOST environment variable.

Can I run Dagger pipelines on Kubernetes?

Yes. Dagger can run on any platform that supports containers, including Kubernetes clusters. You can deploy the Dagger engine as a StatefulSet and execute pipelines from worker pods. For production Kubernetes deployments, consider using the Dagger Helm chart, which handles RBAC, persistent volumes, and engine scaling automatically.

Advertise here
Advertise here