Skip to content

Container Builds with Shipwright

Brokkr integrates with Shipwright Build to provide native container image building capabilities. This guide covers installation, configuration, and usage of Shipwright builds through Brokkr’s work order system.

Shipwright Build is a CNCF Sandbox project that provides a framework for building container images on Kubernetes. Brokkr uses Shipwright as the execution engine for build work orders, allowing you to:

  • Build container images from Git repositories
  • Push images to container registries
  • Leverage production-ready build strategies (buildah, kaniko)
  • Manage builds through Brokkr’s work order API

Shipwright integration requires Kubernetes 1.29 or later due to dependencies on newer API features.

Terminal window
# Verify your Kubernetes version
kubectl version
  • Sufficient resources for build pods (recommended: 4GB memory, 2 CPU cores available)
  • Network access to container registries you’ll push to
  • Network access to Git repositories you’ll build from
Section titled “Option 1: Chart-Managed Installation (Recommended)”

Enabled by default. Nothing is vendored into the chart: on every helm install and every helm upgrade, a pre-install hook Job downloads the pinned upstream release manifests and kubectl applys them.

Terminal window
# Install agent with chart-managed Shipwright (default)
helm install brokkr-agent oci://ghcr.io/colliery-io/charts/brokkr-agent \
--set broker.url=http://brokkr-broker:3000 \
--set broker.existingSecret=brokkr-agent-credentials \
--wait

This installs:

  • Tekton Pipelines (v0.68.1) - Task execution engine
  • Shipwright Build (v0.18.1) - Build orchestration
  • Sample ClusterBuildStrategies (buildah, kaniko, etc.)
  • A webhook TLS certificate for Shipwright — self-signed by the Job by default, or issued by cert-manager when shipwright.certManagement=cert-manager

The versions are pinned in the chart’s values (shipwright.install.tektonVersion and shipwright.install.shipwrightVersion) and are the versions the integration is tested against. Override them there if you need different releases.

Before you enable it, know what the hook does

Section titled “Before you enable it, know what the hook does”

The installer Job is not a scoped add-on. Two consequences are worth deciding on deliberately:

  • It needs internet egress from the cluster. The Job fetches release manifests from storage.googleapis.com and github.com at install time. In an air-gapped or egress-filtered cluster it will hang and then fail on activeDeadlineSeconds. Use Option 2 there.
  • It runs as cluster-admin. Installing CRDs, cluster-scoped controllers, and namespaces requires it, so the chart creates a ServiceAccount bound to cluster-admin for the duration of the hook. It also mutates cluster-wide state outside the release namespace — the tekton-pipelines and shipwright-build namespaces, four shipwright.io CRDs, and the cluster build strategies — none of which Helm will remove when you uninstall the agent.

If either is unacceptable in your cluster, install Shipwright out-of-band and use Option 2.

If you already have Shipwright and Tekton installed, need specific versions, or cannot grant the installer Job cluster-admin and internet egress, turn the installation off but keep shipwright.enabled=true — that flag also gates the agent’s RBAC for shipwright.io and tekton.dev resources, which build work orders require. With both install.* flags false the hook Job, its ServiceAccount, and its cluster-admin binding are not rendered at all:

Terminal window
# Skip the installer job, keep Shipwright integration (and RBAC) enabled
helm install brokkr-agent oci://ghcr.io/colliery-io/charts/brokkr-agent \
--set broker.url=http://brokkr-broker:3000 \
--set broker.existingSecret=brokkr-agent-credentials \
--set shipwright.install.tekton=false \
--set shipwright.install.shipwright=false \
--wait

Set shipwright.enabled=false only if you are not using build work orders at all.

If installing Shipwright manually, prefer the versions the chart pins (Tekton v0.68.1, Shipwright v0.18.1) — older releases may work but are not what Brokkr is tested against. These are the same manifests the hook Job applies, so running them yourself from a mirror is the supported air-gapped path:

Terminal window
# Install Tekton Pipelines
kubectl apply -f https://storage.googleapis.com/tekton-releases/pipeline/previous/v0.68.1/release.yaml
# Wait for Tekton to be ready
kubectl wait --for=condition=Ready pods -l app=tekton-pipelines-controller -n tekton-pipelines --timeout=300s
# Install Shipwright Build
kubectl apply -f https://github.com/shipwright-io/build/releases/download/v0.18.1/release.yaml
# Wait for Shipwright to be ready
kubectl wait --for=condition=Ready pods -l app=shipwright-build-controller -n shipwright-build --timeout=300s
# Install sample build strategies
kubectl apply -f https://github.com/shipwright-io/build/releases/download/v0.18.1/sample-strategies.yaml
Terminal window
# Verify Tekton Pipelines
kubectl get pods -n tekton-pipelines
# Expected: tekton-pipelines-controller and tekton-pipelines-webhook Running
# Verify Shipwright Build
kubectl get pods -n shipwright-build
# Expected: shipwright-build-controller Running
# Verify ClusterBuildStrategies
kubectl get clusterbuildstrategies
# Expected: buildah (and others if sample strategies installed)

Create a simple test build to verify the installation:

# test-build.yaml
apiVersion: shipwright.io/v1beta1
kind: Build
metadata:
name: test-build
spec:
source:
type: Git
git:
url: https://github.com/shipwright-io/sample-go
strategy:
name: buildah
kind: ClusterBuildStrategy
output:
image: ttl.sh/brokkr-test-$(date +%s):1h
---
apiVersion: shipwright.io/v1beta1
kind: BuildRun
metadata:
generateName: test-build-run-
spec:
build:
name: test-build
Terminal window
# Apply the test build
kubectl apply -f test-build.yaml
# Watch the build progress
kubectl get buildruns -w
# Check build logs
kubectl logs -l buildrun.shipwright.io/name=test-build-run-xxxxx -c step-build

The chart-managed installation includes the official sample ClusterBuildStrategies (buildah, kaniko, buildpacks, etc.) by default via shipwright.install.sampleStrategies:

Terminal window
# Sample strategies are installed by default; re-enable explicitly if needed
helm upgrade brokkr-agent oci://ghcr.io/colliery-io/charts/brokkr-agent \
--reuse-values \
--set shipwright.install.sampleStrategies=true

If you only want Shipwright without sample strategies:

Terminal window
helm install brokkr-agent oci://ghcr.io/colliery-io/charts/brokkr-agent \
--set broker.url=http://brokkr-broker:3000 \
--set broker.existingSecret=brokkr-agent-credentials \
--set shipwright.enabled=true \
--set shipwright.install.sampleStrategies=false

Build work orders require the agent to manage shipwright.io and tekton.dev resources. The brokkr-agent chart includes these RBAC rules in the agent ClusterRole whenever shipwright.enabled=true (even when the installer Job is skipped):

# Automatically included when shipwright.enabled=true
- apiGroups: ["shipwright.io"]
resources: ["*"]
verbs: ["*"]
- apiGroups: ["tekton.dev"]
resources: ["*"]
verbs: ["*"]

If you disable shipwright.enabled, the agent loses these permissions and build work orders will fail.

To push images to private registries, create a Kubernetes secret:

Terminal window
# Create registry credentials secret
kubectl create secret docker-registry registry-creds \
--docker-server=ghcr.io \
--docker-username=<username> \
--docker-password=<token> \
--docker-email=<email>

Reference this secret in your Build spec:

apiVersion: shipwright.io/v1beta1
kind: Build
metadata:
name: my-build
spec:
source:
type: Git
git:
url: https://github.com/org/repo
strategy:
name: buildah
kind: ClusterBuildStrategy
output:
image: ghcr.io/org/my-image:latest
pushSecret: registry-creds # Reference your secret here

For private Git repositories:

Terminal window
# Create Git credentials secret (HTTPS)
kubectl create secret generic git-creds \
--from-literal=username=<username> \
--from-literal=password=<token>
# Or for SSH
kubectl create secret generic git-ssh-creds \
--from-file=ssh-privatekey=/path/to/id_rsa

Reference in your Build:

spec:
source:
type: Git
git:
url: https://github.com/org/private-repo
cloneSecret: git-creds

Builds run through Brokkr’s work order system. Create a work order with work_type: "build" and the Shipwright Build YAML in yaml_content, targeting agents by ID, label, or annotation:

Terminal window
curl -X POST "http://broker:3000/api/v1/work-orders" \
-H "Authorization: Bearer $ADMIN_PAK" \
-H "Content-Type: application/json" \
-d '{
"work_type": "build",
"yaml_content": "apiVersion: shipwright.io/v1beta1\nkind: Build\nmetadata:\n name: my-build\nspec:\n source:\n type: Git\n git:\n url: https://github.com/org/repo\n strategy:\n name: buildah\n kind: ClusterBuildStrategy\n output:\n image: ghcr.io/org/my-image:latest\n pushSecret: registry-creds",
"targeting": {
"labels": ["builder"]
}
}'

A matching agent claims the work order, applies the Build, creates a BuildRun, and watches it to completion. The watch lasts at most 15 minutes, and less when the work order has a short claim_timeout_seconds. On success, the work order completes with the image digest as its result message; on failure, the error details are recorded instead. If the BuildRun is still running at the end of the watch, the work order fails without a retry. The agent does not cancel the BuildRun, so it can still finish in the cluster. Completed work orders move to the work-order log:

Terminal window
# View completed builds (the message field carries the image digest)
curl "http://broker:3000/api/v1/work-order-log?work_type=build" \
-H "Authorization: Bearer $ADMIN_PAK"

See the Work Orders Reference for the full request schema, targeting semantics, and retry behavior.

The pre-install hook Job is blocking the release. Read its logs before anything else — a stalled manifest download (no cluster egress) and a rejected kubectl apply (installer ServiceAccount missing its cluster-admin binding) look identical from Helm’s side:

Terminal window
kubectl logs -l app.kubernetes.io/component=shipwright-installer --tail=100

The Job self-deletes shortly after success, so an empty result on a healthy install is expected.

Terminal window
# Check for pending pods
kubectl get pods -l build.shipwright.io/name
# Check events
kubectl get events --sort-by='.lastTimestamp'
# Verify ServiceAccount has required permissions
kubectl auth can-i create pods --as=system:serviceaccount:default:default
Terminal window
# Check controller logs
kubectl logs -n shipwright-build -l app=shipwright-build-controller
# Check for CRD installation
kubectl get crd builds.shipwright.io buildruns.shipwright.io
Terminal window
# Check Tekton controller logs
kubectl logs -n tekton-pipelines -l app=tekton-pipelines-controller
# Check TaskRun status
kubectl get taskruns
kubectl describe taskrun <taskrun-name>