0.12
A long-lived project that still receives updates
A Test Kitchen Driver for Docker & Chef Infra optimized for rapid testing using Chef Infra docker images
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Runtime

>= 1.33, < 3
>= 3.0, < 5
 Project Readme

kitchen-dokken

Gem Version

Fast Chef Infra cookbook testing with Docker. kitchen-dokken is a Test Kitchen plugin that converges your cookbooks inside containers in seconds rather than minutes.

  • Why kitchen-dokken?
    • When not to use it
  • Requirements
  • Installation
  • Quick start
  • How it works
  • Configuration reference
    • Driver · Provisioner · Transport
  • Recipes
    • Images and platforms: Testing services and init systems · Minimalist images · Using dokken-images · Multi-architecture testing
    • Chef Infra Client: Using Cinc · Using with Chef · Chef Infra Client options · Testing without Chef · Pre-release and archived Chef versions
    • Networking: Networking · IPv6 · Testing behind a TLS-intercepting proxy
    • Registries and daemons: Private registries · Pulling through a mirror · Remote and containerised Docker hosts · Using Podman
    • Performance and resources: Caching downloaded packages · Preserving the Chef cache · Limiting container memory · tmpfs on /tmp
  • Troubleshooting
  • Contributing
  • FAQ
  • License

Also in this repository: Running under Podman and Accepting the Chef license.

Why kitchen-dokken?

Test Kitchen normally splits its work across four plugin types: a driver creates the machine, a transport connects to it, a provisioner puts it into the desired state, and a verifier checks the result.

kitchen-dokken ships the first three as one purpose-built unit. That lets it take shortcuts no general-purpose driver can:

  • No Chef install step. Chef Infra Client is mounted into your container from the official chef/chef image, so nothing is downloaded or installed per run.
  • No image commits between phases. The container is kept alive by a long-running PID 1, so converge, verify and login all reuse it.
  • No SSH by default. Commands run through docker exec, and the kitchen sandbox is bind-mounted straight off your disk.

The result is a feedback loop measured in seconds, on a laptop, offline.

When not to use it

The trade-off for that speed is scope. kitchen-dokken only tests Chef Infra cookbooks — there is no Puppet or Ansible provisioner here — and a container is not a perfect stand-in for a full OS install. Official distro images are extremely minimal, so you may need to install packages before your cookbook will run at all (see Minimalist images).

If you need a faithful analog of a production machine and would rather not troubleshoot missing packages, reach for kitchen-vagrant instead. If you want a general-purpose Docker driver that works with other provisioners, use kitchen-docker.

Requirements

  • Ruby 3.1 or later
  • A reachable Docker daemon — Docker Engine, Docker Desktop, Rancher Desktop or Podman
  • Test Kitchen 3.0 or later (below 5.0)
  • kitchen-omnibus-chef 1.0 or later, which is where the ChefInfra provisioner this plugin extends now lives

The last two are gem dependencies and are installed for you.

The daemon is found via DOCKER_HOST, then /var/run/docker.sock, then tcp://127.0.0.1:2375. Set docker_host_url to override.

Installation

kitchen-dokken is bundled with Cinc Workstation and with Chef Workstation, so if you have either installed you already have it.

Otherwise, add it to your cookbook's Gemfile:

gem "kitchen-dokken"

or install the gem directly:

gem install kitchen-dokken

The examples below use Cinc. Everything works identically with Chef Infra Client — see Using Cinc and Using with Chef.

Quick start

Create a kitchen.yml in your cookbook:

---
driver:
  name: dokken
  chef_version: latest

transport:
  name: dokken

provisioner:
  name: dokken
  product_name: cinc

verifier:
  name: inspec

platforms:
  - name: almalinux-9
    driver:
      image: dokken/almalinux-9
      pid_one_command: /usr/lib/systemd/systemd

  - name: ubuntu-24.04
    driver:
      image: dokken/ubuntu-24.04
      pid_one_command: /bin/systemd

suites:
  - name: default
    run_list:
      - recipe[my_cookbook::default]

Then run it:

kitchen test

Useful during development:

kitchen converge default-almalinux-9   # apply the run_list, keep the container
kitchen login default-almalinux-9      # shell into the running container
kitchen verify default-almalinux-9     # run InSpec against it
kitchen destroy                        # clean up

The dokken/* images used above come from dokken-images: distro images pre-loaded with the packages Ohai and a debugging session expect. See Using dokken-images.

How it works

A single kitchen create builds up to three containers per suite.

                    ┌───────────────────────────────┐
   /opt/chef  ─────▶ │                               │
   (volume)          │   runner                      │
                     │   <hash>-default-almalinux-9  │  ◀── docker exec
   /opt/kitchen ────▶│                               │      (converge, verify,
   /opt/verifier     │   your cookbook converges     │       login)
   (bind or volume)  │   in here                     │
                     └───────────────────────────────┘

The chef container is a stopped container created from chef/chef:<version>. Nothing runs in it; it exists so its /opt/chef can be mounted into the runner with VolumesFrom. It is shared by every suite using the same Chef version, so the image is pulled once no matter how many suites you run.

The runner container is where your cookbook actually converges. It is built from a per-suite work image — your platform image plus any intermediate_instructions — and kept alive by pid_one_command, so the same container serves every subsequent converge, verify and login.

The data container is only created when the Docker daemon cannot read your local filesystem: a remote daemon, or Test Kitchen itself running inside a container. In that case the kitchen sandbox is copied into it over SSH and shared with the runner, instead of being bind-mounted. On a local daemon this container is never created.

Between them you will see something like this while a suite is up:

$ docker ps -a --format '{{.Names}}\t{{.Image}}\t{{.Status}}'
1f9d1cc06d-default-almalinux-9   1f9d1cc06d-default-almalinux-9:latest   Up 2 minutes
chef-latest                      chef/chef:latest                        Created

kitchen destroy removes the runner and its work image. The chef container and the dokken network are deliberately left behind — they are shared, and rebuilding them is the slow part.

Configuration reference

Every option below is optional; the defaults are what you get from the kitchen.yml in Quick start. Types are given because several options accept more than one shape — anywhere String, Array<String> appears, a bare scalar is wrapped for you, so cap_add: SYS_ADMIN and cap_add: [SYS_ADMIN] mean the same thing.

Driver

Image and Chef Infra Client

Option Type Default Description
image String derived from the platform name Base image for the platform under test, e.g. dokken/almalinux-9. Falls back to <platform>:<release>.
chef_version String latest Tag of the Chef Infra image to mount. Also accepts current for pre-releases, or stable as an alias for latest.
chef_image String chef/chef (or cincproject/cinc) Repository the Chef Infra Client is mounted from.
intermediate_instructions String, Array<String> none Extra Dockerfile lines baked into the work image. See Minimalist images.
image_prefix String none Prefix for the locally built work image name.
pull_platform_image Boolean true Always pull the platform image. false pulls only when it is missing.
pull_chef_image Boolean true Always pull the Chef Infra image. false pulls only when it is missing.
platform String "" (the daemon's own) OCI platform to pin containers and images to, e.g. linux/arm64/v8. See Multi-architecture testing.

Container runtime

Option Type Default Description
pid_one_command String sh -c "trap exit 0 SIGTERM; while :; do sleep 1; done" Command run as PID 1 to keep the container alive. Set to an init for service testing.
entrypoint String, Array<String> none Container Entrypoint.
env Array<String> none Environment variables, as KEY=value strings.
privileged Boolean false Run privileged. Forces host user namespaces, because the daemon will not run a privileged container inside one.
cap_add String, Array<String> none Linux capabilities to add.
cap_drop String, Array<String> none Linux capabilities to drop.
security_opt String, Array<String> none Values for --security-opt, e.g. seccomp=unconfined.
userns_host Boolean false Shorthand for user_ns_mode: host: disable user-namespace remapping for the container.
user_ns_mode String none Docker UsernsMode for the container. Wins over userns_host; privileged forces host regardless.
cgroupns_host Boolean false Run in the host cgroup namespace.
memory_limit Integer 0 (unlimited) Container memory limit, in bytes.

Volumes

Option Type Default Description
volumes Array<String>, Hash none Anonymous volumes. Entries containing a colon are treated as host:container bind mounts and moved into binds for you.
binds Array<String> [] Bind mounts, in Docker host:container[:opts] form.
tmpfs Hash, Array<String> {} tmpfs mounts, as a /path: options mapping or "/path:options" strings.

Networking

Option Type Default Description
hostname String dokken Container hostname, also registered as a network alias.
hostname_aliases String, Array<String> none Extra network aliases resolving to the container.
network_mode String dokken Docker network to attach to. host and bridge skip alias configuration.
ports String, Integer, Array, Hash none Published ports: container, host:container or ip:host:container, with optional /proto and low-high ranges.
dns String, Array<String> none Nameservers for the container.
dns_search String, Array<String> none DNS search domains for the container.
links String, Array<String> none Legacy container links.
ipv6 Boolean false Create the dokken network with IPv6 enabled.
ipv6_subnet String 2001:db8:1::/64 Subnet used when ipv6 is on.

Registries

Option Type Default Description
docker_registry String none Registry prefix applied to every image pulled.
creds_file String none Path to a JSON file of registry credentials. See Private registries.
docker_config_creds Boolean true Read credentials from ~/.docker/config.json.

Daemon and data container

Option Type Default Description
docker_host_url String auto-detected Docker daemon to talk to.
read_timeout Integer 3600 Docker API read timeout, in seconds.
write_timeout Integer 3600 Docker API write timeout, in seconds.
api_retries Integer 20 How many times to retry a retryable Docker API call.
docker_info Hash queried from the daemon Cached docker info output, used to detect the daemon's operating system. Resolved automatically; set it only to override that detection.
data_image String dokken/kitchen-cache:latest Image the data container is built from. Only used when the daemon cannot read your filesystem — see Remote and containerised Docker hosts.
data_ssh_port Integer, String none Fixed host port for the data container's SSH service.

pre_create_command, which the Test Kitchen driver base class declares, is not supported: kitchen-dokken replaces create outright and never runs it.

Provisioner

Option Type Default Description
product_name String chef chef or cinc. See Using Cinc.
product_version String the driver's chef_version Version of the client the provisioner reports running. Follows chef_version unless overridden.
chef_license String prompted, then remembered License acceptance value, e.g. accept, accept-silent, accept-no-persist. Set it to avoid an interactive prompt in CI.
chef_binary String /opt/chef/bin/chef-client Client binary to run. Defaults to the Cinc path when product_name: cinc.
chef_options String " -z" Options passed to the client. A leading space is added if you omit it.
chef_log_level String warn Value for -l. This is the log level the client actually runs at: it also overwrites the inherited log_level, so setting log_level on its own has no effect.
chef_output_format String doc Value for -F.
root_path String /opt/kitchen Where the kitchen sandbox is mounted inside the container.
clean_dokken_sandbox Boolean true Empty the sandbox after each converge. See Preserving the Chef cache.
profile_ruby Boolean false Pass --profile-ruby.
slow_resource_report Boolean, Integer none Pass --slow-report. An Integer sets the threshold. Requires Chef Infra Client 17.2+.
docker_host_url String auto-detected Docker daemon the provisioner resolves its own docker_info against.
docker_info Hash queried from the daemon Cached docker info output for the provisioner's docker_host_url. The driver, provisioner and transport resolve this independently.

The provisioner inherits from ChefInfra, so enforce_idempotency, multiple_converge, deprecations_as_errors, retry_on_exit_code, max_retries, wait_for_retry, attributes, named_run_list, policyfile, berksfile_path and the rest of the cookbook-resolution options all work here too.

Two inherited options do not, because this provisioner replaces call rather than extending it:

  • uploads — files are never copied in.
  • downloads — files are never copied out. The transport implements no download either, so this one is a genuine gap rather than a decision.

Transport

Option Type Default Description
login_command String docker Executable used by kitchen login.
write_timeout Integer 3600 How long a single docker exec may run, in seconds. Despite the name this is a command timeout, not a Docker API timeout.
read_timeout Integer 3600 Accepted but unused. The transport's own API connection uses the global docker-api options; the driver's read_timeout is the one that has an effect.
host_ip_override String, false auto-detected Address used to reach the data container's SSH service. Detected as host.docker.internal inside Docker Desktop, localhost against a Docker Desktop daemon, and unused otherwise.
docker_host_url String auto-detected Docker daemon to talk to.
docker_info Hash queried from the daemon Cached docker info output for docker_host_url. The driver, provisioner and transport resolve this independently.

Recipes

Testing services and init systems

Containers are process-oriented, not machine-oriented, which matters as soon as a recipe uses the service resource. To make systemd work, run it as PID 1:

platforms:
  - name: almalinux-9
    driver:
      image: dokken/almalinux-9
      privileged: true
      pid_one_command: /usr/lib/systemd/systemd

Older kernels or daemons may also need the host cgroup filesystem mounted in:

      volumes:
        - /sys/fs/cgroup:/sys/fs/cgroup:ro

Minimalist images

Official distro images ship a package manager, coreutils and very little else — far less than a Vagrant box or a cloud image. You will often need to install prerequisites before Chef, InSpec or your own programs will run.

intermediate_instructions adds Dockerfile lines to the per-suite work image:

platforms:
  - name: debian-12
    driver:
      image: debian:12
      intermediate_instructions:
        - RUN /usr/bin/apt-get update
        - RUN /usr/bin/apt-get install -y apt-transport-https net-tools

Any valid Dockerfile instruction works. Use this as little as possible — it runs on every work-image build, and dokken-images usually solves the same problem for free.

Using dokken-images

dokken-images are distro images pre-loaded with the packages that make Ohai behave and kitchen login useful. They are rebuilt daily so package metadata stays fresh. Prefix a distro with dokken/:

platforms:
  - name: ubuntu-24.04
    driver:
      image: dokken/ubuntu-24.04
      pid_one_command: /bin/systemd

You still need to specify pid_one_command yourself.

Using Cinc

Cinc Client is the community distribution of Chef Infra Client. Set product_name: cinc on the provisioner; nothing else is required:

provisioner:
  name: dokken
  product_name: cinc

With that set, kitchen-dokken pulls cincproject/cinc instead of chef/chef, uses a cinc-<version> volume container so it will not collide with Chef Infra containers, runs /opt/cinc/bin/cinc-client, and skips the Chef license prompt (Cinc is community-built and Apache-licensed).

Override chef_image or chef_binary if you need a custom Cinc image or a non-standard install path.

Using with Chef

The examples in this README set product_name: cinc. Leaving it unset, or setting product_name: chef, runs Chef Infra Client instead — that is the driver's default:

provisioner:
  name: dokken
  product_name: chef

With Chef Infra Client you will need to accept the Chef licence, either by setting chef_license on the provisioner or by answering the prompt. Cinc has no such requirement.

Nothing else differs: the same driver, transport, and verifier options apply either way.

Using Podman

See the Podman documentation.

Multi-architecture testing

Set platform to test an architecture other than your host's. It is passed to the daemon as an OCI platform, so emulation must be available (Docker Desktop and binfmt_misc both provide it):

platforms:
  - name: almalinux-9-arm
    driver:
      image: dokken/almalinux-9
      platform: linux/arm64/v8

The runner and the Chef volume container are both pinned to this platform, since /opt/chef is mounted from one into the other. The data container is not: it only serves files over SSH, and is always built for the host.

Networking

Publish ports, add aliases and set DNS on the driver:

driver:
  name: dokken
  hostname: web.example.com
  hostname_aliases:
    - web
    - www
  ports:
    - '8080:80'
    - '8301:8301/udp'
    - '127.0.0.1:8500:8500'
    - '9000-9002'
  dns:
    - 8.8.8.8
  dns_search:
    - example.com

Containers join the dokken network by default, which is what lets suites resolve each other by hostname. network_mode: host and network_mode: bridge are also supported, but skip alias registration.

IPv6

driver:
  name: dokken
  ipv6: true
  ipv6_subnet: "2001:db8:1::/64"

This is effectively a global setting: kitchen-dokken does not reconfigure the dokken network once it exists, so setting it per-suite will not do what you expect. Check the current state with:

docker network inspect dokken --format='{{.EnableIPv6}}'

If that prints false, delete the network and let dokken recreate it.

For IPv6 containers to reach the internet, the daemon needs ip6tables support in /etc/docker/daemon.json (restart the daemon afterwards):

{
  "experimental": true,
  "ip6tables": true
}

Some images additionally need the ip6table_filter module loaded on the host:

sudo lsmod | grep ip6table_filter || sudo modprobe ip6table_filter

Private registries

Credentials are scoped to the registry an image is pulled from, the way the docker CLI scopes them. An image whose registry has no matching entry is pulled anonymously rather than with some other registry's credentials.

By default ~/.docker/config.json is read, so docker login is usually all you need:

{
  "auths": {
    "quay.io": {
      "auth": "<base64 encoded username:password>"
    }
  }
}

Images with no registry prefix (almalinux:9, dokken/almalinux-8) resolve to Docker Hub and use the https://index.docker.io/v1/ entry. docker.io and registry-1.docker.io are accepted as Hub aliases too.

Credential helpers work as well — kitchen-dokken runs docker-credential-<helper> on demand:

{
  "credHelpers": {
    "1234-cloud-registry.example.com": "example-cloud"
  }
}

Set docker_config_creds: false to ignore the Docker config entirely and pull anonymously.

To supply credentials directly instead, point creds_file at a JSON file:

platforms:
  - name: almalinux-9
    driver:
      image: registry.example.com/almalinux-9
      creds_file: './creds.json'
{
  "username": "org_username",
  "password": "password",
  "email": "email@org.com",
  "serveraddress": "https://registry.example.com/"
}

A creds_file takes precedence over ~/.docker/config.json, and is used for every registry.

Pulling through a mirror

To route every pull through your own registry or Docker Hub mirror:

driver:
  docker_registry: docker.example.com

The mirror must carry the platform images you test with, plus the almalinux:9 image used to build the data image.

Remote and containerised Docker hosts

When the daemon cannot read your local filesystem, kitchen-dokken automatically builds the data container and ships the sandbox to it over SSH. This happens when docker_host_url is a tcp:// URL (Docker Desktop and Boot2Docker excepted — they share the host's files), or when Test Kitchen is itself running inside a container.

The data container's SSH service is published on a random host port. Pin it if you need a predictable firewall rule:

driver:
  name: dokken
  data_ssh_port: 30000

When Test Kitchen is the thing running in a container, that container also has to be able to reach the containers it creates. They are attached to the network_mode network — dokken by default — and Docker will not route from the default bridge to a user-defined network, so attach kitchen's own container to the same one:

docker network inspect dokken >/dev/null 2>&1 || docker network create dokken

docker run --rm \
  --network dokken \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v "$PWD:/workspace" -w /workspace \
  ruby:3.4 bundle exec kitchen test

Without it the converge fails at Transferring files with a connection timeout.

Caching downloaded packages

Package downloads can be cached outside the container. On Debian and Ubuntu, apt also needs to be told to stop deleting them:

driver:
  name: dokken
  volumes:
    - /var/cache/apt/archives/:/var/cache/apt/archives/

platforms:
  - name: ubuntu-24.04
    driver:
      image: dokken/ubuntu-24.04
      pid_one_command: /bin/systemd
      intermediate_instructions:
        - RUN rm -f /etc/apt/apt.conf.d/docker-clean

Preserving the Chef cache

/opt/kitchen is emptied after every converge, which includes the Chef file cache. For cookbooks that pull a lot of data with remote_file, keeping it makes repeat converges much faster:

provisioner:
  name: dokken
  clean_dokken_sandbox: false

Limiting container memory

Memory is unbounded by default (or bounded by the Docker VM on macOS and Windows):

driver:
  name: dokken
  memory_limit: 2147483648 # 2GB

tmpfs on /tmp

A container running an init system will usually mount a tmpfs over /tmp. Because Docker bind-mounts the kitchen data before init starts, Bats and Serverspec need to be pointed somewhere else. InSpec is unaffected.

verifier:
  root_path: '/opt/verifier'
  sudo: false

Chef Infra Client options

provisioner:
  name: dokken
  chef_binary: /opt/chef/bin/chef-solo
  chef_options: ""
  chef_log_level: debug
  chef_output_format: minimal
  profile_ruby: true
  slow_resource_report: true

Use chef_options: "" for old clients that predate -z.

Testing without Chef

If the image supplies a no-op binary that exits successfully, a suite can be verified without converging at all:

platforms:
  - name: alpine
    driver:
      image: alpine:latest
    provisioner:
      chef_binary: /bin/true

Pre-release and archived Chef versions

Chef publishes every functioning build to Docker Hub, including the current channel:

driver:
  chef_version: current

For versions not published as chef/chef, point chef_image at another repository:

driver:
  chef_image: someara/chef
  chef_version: 12.21.31

Testing behind a TLS-intercepting proxy

Corporate networks often terminate TLS with an internal CA that containers do not trust. intermediate_instructions plus entrypoint can install it into both the system trust store and Chef's own bundle:

driver:
  name: dokken
  entrypoint: /bin/entrypoint
  intermediate_instructions:
    - RUN /usr/bin/openssl s_client -showcerts -verify 5 -connect example.com:443 </dev/null | /usr/bin/awk '/BEGIN/,/END/{if(/BEGIN/){a++}; certs[a]=(certs[a] "\n" $0)}; END {print certs[a]}' >> /usr/local/share/ca-certificates/ca.crt && update-ca-certificates
    - RUN echo "#!/bin/sh -e\ncat /usr/local/share/ca-certificates/ca.crt >> /opt/chef/embedded/ssl/certs/cacert.pem\nexec \"\$@\"\n" >> /bin/entrypoint && chmod +x /bin/entrypoint

The first instruction extracts the root CA from a live handshake and adds it to the system trust store. The second writes an entrypoint that appends it to Chef's CA bundle before exec'ing pid_one_command.

Note that each suite adds the certificate again, which accumulates disk usage over time. Prune regularly on CI.

A complete, non-trivial kitchen.yml lives in the docker cookbook.

Troubleshooting

Two Test Kitchen commands answer most of the questions below without you having to guess:

kitchen doctor default-almalinux-9   # is the daemon reachable? is creds_file readable?
kitchen list --live                  # ask the daemon what is actually running

kitchen doctor reports the daemon it resolved, its version, and whether a configured creds_file exists and parses — the two things that stop a run before anything is built. kitchen list --live reads container state straight from the daemon rather than from the last recorded action, so a container that died out from under Test Kitchen shows as exited instead of created.

could not connect to the docker host ... Is docker running? The daemon is unreachable. Check docker info, and set docker_host_url if your daemon is not at DOCKER_HOST or /var/run/docker.sock. Run kitchen doctor to see which daemon was resolved.

The service resource fails, or systemctl reports no init system. Set pid_one_command to your init and run privileged: true. See Testing services and init systems.

Ohai warnings, or a missing ip, ps or hostname command. The base image is too minimal. Switch to a dokken-image or add the packages with intermediate_instructions.

A pull from a private registry returns 401. Credentials are matched per registry. Confirm the registry host in your image reference has an entry in ~/.docker/config.json, or supply a creds_file.

Changes to intermediate_instructions seem to be ignored. The work image is cached per suite. Run kitchen destroy for that suite to force a rebuild.

IPv6 settings do not take effect. The dokken network is created once and never reconfigured. See IPv6.

Transferring files to ... hangs or times out against a remote daemon. The sandbox is shipped over SSH to a data container, and Test Kitchen has to have a route to it. See Remote and containerised Docker hosts — when Test Kitchen is itself in a container, it has to join the dokken network.

A container will not start, and the error names pid 1. The image's PID 1 exited immediately. Check pid_one_command and entrypoint, and read docker logs <instance-name>. If the daemon refused outright the error says so instead, and quotes the daemon's own reason — a capability the kernel would not grant, or a port already bound.

Stale containers or images are piling up. kitchen destroy intentionally leaves the shared chef container and the dokken network behind. Remove them by hand when you want a clean slate:

docker rm -f chef-latest
docker network rm dokken

Contributing

Bug reports and pull requests are welcome. See CONTRIBUTING.md for how to set up, run the unit and integration suites, and what CI checks.

FAQ

What about kitchen-docker?

kitchen-docker bakes SSH, Chef and the kitchen data into the image, and works as a general-purpose driver with any provisioner. kitchen-dokken blurs the driver, transport and provisioner roles together to avoid all of that, at the cost of only supporting Chef Infra.

See "Kitchen-Docker or Kitchen-Dokken?" for a longer comparison.

Can I publish the container I just tested?

There is no kitchen publish (test-kitchen#329), but you can do it by hand:

kitchen verify suite_name
docker stop suite_name
docker tag suite_name:latest registry.example.com:5043/org/image
docker push registry.example.com:5043/org/image
kitchen destroy

Do I have to accept the Chef license?

Yes, for Chef Infra Client. See the license documentation. Cinc does not require it.

License

Apache-2.0. See LICENSE.