Skip to content
Back to skills

Dagger

BSecurity

Write CI/CD pipelines as code with Dagger — portable, cacheable, container-based pipelines that run locally and in any CI system. Use when someone asks to "write CI pipeline in TypeScript", "portable CI/CD", "run GitHub Actions locally", "Dagger pipeline", "CI as code", "containerized build pipeline", or "test my CI locally before pushing". Covers Dagger SDK (TypeScript/Python), pipeline composition, caching, secrets, and multi-stage builds.

  • 142 stars
  • 0 votes
  • 0 copies
  • 5 views
  • Added May 27, 2026
devopstypescriptpythongoshellbashsqlnextjsnodedockeraws

Works with

  • terminal
  • cli

Security analysis

B84/100
  • mediumUses curl or wget to download content
  • criticalAccesses sensitive system or user directories

Pro scans all 2 files and shows the line behind each finding

Scanned October 4, 2026

npx -y skills add TerminalSkills/skills --skill dagger --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Dagger?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Dagger
[![Security: B — Skills Directory](https://www.skillsdirectory.com/api/skills/terminalskills-dagger/badge)](https://www.skillsdirectory.com/skills/terminalskills-dagger)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: dagger
description: >-
  Write CI/CD pipelines as code with Dagger — portable, cacheable, container-based
  pipelines that run locally and in any CI system. Use when someone asks to
  "write CI pipeline in TypeScript", "portable CI/CD", "run GitHub Actions
  locally", "Dagger pipeline", "CI as code", "containerized build pipeline",
  or "test my CI locally before pushing". Covers Dagger SDK (TypeScript/Python),
  pipeline composition, caching, secrets, and multi-stage builds.
license: Apache-2.0
compatibility: "A container runtime (Docker) and the Dagger CLI. Modules in TypeScript, Python, Go and other SDKs."
metadata:
  author: terminal-skills
  version: "1.1.0"
  repository: https://github.com/dagger/dagger
  category: devops
  tags: ["ci-cd", "containers", "pipelines", "dagger", "docker"]
---

# Dagger

## Overview

Dagger lets you write CI/CD pipelines in TypeScript, Python, or Go instead of YAML. Pipelines run in containers, are fully cacheable, and work identically on your laptop and in GitHub Actions/GitLab CI/Jenkins. No more "works in CI but not locally" — test your entire pipeline before pushing.

## When to Use

- Tired of debugging YAML-based CI configs by pushing commits
- Need to run the exact same pipeline locally and in CI
- Complex build pipelines that benefit from TypeScript/Python logic (conditionals, loops)
- Want container-level caching for build steps
- Multi-language monorepo where different projects need different pipelines

## Instructions

### Setup

```bash
# macOS / Linux with Homebrew
brew install dagger/tap/dagger

# Linux without Homebrew: download the release and verify its checksum first
VER=0.21.10
curl -fsSLO https://dl.dagger.io/dagger/releases/$VER/dagger_v${VER}_linux_amd64.tar.gz
curl -fsSL https://dl.dagger.io/dagger/releases/$VER/checksums.txt | grep linux_amd64 | sha256sum -c -
tar -xzf dagger_v${VER}_linux_amd64.tar.gz dagger && mv dagger ~/.local/bin/

dagger version

# Create a module in your project (code goes to .dagger/)
dagger init --sdk=typescript --name=ci
dagger functions        # list what the module exposes
```

The CLI starts a Dagger Engine container on first use, so Docker (or another supported container runtime) must be running. Latest release checked: v0.21.10 (2026-09-30). Docs also describe a 1.0 beta line; the code below targets the 0.21 TypeScript SDK.

### Basic Pipeline

```typescript
// .dagger/src/index.ts - CI pipeline for a Node.js project
import { dag, Container, Directory, Secret, argument, check, object, func } from "@dagger.io/dagger";

@object()
export class Ci {
  /** Base container with dependencies installed; the npm cache persists between runs. */
  @func()
  base(
    @argument({ defaultPath: "/", ignore: ["node_modules", ".git", "dist"] })
    source: Directory,
  ): Container {
    return dag
      .container()
      .from("node:22-slim")
      .withDirectory("/app", source)
      .withWorkdir("/app")
      .withMountedCache("/root/.npm", dag.cacheVolume("npm-cache"))
      .withExec(["npm", "ci"]);
  }

  /** Lint, test and build; `dagger check` runs every @check function. */
  @func()
  @check()
  async lint(
    @argument({ defaultPath: "/", ignore: ["node_modules", ".git", "dist"] })
    source: Directory,
  ): Promise<void> {
    await this.base(source).withExec(["npm", "run", "lint"]).sync();
  }

  @func()
  @check()
  async test(
    @argument({ defaultPath: "/", ignore: ["node_modules", ".git", "dist"] })
    source: Directory,
  ): Promise<void> {
    await this.base(source).withExec(["npm", "test"]).sync();
  }

  @func()
  build(
    @argument({ defaultPath: "/", ignore: ["node_modules", ".git", "dist"] })
    source: Directory,
  ): Directory {
    return this.base(source).withExec(["npm", "run", "build"]).directory("/app/dist");
  }

  /** Build and push a runtime image; returns the pushed reference. */
  @func()
  async publish(
    @argument({ defaultPath: "/", ignore: ["node_modules", ".git", "dist"] })
    source: Directory,
    address: string,
  ): Promise<string> {
    return dag
      .container()
      .from("node:22-slim")
      .withDirectory("/app", this.build(source))
      .withWorkdir("/app")
      .withEntrypoint(["node", "index.js"])
      .publish(address);
  }
}
```

`defaultPath: "/"` makes the project root the default, so `--source` can be omitted. Functions with no required arguments marked `@check()` are what `dagger check` runs (check support is in the 0.21 SDK; see the quickstart if your engine is older).

### Run Locally

```bash
dagger check                # run every @check function (lint + test)
dagger check -l             # list the checks
dagger call test            # run one function
dagger call build export --path=./dist
dagger call publish --address=ghcr.io/acme-labs/storefront:1.4.2
```

Registry credentials for `publish` come from `container.withRegistryAuth(address, username, secret)`; pass the token as a `Secret` argument, not a string.

### Pipeline with Services (Database)

```typescript
@func()
async integrationTest(
  @argument({ defaultPath: "/", ignore: ["node_modules", ".git"] })
  source: Directory,
): Promise<string> {
  const db = dag
    .container()
    .from("postgres:16")
    .withEnvVariable("POSTGRES_PASSWORD", "ci-only-password")
    .withEnvVariable("POSTGRES_DB", "storefront_test")
    .withExposedPort(5432)
    .asService({ useEntrypoint: true });   // run the image's own entrypoint

  return this.base(source)
    .withServiceBinding("db", db)          // reachable as host "db"
    .withEnvVariable("DATABASE_URL", "postgresql://postgres:ci-only-password@db:5432/storefront_test")
    .withExec(["npm", "run", "db:migrate"])
    .withExec(["npm", "run", "test:integration"])
    .stdout();
}
```

### Secrets

```typescript
// The Secret type keeps values out of image layers, logs and the cache.
@func()
async deploy(source: Directory, deployToken: Secret): Promise<string> {
  return this.base(source)
    .withSecretVariable("DEPLOY_TOKEN", deployToken)
    .withExec(["npm", "run", "deploy"])
    .stdout();
}
```

```bash
export DEPLOY_TOKEN=...                       # set in your shell or CI secret store
dagger call deploy --source=. --deploy-token=env://DEPLOY_TOKEN
```

Other providers: `file://`, `cmd://`, `op://` (1Password), `vault://`, `aws+sm://`. Argument names are kebab-cased on the CLI.

### GitHub Actions Integration

```yaml
# .github/workflows/ci.yml
name: CI
on: [push, pull_request]

jobs:
  ci:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dagger/dagger-for-github@v8.4.0
        with:
          call: test
          version: "0.21.10"
          cloud-token: ${{ secrets.DAGGER_CLOUD_TOKEN }}   # optional
```

Pin `version` to the engine you developed against; the action installs that CLI.

## Examples

### Example 1: Monorepo CI pipeline

**User prompt:** "I have a monorepo with a Next.js frontend and a Python backend. Set up a CI pipeline that tests both."

The agent will create a Dagger pipeline with separate functions for frontend (Node.js container, npm test) and backend (Python container, pytest), both running from the same module; `dagger check` runs them in parallel and a second run reuses cached layers.

### Example 2: Replace GitHub Actions with Dagger

**User prompt:** "My GitHub Actions workflow is 200 lines of YAML and I can't test it locally. Convert it to Dagger."

The agent runs `dagger init --sdk=typescript --name=ci`, translates each YAML step into a function (`lint`, `test`, `build`), mounts a cache volume for the package manager, and verifies with `dagger check`; each step prints in the TUI with a pass or fail mark, and a second run is served from cache.

## Guidelines

- **Run locally first** — `dagger check` / `dagger call` on your laptop before pushing to CI
- **Cache the package-manager cache, not node_modules** — `withMountedCache("/root/.npm", ...)`; a mounted node_modules hides what `npm ci` installed
- **Secrets via `Secret` type** — pass with `env://NAME`; never as plain string arguments or `withEnvVariable`
- **Narrow the source** — `defaultPath` plus `ignore` keeps `.git` and `node_modules` out of the upload and the cache key
- **Services for databases** — `asService()` + `withServiceBinding()` for Postgres/Redis in tests
- **Each `@func()` is independently callable** — design for composability
- **Container layers are cached** — order operations so rarely-changing steps come first
- **Dagger Cloud for team caching** — share build cache across CI runners
- **Use `withExec` for each command** — separate steps for better cache granularity

Files in this skill

  • SKILL.md6.6 KB
  • _scores.json1.6 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…