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 | Input | Default | Description |
|---|---|---|
tests | required | Path to .dokkimi/ directory or a specific definition file |
max-parallel | 6 | Maximum concurrent test namespaces |
max-booting | 2 | Maximum namespaces booting simultaneously |
timeout | 30000 | HTTP request timeout in milliseconds |
viewport-width | 1280 | Default browser viewport width for UI tests |
viewport-height | 720 | Default browser viewport height for UI tests |
dokkimi-version | latest | Dokkimi CLI version to install |
timeout-minutes | 15 | Overall 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:
| Variable | Default | Description |
|---|---|---|
CONTROL_TOWER_HOST | localhost | Host IP that pods use to reach Control Tower |
DOKKIMI_MAX_CONCURRENT_NAMESPACES | 6 | Max parallel test namespaces |
DOKKIMI_MAX_BOOTING_NAMESPACES | 2 | Max namespaces booting at once |
DOKKIMI_HTTP_TIMEOUT | 30000 | HTTP request timeout (ms) |
DOKKIMI_DEFAULT_VIEWPORT_WIDTH | 1280 | Default browser viewport width for UI tests |
DOKKIMI_DEFAULT_VIEWPORT_HEIGHT | 720 | Default 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