# Screwdriver API: a pluggable CI/CD service with the executor and SCM left open

> The API server behind Screwdriver, a build, test and delivery platform that grew out of a 2012 Jenkins workaround at Yahoo and was rebuilt in open source in 2016, with eighteen resources, three datastores, three executors and three source control systems chosen by configuration. The versioning is where the confusion starts, because the manifest says 8.0.0 while the newest tagged release is from 2020.

**screwdriver-cd/screwdriver** — An open source build platform designed for continuous delivery.

- Repository: https://github.com/screwdriver-cd/screwdriver
- Website: http://screwdriver.cd
- Stars: 1,049 · Forks: 175
- Language: JavaScript
- License: NOASSERTION
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/screwdriver-cd-screwdriver

## Where Screwdriver came from, and what it is now

Screwdriver is a self-contained, pluggable service to help you build, test and continuously deliver software using the latest containerization technologies, and this repository is its API server.

The history section is unusually direct about the origin. Screwdriver began as a hack for simplified interfacing with Jenkins at Yahoo in 2012. As the volume of builds increased, it became clear that Jenkins was not stable or feasible to use at the scale they were running builds. In 2016 they rebuilt Screwdriver from scratch in open source with their best coding practices and CI/CD goals in mind.

So the project is a rewrite of a Jenkins integration, and that origin explains the central design decision. Screwdriver is executor and SCM agnostic, meaning you can choose whichever plugin better suits your need or build your own. The project describes itself as completely free and open source with an actively maintained team.

The top level of the repository reflects the shape of the thing. There is `plugins/` for the pluggable resources, `lib/` for the shared code, `config/` for the configuration, `features/` for the cucumber functional tests, `test/` for the unit tests, `design/` with PlantUML diagrams, `hooks/`, `bin/server` as the entry point, a `Dockerfile` and a `Dockerfile.local`, a `screwdriver.yaml`, a `cucumber.js` and a `mocha.config.json`.

The primary language in the metadata is JavaScript, the licence is BSD 3-Clause according to the README, and the repository is not archived with the last push on 2026-10-15.

## Eighteen resources and three of everything

The API comes preloaded with eighteen resources, and the list is the fastest way to understand what the service does.

They are auth, banners, builds, buildClusters, collections, commands, coverage, events, jobs, pipelines, secrets, stages, stageBuilds, templates, tokens, webhooks, and two that are single files rather than directories, stats and isAdmin. The coverage resource is marked optional.

The vocabulary is worth unpacking because it is not the vocabulary of most CI tools. A pipeline is a list of stages, and a stage is a list of jobs, and a job is one step that runs in an executor. A stageBuild and a build are the execution records. Collections and templates are the reusable pieces, secrets is the credential store, tokens the authentication tokens, and the webhooks and events resources are how the service is told that something happened elsewhere.

Underneath that there are three choices in each of three areas, and the README presents them as a set.

The datastores are Postgres, MySQL and Sqlite, through the sequelize adapter. The executors are Kubernetes, Docker and Nomad. The source control systems are GitHub, GitLab and Bitbucket.

The executor list is the interesting one, because it is what makes the platform portable. Running builds on Kubernetes is the serious case, and Docker is the local case, and Nomad is the outlier that shows the abstraction is genuinely there rather than Kubernetes with a compatibility layer.

## Running it: from source, from a container, in a box, or with Helm

There are four ways to get it running, and the README gives the commands for each.

From source, the sequence is clone, install, edit the config, start:

```bash
$ git clone git@github.com:screwdriver-cd/screwdriver.git ./
$ npm install
$ vim ./config/local.yaml # See below for configuration
$ npm start
info: Server running at http://localhost:8080
```

From a prebuilt Docker image:

```bash
$ vim ./local.yaml # See below for configuration
$ docker run --rm -it --volume=`pwd`/local.yaml:/config/local.yaml -p 8080 screwdrivercd/screwdriver:stable
info: Server running at http://localhost:8080
```

The image mounts your configuration file into the container at the path the service expects, which is the detail that makes the container route usable rather than a demo.

For evaluation there is the in-a-box script, which brings up an entire Screwdriver instance, meaning the user interface, the API and the log store, locally so you can play with it. The instructions live in a separate repository, screwdriver-cd/in-a-box.

And for production there is a Helm chart, in the screwdriver-chart repository, which bootstraps the whole Screwdriver ecosystem and also an nginx ingress controller. So the chart is not for the API alone, it is the deployment of the platform.

The prerequisites are short: Node v12.0.0 or higher, and either Kubernetes or Docker. That Node number is worth a second look, because the package manifest's `engines` field says `>=22.0.0`, and the container image is built `FROM node:22`. The README's prerequisite list is out of date relative to the manifest.

## Configuration: defaults, a YAML override, or environment variables

Screwdriver already defaults most of the configuration, in `config/default.yaml`, and you can override the defaults using a `local.yaml` or environment variables. The cluster management instructions are linked for the rest of the setup.

The YAML override is a nested structure that names the plugin and then its options:

```yaml
executor:
    plugin: k8s
    k8s:
        options:
            kubernetes:
                host: kubernetes.default
                token: this-is-a-real-token
            launchVersion: stable

scms:
    github:
        plugin: github
        config:
            oauthClientId: totally-real-client-id
            oauthClientSecret: another-real-client-secret
            secret: a-really-real-secret
            username: sd-buildbot
            email: dev-null@screwdriver.cd
```

The environment variable route is the same configuration with flattened names:

```bash
$ export K8S_HOST=127.0.0.1
$ export K8S_TOKEN=this-is-a-real-token
$ export SECRET_OAUTH_CLIENT_ID=totally-real-client-id
$ export SECRET_OAUTH_CLIENT_SECRET=another-real-client-secret
```

And every possible environment variable is defined in a file the README links, `config/custom-environment-variables.yaml`.

That file existing at all is a good sign for a containerised service, because it means the full configuration surface is enumerable rather than being scattered through the code as string literals. And the naming convention, a prefix per plugin area, means two plugins can both want a `token` setting without colliding.

## Testing: mocha for units, cucumber for behaviour

The unit tests run with `npm test`, and the package manifest shows what that means in practice: eslint runs first as a pretest step, then nyc produces coverage into `./artifacts/coverage` with an lcov report, and mocha runs the tests with a multi-reporter configuration, a ten second timeout, one retry and `--exit`.

The README warns about a practical problem. You might run into memory issues running all the unit tests, and the fix is a line in your `.bashrc`:

```bash
export NODE_OPTIONS=--max_old_space_size=4096
```

The functional tests are a different machine. You fork the `functional-*` repositories to your own organisation from the screwdriver-cd-test organisation, and add a `.func_config` file to the root of the API folder with your username, GitHub token, access key, host and organisation.

The config file is a set of key and value pairs, and the comments explain the defaults:

```
GIT_TOKEN=YOUR-GITHUB-TOKEN
SD_API_TOKEN=YOUR-SD-API-TOKEN
SD_API_HOST=YOUR-SD-API-HOST
SD_API_PROTOCOL=PROTOCOL-FOR-SD-API // e.g.PROTOCOL=http; by default it is https
TEST_ORG=YOUR-TEST-ORGANIZATION
TEST_USERNAME=YOUR-GITHUB-USERNAME
TEST_SCM_HOSTNAME=YOUR-TEST-SCM-HOSTNAME // e.g. TEST_SCM_HOSTNAME=mygithub.com; by default it is github.com
TEST_SCM_CONTEXT=YOUR-TEST-SCM-CONTEXT // e.g.TEST_SCM_CONTEXT=bitbucket; by default it is github
```

The same values can be given as environment variables instead, and then `npm run functional` runs the cucumber tests. The manifest shows that the functional suite has several profiles, prod, banner, beta, beta-parallel, beta-x1-parallel and dev, which tells you the functional tests are run against more than one environment and in more than one concurrency arrangement.

The distinction is sensible. The unit tests need nothing, and the functional tests need a real source control organisation, a real API and credentials, which is why they are forked per developer rather than shared.

## The version numbers, and where the 8.0.0 in the manifest comes from

The package manifest says the version is 8.0.0 for `screwdriver-api`, and the release list in the repository metadata stops at v4.1.0 from 2020-10-13, with v4.0.20 and v4.0.19 from the same day.

So the tag history and the published package are three major versions apart, and the tags stopped in 2020 while the last push was on 2026-10-15. The reason is in the release script: `semantic-release` runs in a pre mode, then publishes, then runs a post mode. The project publishes to npm from the repository with semantic-release driving the version and the changelog, and the GitHub releases are not the source of truth for what is installed.

That is a defensible arrangement for a project that treats npm as the distribution channel, and it is also the reason the tag list is a poor guide. If you are pinning a Screwdriver API version for a deployment, pin the npm version, not a git tag.

The Node requirement follows the same pattern of the README lagging the code. The manifest requires `>=22.0.0`, the container is built from `node:22` with npm 11.10, and the README's prerequisites still say Node v12.0.0 or higher.

The greenkeeper ignore list in the manifest is also interesting, because it names the plugin packages that are held back, and it shows that the plugins are published separately as their own npm packages, with the ones for docker, kubernetes and the datastore adapter visible in the truncated list.

## Where Screwdriver is the wrong choice

The first wrong case is a team that wants a CI service with a user interface. This repository is the API. The web interface is a separate project, and the in-a-box script pulls the UI, the API and the log store together, which is a hint about how many pieces a Screwdriver installation is.

The second is a team that wants the default CI tool of the platform they already run. If you are on GitHub Actions or on a managed CI, Screwdriver's value is that it is yours to extend, and if you do not need to extend it, the operational cost is not repaid.

The third is a team that wants fewer moving parts. Three datastores, three executors and three SCMs are options, but each one you enable is a plugin with its own configuration, its own failure modes and its own version. And the platform runs a queue, a log store, a user interface and the API, so the deployment is a cluster rather than a container.

The fourth is anyone reading the README as current documentation. The Node prerequisite is four major versions out of date, and the tag list stopped in 2020. The documentation is in `docs.screwdriver.cd` and in the guide repository, and the README points there for the parts that matter.

The fifth is a licensing question. The README says the code is licensed under the BSD 3-Clause license and points at the `LICENSE` file, while the licence field in the repository metadata is listed as a custom one that GitHub cannot classify. Read the file before you rely on either statement.

What the platform offers a team that has been fighting a CI tool is control. The executor and the SCM are plugins, the resources are plugins, and the design documents are in the repository as PlantUML sources that the `diagrams` script renders.

## Conclusion

Screwdriver suits a platform team that wants a CI/CD service it can extend from the inside, where the executor and the source control system are configuration choices rather than hard-coded assumptions, and that is prepared to run its own plugins. It does not suit a team that wants a hosted CI service with a web interface, since the user interface is a separate project, and it does not suit anyone expecting the current release to be a 4.x, because the published package version is 8.0.0 while the tag list stops at v4.1.0 from October 2020. Before adopting it, read the version numbers on npm and the releases page rather than assuming the tag list is current, and check the Node requirement, because the README says 12 or newer while the manifest says 22 or newer.

## FAQ

### What is Screwdriver?

It is a self-contained, pluggable service to build, test and continuously deliver software, and this repository holds its API server. Screwdriver began as a Jenkins interfacing hack at Yahoo in 2012 and was rebuilt from scratch in open source in 2016.

### Which datastores, executors and source control systems does Screwdriver support?

Three datastores, Postgres, MySQL and Sqlite through sequelize. Three executors, Kubernetes, Docker and Nomad. And three SCMs, GitHub, GitLab and Bitbucket. All are chosen by configuration.

### How do I run the Screwdriver API locally?

From source with `git clone`, `npm install`, editing `config/local.yaml` and `npm start`, or from the prebuilt image with `docker run --rm -it --volume=.../local.yaml:/config/local.yaml -p 8080 screwdrivercd/screwdriver:stable`. There is also an in-a-box script and a Helm chart.

### How do I run the Screwdriver functional tests?

Fork the `functional-*` repositories from the screwdriver-cd-test organisation, add a `.func_config` file with your tokens and organisation, then run `npm run functional`, which is `cucumber-js --profile prod`. The same values can be supplied as environment variables instead.

### Which Node version does the Screwdriver API need?

The package manifest requires Node 22 or newer and the container image is built from node:22, although the README's prerequisites still say Node v12.0.0 or higher. The published package version is 8.0.0 while the newest tagged release listed is v4.1.0 from 2020, because releases are driven by semantic-release publishing to npm.

## Sources

- [Issues](https://github.com/screwdriver-cd/screwdriver/issues)
- [Project website](http://screwdriver.cd)
- [README](https://github.com/screwdriver-cd/screwdriver/blob/master/README.md)
- [Releases](https://github.com/screwdriver-cd/screwdriver/releases)
- [screwdriver-cd/screwdriver on GitHub](https://github.com/screwdriver-cd/screwdriver)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/screwdriver-cd-screwdriver
