Kubernetes Deployments reference container images by string: a registry, a repository, and a tag or digest. That works everywhere. OpenShift adds ImageStreams and BuildConfigs so teams can name images inside the cluster, trigger builds from Git or Dockerfiles, and roll out when tags change — without every developer hand-pushing to an external registry first.
That platform layer helps until it does not. Some teams use OpenShift builds for everything. Others build in CI, push to Harbor or ECR, and only consume images on the cluster. Both patterns are valid. This post explains what ImageStreams and BuildConfigs do, how S2I differs from Dockerfile builds, and when to skip builds on the cluster entirely.
Why ImageStreams exist
On plain Kubernetes, if you change an image in a registry under the same tag, nothing happens until something pulls again or you restart Pods. Tags are mutable; clusters do not automatically notice.
An ImageStream is an OpenShift object that tracks one or more tags and points at actual image content — often in the internal OpenShift registry, sometimes at an external registry. Deployments and BuildConfigs can reference imagestream-name:tag instead of a long registry URL.
Benefits in practice:
- Stable names inside the project —
web:latestin YAML means the project’s ImageStream, not “whatever:latestmeans on Docker Hub today.” - Trigger rollouts — when a tag moves to new content, OpenShift can trigger DeploymentConfigs (legacy) or you can wire similar behavior with Deployments and image change triggers where configured.
- Promotion between environments — import or retag an ImageStreamTag from staging to production without rewriting application manifests.
ImageStreams are not a replacement for registries. They are an abstraction and tracking layer on top of registry storage.
Inspect what exists in a project:
oc get imagestream
oc describe imagestream web
oc get istag
The istag shorthand lists ImageStreamTags — the concrete tag entries you reference in manifests.
ImageStream basics
An ImageStream holds metadata and a history of tags. Each tag records which image ID (digest) it currently references.
Tags appear when a build completes or when you import an image:
apiVersion: image.openshift.io/v1
kind: ImageStream
metadata:
name: web
namespace: shop-staging
spec:
tags:
- name: "1.2.0"
from:
kind: DockerImage
name: "registry.example.com/shop/web:1.2.0"
importPolicy:
scheduled: true
referencePolicy:
type: Source
Note quoted tag names and image references where colons appear — YAML parsers treat colons carefully.
Import an external tag into the project’s ImageStream:
oc import-image web:1.2.0 --from="registry.example.com/shop/web:1.2.0" --confirm
oc tag registry.example.com/shop/web:1.2.0 web:latest --import
Deployments reference the ImageStream through the internal naming convention:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
namespace: shop-staging
spec:
replicas: 2
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: web
image: "image-registry.openshift-image-registry.svc:5000/shop-staging/web:latest"
ports:
- containerPort: 8080
In practice, oc new-app and BuildConfigs inject the correct pull spec. Hand-written YAML should match what oc describe deployment shows on a working cluster.
Check whether a tag resolved:
oc get imagestreamtag web:latest -o yaml
oc describe imagestreamtag web:latest
If the tag has no image, builds or imports failed — the Deployment will fail later with pull errors.
BuildConfig overview
A BuildConfig describes how to produce image content for an ImageStream tag: source location, strategy, output target, and triggers.
List and inspect:
oc get buildconfig
oc describe buildconfig web
oc get builds
oc logs -f build/web-3
Each Build is one execution of a BuildConfig — like a Job for image production.
Typical BuildConfig shape:
apiVersion: build.openshift.io/v1
kind: BuildConfig
metadata:
name: web
namespace: shop-staging
spec:
source:
type: Git
git:
uri: "https://github.com/example/shop-web.git"
ref: main
strategy:
type: Source
sourceStrategy:
from:
kind: ImageStreamTag
name: "nodejs-20:latest"
namespace: openshift
env:
- name: NPM_CONFIG_LOGLEVEL
value: warn
output:
to:
kind: ImageStreamTag
name: "web:latest"
triggers:
- type: ConfigChange
- type: GitHub
github:
secretReference:
name: web-github-secret
Important fields:
source— Git, binary, or Dockerfile context.strategy— Source (S2I), Docker, or Custom.output.to— which ImageStreamTag receives the built image.triggers— rebuild on Git push, image change, or config change.
Start a build manually:
oc start-build web
oc start-build web --from-dir=.
oc start-build web --commit main
Watch until success or failure before expecting Deployments to roll:
oc logs -f build/web-4
oc describe build web-4
Failed builds leave old tags in place. That is safer than deploying broken images — but confusing if you assume a git push already updated the cluster.
S2I vs Dockerfile builds
OpenShift supports multiple build strategies. The two you see most often are Source-to-Image (S2I) and Docker.
S2I (Source strategy) — OpenShift injects source into a builder image that knows how to assemble a runtime image (Node, Python, Java, etc.). You supply code and maybe a few environment variables; the platform applies conventions.
When S2I fits:
- The team wants standard, maintained builder images from the
openshiftnamespace. - Applications follow framework defaults without exotic OS packages.
- Operators prefer not to maintain Dockerfiles for every microservice.
Example S2I BuildConfig strategy section:
strategy:
type: Source
sourceStrategy:
from:
kind: ImageStreamTag
name: "python-311:latest"
namespace: openshift
env:
- name: FLASK_APP
value: app.py
Docker strategy — OpenShift runs docker build (or equivalent) against a Dockerfile in the Git context or uploaded directory.
When Dockerfile builds fit:
- The app needs custom base layers, packages, or multi-stage builds the stock builders do not cover.
- Security or compliance requires a reviewed Dockerfile as the contract.
- The same Dockerfile already builds in CI and you want identical behavior on the cluster.
Example Docker strategy section:
strategy:
type: Docker
dockerStrategy:
dockerfilePath: Dockerfile
buildArgs:
- name: HTTP_PROXY
value: "http://proxy.example.com:8080"
Compare quickly on a running cluster:
oc describe buildconfig web | grep -A5 "Strategy"
oc get build web-5 -o yaml | grep -A10 strategy
Neither strategy is “more OpenShift.” S2I trades flexibility for speed and consistency. Dockerfile trades convention for control. Many platforms use Dockerfile in CI, import in cluster and skip both on OpenShift.
When a build fails, read oc describe build and oc logs build/... before chasing Deployment ImagePullBackOff. Missing Git secrets, absent builder ImageStreamTags in openshift, wrong Dockerfile paths, and OOM during compile steps are the usual suspects.
When to skip in-cluster builds
Not every OpenShift deployment needs BuildConfigs. Skip cluster builds when:
- CI already builds, scans, signs, and pushes images to a registry the cluster trusts.
- Build reproducibility must match a pipeline outside OpenShift — one source of truth for tags and digests.
- Resource cost — compiles on cluster nodes compete with application workloads; large monorepos amplify that.
- Compliance — only approved pipelines may produce release artifacts.
The alternative pattern:
- CI pushes
registry.example.com/shop/web:1.2.0. - OpenShift imports or tags that image into an ImageStream (optional but useful for stable in-cluster names).
- Deployment references the ImageStream tag or the external digest directly.
Import without building:
oc tag registry.example.com/shop/web:1.2.0 web:1.2.0 --import
oc set image deployment/web web=web:1.2.0
Or reference the external image with digest for immutability:
containers:
- name: web
image: "registry.example.com/shop/web@sha256:abc123def456..."
ImageStreams still help as documentation and promotion handles even when no BuildConfig exists. They are not only build outputs.
Pulling from an external registry requires pull secrets on the right service account:
oc create secret docker-registry regcred \
--docker-server=registry.example.com \
--docker-username=puller \
--docker-password='TOKEN' \
--docker-email=ops@example.com
oc secrets link default regcred --for=pull
ImagePullBackOff after a successful CI build usually means a missing pull secret — not a bad Dockerfile. Standard frameworks often suit S2I; custom OS layers suit Dockerfile or CI. Mature pipelines often push to Harbor or ECR and import tags into ImageStreams for stable in-cluster names.
Final thought
ImageStreams and BuildConfigs are OpenShift’s way of naming, building, and tracking images inside a project. They shine when teams want Git push to image to rollout without assembling a separate pipeline first. They are optional when a mature CI system already owns build, scan, and push. Understanding both paths — build on cluster versus import from outside — keeps you from fighting the platform or duplicating work. Start with one service, one ImageStream tag, and one clear owner for who produces the image. The rest of the model scales from there.