Developer Tools Azure

Screwdriver CI/CD on Ubuntu 24.04 on Azure User Guide

| Product: Screwdriver CI/CD on Ubuntu 24.04 LTS on Azure

Overview

Screwdriver is an open-source build and continuous-delivery (CI/CD) platform, originally built at Yahoo. Pipelines are defined as code in your repository, every build step runs in an isolated container, and Screwdriver integrates with your source-control provider so commits and pull requests trigger builds automatically. The cloudimg image runs the complete single-node stack behind one nginx reverse proxy on port 80: the API, the web UI, an artifact and log store, a PostgreSQL datastore, and the Docker build executor ready to launch build containers. Backed by 24/7 cloudimg support.

What is included:

  • Screwdriver API (screwdrivercd/screwdriver:v8.0.174), web UI (screwdrivercd/ui:v1.0.1416) and artifact/log store (screwdrivercd/store:v7.0.3), run as a container stack and baked into the image (no runtime pull)
  • The Docker build executor with the build launcher (screwdrivercd/launcher:v6.0.237) baked in - build steps run in isolated containers on the host Docker engine
  • A PostgreSQL 16 datastore whose schema is created automatically on first boot
  • nginx on :80 as the single exposed surface, path-routing / to the UI, /v4 to the API and /v1 to the store - the containers bind the loopback interface only
  • A per-VM RSA JWT signing keypair and fresh secrets generated on first boot - no default login and no baked credential ship in the image
  • docker.service, screwdriver.service and nginx.service as systemd units, enabled and active; ufw default-deny with only 22 and 80 open
  • 24/7 cloudimg support

Screwdriver is distributed under the BSD-3-Clause licence.

Secure by default - per-VM secrets, no default login

This image ships with no default or shared login and no baked secret. On first boot a one-shot service generates a per-VM RSA-2048 JWT signing keypair, fresh cookie/encryption/hashing passwords and a fresh PostgreSQL password, resolves your VM's public address, renders the stack configuration, brings the services up and proves the artifact store's signed round-trip before writing a root-only information file. Screwdriver authenticates users through your own source-control provider (see Step 6), so there is no built-in administrator password to leak; the only signing key that can write to the store is the per-VM private key generated on your machine.

Screwdriver first-boot: per-VM secrets, the signed store round-trip, and the firewall posture

Prerequisites

An active Azure subscription, an SSH key pair, and a VNet + subnet in the target region. Standard_B2s (2 vCPU / 4 GiB RAM) is a comfortable starting point for the control plane; teams running heavy or concurrent pipelines (each build spawns its own container) should choose a larger size. NSG inbound: allow 22/tcp from your management network and 80/tcp from wherever you browse Screwdriver. You will also need an OAuth application registered with your source-control provider (GitHub, GitLab or Bitbucket) to sign in - covered in Step 6.

Step 1 - Deploy from the Azure Marketplace

Sign in to the Azure Portal, choose Create a resource, search the Marketplace for Screwdriver by cloudimg, and select Create. On Basics pick your subscription, resource group, region and size; under Administrator account choose SSH public key and paste your key; under Inbound port rules allow SSH (22) and HTTP (80). Then Review + create -> Create.

Step 2 - Deploy from the Azure CLI

az vm create \
  --resource-group <your-rg> \
  --name screwdriver \
  --image <marketplace-image-urn> \
  --size Standard_B2s \
  --admin-username azureuser \
  --ssh-key-values ~/.ssh/id_ed25519.pub \
  --vnet-name <your-vnet> --subnet <your-subnet> \
  --public-ip-sku Standard

az vm open-port --resource-group <your-rg> --name screwdriver --port 80 --priority 1010

Step 3 - Connect to your VM

ssh azureuser@<vm-public-ip>

On first boot the stack takes a minute or two to become healthy (it generates per-VM secrets and initialises the database). The information file records the URL your Screwdriver instance is served on:

sudo grep -E '^SCREWDRIVER_URL=' /root/screwdriver-credentials.txt

Step 4 - Verify the stack is healthy

Confirm the systemd units are active and the API, UI and store all answer through nginx:

systemctl is-active docker.service screwdriver.service nginx.service
curl -s -o /dev/null -w 'API   /v4/status -> HTTP %{http_code}\n' http://127.0.0.1/v4/status
curl -s -o /dev/null -w 'UI    /          -> HTTP %{http_code}\n' http://127.0.0.1/
curl -s -o /dev/null -w 'store /v1/status -> HTTP %{http_code}\n' http://127.0.0.1/v1/status

All three endpoints return HTTP 200, and the API also reports the running component versions:

curl -s http://127.0.0.1/v4/versions | head -c 300; echo

Screwdriver service health: the API, UI and store all answer HTTP 200

The four containers (API, UI, store and PostgreSQL) run under one systemd unit, and only nginx is exposed - the containers bind the loopback interface:

The four-container Screwdriver stack and the loopback + nginx binding posture

Step 5 - Open the web console

Browse to http://<vm-public-ip>/ (the SCREWDRIVER_URL from Step 3). The Screwdriver web console loads and prompts you to sign in through your source-control provider - Screwdriver uses your SCM to handle user authentication and to apply the same repository permissions to your pipelines.

The Screwdriver web console with source-control sign-in

Step 6 - Connect your source-control provider

Screwdriver signs users in and reads pipelines through an SCM OAuth application that you own. Register an OAuth app with your provider (for example, on GitHub: Settings -> Developer settings -> OAuth Apps -> New OAuth App), setting the authorization callback URL to http://<vm-public-ip>/v4/auth/login/github:github.com/web. Then edit the SCM_SETTINGS block in the stack configuration to replace the placeholder client id and secret with your OAuth app's credentials, and restart the stack:

sudo nano /opt/screwdriver/docker-compose.yml
# in the api service, set SCM_SETTINGS' oauthClientId / oauthClientSecret / secret
sudo docker compose -f /opt/screwdriver/docker-compose.yml up -d

For production use also set SECRET_WHITELIST to the list of SCM users or organisations allowed to sign in, and front nginx with TLS (for example with your own certificate or a reverse proxy) so tokens are never sent in clear text. After the restart, click Sign in with SCM Provider in the console and authorize the app.

Step 7 - Pipelines and the build API

Once signed in, you create a pipeline by pointing Screwdriver at a repository that contains a screwdriver.yaml; each push then runs the pipeline's jobs as isolated build containers via the Docker executor, with logs and artifacts stored in the built-in store. The full pipeline, job, build and event surface is available through the Screwdriver REST API, which the image serves with live documentation at http://<vm-public-ip>/v4/documentation:

curl -s -o /dev/null -w 'API docs -> HTTP %{http_code}\n' http://127.0.0.1/v4/documentation

The live Screwdriver API documentation

The Screwdriver pipeline and build REST API

The artifact store is authenticated with JWTs signed by the per-VM key. You can prove the signing, verification and storage path end to end with the bundled round-trip check, which mints a build token with the per-VM private key, writes an artifact (create) and reads it back (read), and confirms an unsigned token is rejected:

sudo /usr/local/sbin/screwdriver-store-roundtrip.sh

It prints OK store round-trip: PUT=202 GET=match badtoken=401.

Step 8 - Maintenance

The operating system ships fully patched with unattended security upgrades enabled, and the Docker build launcher is baked in so the executor is ready without a runtime pull:

echo "held=[$(apt-mark showhold)]"; systemctl is-enabled unattended-upgrades.service
free -m | head -2

OS patch posture and the baked-in Docker executor launcher

Manage the stack with systemd and Docker Compose:

sudo systemctl status screwdriver nginx docker
sudo docker compose -f /opt/screwdriver/docker-compose.yml ps
sudo docker compose -f /opt/screwdriver/docker-compose.yml logs --tail 100 api

The PostgreSQL data and stored artifacts live under /opt/screwdriver/data; back this directory up to preserve your pipelines and build history. Because the public URL is baked into the configuration on first boot, assign your VM a static public IP or a DNS name before going to production so the console and API URLs stay stable across restarts.

Support

Every cloudimg image is backed by 24/7 support. If you have any questions about this image, contact us at support@cloudimg.co.uk.