CI Pipeline

Run your Dokkimi tests in CI. No config files, no cluster expertise required.

GitHub Action (recommended)

The simplest way to run Dokkimi in CI is with the official GitHub Action. Add one step to your workflow:

# .github/workflows/test.yml
name: Integration Tests
on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5

      - name: Build my service
        run: docker build -t my-app:latest .

      - uses: dokkimi/github-action@v1
        with:
          tests: .dokkimi/

That's it. The action installs k3s, sets up a single-node Kubernetes cluster, installs the Dokkimi CLI, pulls sidecar images, and runs your tests. Results stream to the GitHub Actions log.

Configuration

Override defaults with with: inputs — no config files needed:

- uses: dokkimi/github-action@v1
  with:
    tests: .dokkimi/
    max-parallel: 3
    max-booting: 1
    timeout: 60000
    viewport-width: 1920
    viewport-height: 1080
InputDefaultDescription
testsrequiredPath to .dokkimi/ directory or a specific definition file
max-parallel6Maximum concurrent test namespaces
max-booting2Maximum namespaces booting simultaneously
timeout30000HTTP request timeout in milliseconds
viewport-width1280Default browser viewport width for UI tests
viewport-height720Default browser viewport height for UI tests
dokkimi-versionlatestDokkimi CLI version to install
timeout-minutes15Overall job timeout

Build your images first. The action expects your Docker images to already be built on the runner. Add a docker build step before calling dokkimi/github-action.

Multiple services

If your repo has multiple services, build them all before running tests:

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5

      - name: Build services
        run: |
          docker build -t api-gateway:latest -f services/api/Dockerfile .
          docker build -t user-service:latest -f services/user/Dockerfile .
          docker build -t web-app:latest -f apps/web/Dockerfile .

      - uses: dokkimi/github-action@v1
        with:
          tests: .dokkimi/

Running a subset of tests

Point tests at a specific directory or file to run only what you need:

# Run only the checkout tests
- uses: dokkimi/github-action@v1
  with:
    tests: .dokkimi/definitions/checkout.yaml

# Run everything under a subdirectory
- uses: dokkimi/github-action@v1
  with:
    tests: .dokkimi/api-tests/

Manual setup

If you're not using the GitHub Action — or you're on a different CI system — you can set up the pipeline yourself. The key components are k3s, the Dokkimi CLI, and a few environment variables.

Step 1: Install k3s

k3s is a lightweight Kubernetes distribution that runs as a single binary. The --docker flag tells it to use the host's Docker daemon, so your locally-built images are available to pods immediately — no image loading or registry needed.

curl -sfL https://get.k3s.io | \
  INSTALL_K3S_EXEC="--docker --disable=traefik --write-kubeconfig-mode=644" sh -

export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
until kubectl get nodes | grep -q ' Ready'; do sleep 2; done

Step 2: Detect the node IP

Pods running inside k3s need to reach Control Tower on the host. The node's InternalIP is the reliable way to do this — it works across all container runtimes and CI environments:

NODE_IP=$(kubectl get node -o jsonpath='{.items[0].status.addresses[?(@.type=="InternalIP")].address}')
export CONTROL_TOWER_HOST=$NODE_IP

Step 3: Install Dokkimi

npm install -g dokkimi

Step 4: Build your images

Build your service images on the runner. Since k3s uses the host Docker daemon, pods can pull them directly — no docker push or kind load needed.

docker build -t my-app:latest .

Step 5: Pre-pull external images

Dokkimi sidecars depend on a few external images. Pre-pulling them avoids timeouts during test runs:

docker pull busybox:1.37
docker pull fluent/fluent-bit:3.2
docker pull andyshinn/dnsmasq:2.83

Step 6: Run tests

The --ci flag enables CI-optimized output (no spinners, no interactive prompts):

dokkimi run .dokkimi/ --ci

Environment variable overrides

Dokkimi reads these environment variables to override default configuration. Set them before running dokkimi run:

VariableDefaultDescription
CONTROL_TOWER_HOSTlocalhostHost IP that pods use to reach Control Tower
DOKKIMI_MAX_CONCURRENT_NAMESPACES6Max parallel test namespaces
DOKKIMI_MAX_BOOTING_NAMESPACES2Max namespaces booting at once
DOKKIMI_HTTP_TIMEOUT30000HTTP request timeout (ms)
DOKKIMI_DEFAULT_VIEWPORT_WIDTH1280Default browser viewport width for UI tests
DOKKIMI_DEFAULT_VIEWPORT_HEIGHT720Default browser viewport height for UI tests

Full GitHub Actions example (manual)

Here's a complete workflow without the official action:

name: Integration Tests
on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    env:
      DOKKIMI_MAX_CONCURRENT_NAMESPACES: 3
      DOKKIMI_MAX_BOOTING_NAMESPACES: 1
    steps:
      - uses: actions/checkout@v5

      - uses: actions/setup-node@v5
        with:
          node-version: 22

      - name: Build service images
        run: |
          docker build -t api-gateway:latest -f services/api/Dockerfile .
          docker build -t web-app:latest -f apps/web/Dockerfile .

      - name: Free disk space
        run: |
          sudo rm -rf /usr/local/lib/android /usr/share/dotnet /opt/ghc /usr/local/share/powershell
          docker builder prune -af

      - name: Install k3s
        run: |
          curl -sfL https://get.k3s.io | \
            INSTALL_K3S_EXEC="--docker --disable=traefik --write-kubeconfig-mode=644" sh -
          export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
          until kubectl get nodes | grep -q ' Ready'; do sleep 2; done
          echo "KUBECONFIG=/etc/rancher/k3s/k3s.yaml" >> $GITHUB_ENV
          NODE_IP=$(kubectl get node -o jsonpath='{.items[0].status.addresses[?(@.type=="InternalIP")].address}')
          echo "CONTROL_TOWER_HOST=$NODE_IP" >> $GITHUB_ENV

      - name: Pull external images
        run: |
          docker pull busybox:1.37
          docker pull fluent/fluent-bit:3.2
          docker pull andyshinn/dnsmasq:2.83

      - name: Install Dokkimi
        run: npm install -g dokkimi

      - name: Run tests
        timeout-minutes: 15
        run: dokkimi run .dokkimi/ --ci

      - name: Cleanup
        if: always()
        run: |
          dokkimi clean 2>/dev/null || true
          /usr/local/bin/k3s-uninstall.sh || true

The dokkimi clean command tears down any remaining test namespaces and releases cluster resources. Always run it in your cleanup step to avoid leaking pods if a test run is interrupted.

GitLab CI

The same manual approach works in GitLab CI. You need a shell executor with Docker installed — k3s requires host-level access that isn't available inside a standard Docker-in-Docker container:

integration-tests:
  tags:
    - shell
  variables:
    DOKKIMI_MAX_CONCURRENT_NAMESPACES: "3"
    DOKKIMI_MAX_BOOTING_NAMESPACES: "1"
  before_script:
    - docker build -t my-app:latest .
    - sudo rm -rf /usr/local/lib/android /usr/share/dotnet /opt/ghc || true
    - docker builder prune -af
  script:
    - curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--docker --disable=traefik --write-kubeconfig-mode=644" sudo sh -
    - export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
    - until kubectl get nodes | grep -q ' Ready'; do sleep 2; done
    - export CONTROL_TOWER_HOST=$(kubectl get node -o jsonpath='{.items[0].status.addresses[?(@.type=="InternalIP")].address}')
    - docker pull busybox:1.37 && docker pull fluent/fluent-bit:3.2 && docker pull andyshinn/dnsmasq:2.83
    - npm install -g dokkimi
    - dokkimi run .dokkimi/ --ci
  after_script:
    - dokkimi clean 2>/dev/null || true
    - sudo /usr/local/bin/k3s-uninstall.sh || true

Private images

If your service definitions reference images from a private registry, there are two places authentication is needed: the CI runner (for docker build and docker pull) and the k3s cluster (for pods pulling images at runtime).

Log in on the CI runner

Add a registry login step before you build or pull any private images. This example uses GitHub Container Registry, but the same pattern works for DockerHub, AWS ECR, or any OCI registry:

# GitHub Container Registry
- name: Log in to ghcr.io
  uses: docker/login-action@v3
  with:
    registry: ghcr.io
    username: ${{ github.actor }}
    password: ${{ secrets.GITHUB_TOKEN }}

# DockerHub (requires a repository secret)
- name: Log in to DockerHub
  uses: docker/login-action@v3
  with:
    username: ${{ secrets.DOCKERHUB_USERNAME }}
    password: ${{ secrets.DOCKERHUB_TOKEN }}

# AWS ECR
- name: Log in to ECR
  uses: docker/login-action@v3
  with:
    registry: 123456789.dkr.ecr.us-east-1.amazonaws.com
    username: ${{ secrets.AWS_ACCESS_KEY_ID }}
    password: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

With the GitHub Action, add the login step before dokkimi/github-action@v1. For manual setups, add it before any docker build or docker pull commands.

Create a Kubernetes image pull secret

If your .dokkimi/ definitions reference private images that pods need to pull at runtime (not just images you built locally on the runner), k3s also needs registry credentials. Create an image pull secret after the cluster is ready:

- name: Create image pull secret
  run: |
    kubectl create secret docker-registry registry-creds \
      --docker-server=ghcr.io \
      --docker-username=${{ github.actor }} \
      --docker-password=${{ secrets.GITHUB_TOKEN }}

Then reference it in your service definition:

services:
  - name: my-service
    image: ghcr.io/my-org/my-service:latest
    imagePullSecret: registry-creds

You don't need image pull secrets for locally-built images. When k3s runs with --docker, any image you build on the runner with docker build is already available to pods. Image pull secrets are only needed for images that pods pull from a remote registry at runtime.

Tips

Free disk space on GitHub Actions

GitHub runners have limited disk. If you're building multiple Docker images, free space before installing k3s:

- name: Free disk space
  run: |
    sudo rm -rf /usr/local/lib/android /usr/share/dotnet /opt/ghc /usr/local/share/powershell
    docker builder prune -af

Why k3s with --docker?

CI runners already have Docker installed. The --docker flag tells k3s to use the host's Docker daemon instead of containerd. This means images you build with docker build are immediately available to Kubernetes pods — no registry, no kind load, no extra steps.

UI test viewport

Dokkimi sets a default browser viewport of 1280×720 for UI tests. Override it globally via environment variables, or per-test with the viewport sub-step:

# Override the global default
steps:
  - visit: /
  - waitFor: "[data-testid='dashboard']"

# Or set a specific viewport for one test
steps:
  - viewport:
      width: 375
      height: 812
  - visit: /
  - waitFor: "[data-testid='mobile-nav']"

Debugging failures

Use dokkimi dump --failed to export structured JSON of failing tests. In CI, you can upload this as an artifact for post-run analysis:

- name: Export failures
  if: failure()
  run: dokkimi dump --failed -o test-failures.json

- uses: actions/upload-artifact@v4
  if: failure()
  with:
    name: test-failures
    path: test-failures.json