Table of Contents
Concept and Workflow of S2I
Source-to-Image (S2I) is an OpenShift build mechanism that turns application source code into a runnable container image without requiring you to write a Dockerfile. It is designed to:
- Standardize how applications are built
- Separate “how to build” (build logic and runtime) from “what to build” (your source code)
- Integrate tightly with OpenShift builds, image streams, and deployment workflows
At a high level, S2I:
- Starts from a builder image that knows how to build and run a specific type of application (e.g., Python, Node.js, Java).
- Adds your source code (e.g., from Git) into that builder image.
- Runs a build process inside the container to produce your application artifact (e.g., compiled binaries, JAR/WAR files, static assets).
- Produces a new runtime image that contains both the runtime environment and your built application.
Typical S2I flow in OpenShift:
- User input: You provide a Git repo URL (or local source) and pick a builder image.
- OpenShift build: OpenShift starts a build pod using the builder image.
- Build scripts: The builder image runs S2I scripts (
assemble, optional tests, etc.). - Resulting image: A new image is created and stored in an image registry or image stream.
- Deployment: OpenShift uses that image in a
Deployment/DeploymentConfigto run pods.
S2I Builder Images
An S2I builder image is a container image prepared to work with S2I. It contains:
- A runtime (e.g., Node.js, Python, OpenJDK, PHP, Go, .NET)
- Build tools (e.g.,
maven,npm,pip,gradle) - A set of S2I scripts in a known location
- Conventions for where code and artifacts are placed
OpenShift provides many official builder images, typically published by Red Hat and exposed in the OpenShift web console as “QuickStarts” or “builder” images. Examples:
registry.redhat.io/rhscl/nodejs-XX-rhel7registry.redhat.io/rhscl/python-XX-rhel7registry.redhat.io/openjdk/openjdk-11-rhel8
When you choose a builder image, you effectively choose:
- The language/runtime version
- The build toolchain used
- The conventions and environment variables available to your application
You can also create custom builder images when the built-in ones do not match your requirements (e.g., special native dependencies, specific build tool versions, custom test frameworks).
S2I Scripts and Their Roles
The core of S2I behavior is defined by a small set of scripts inside the builder image. They are typically located under /usr/libexec/s2i or /usr/local/s2i.
The standard S2I scripts are:
assemble- Required.
- Performs the build.
- Copies your source into the image, installs dependencies, compiles code, and prepares the runtime environment.
run- Required.
- Defines how the resulting image starts the application container (e.g.,
npm start,python app.py,java -jar app.jar). save-artifacts- Optional (used in incremental builds).
- Extracts and stores build artifacts from an existing image (e.g., downloaded dependencies, compiled modules).
usage- Optional (informational).
- Prints help or usage information describing how to use the builder image.
Typical responsibilities of assemble:
- Create the application directory (e.g.,
/opt/app-root/src) - Copy source code into that directory
- Detect project type (e.g.,
package.jsonvsrequirements.txt) - Install dependencies (
npm install,pip install,mvn package, etc.) - Run build steps (e.g.,
ng build,mvn -DskipTests=false package)
Typical responsibilities of run:
- Set default environment variables
- Possibly perform final runtime adjustments
- Execute the app’s entrypoint command
Creating and Using S2I Builds in OpenShift
OpenShift exposes S2I through BuildConfigs and the web console. While details of BuildConfigs are handled in other chapters, here is how S2I fits into actual usage.
Creating an S2I application from the web console
A typical workflow:
- In the OpenShift web console, choose “Create” or “+Add”.
- Select “From Git” (or similar).
- Enter:
- Git repository URL
- (Optional) Git reference (branch/tag) and context directory
- Choose a builder image (e.g., Node.js, Python, Java).
- Configure:
- Application name
- Resource limits, environment variables (as needed)
- Confirm to create:
- A
BuildConfigof typeSource - An
ImageStreamfor the built image - A
Deployment/DeploymentConfigand aService(and possibly aRoute)
OpenShift then:
- Starts an S2I build pod
- Produces a new application image
- Triggers a deployment using that image
Creating an S2I build with `oc new-app`
From the CLI, S2I-based applications are typically created with oc new-app. Basic patterns:
- From Git and a builder image:
oc new-app registry.redhat.io/rhscl/nodejs-14-rhel7~https://github.com/example/my-node-app.git- Let OpenShift detect the builder image automatically:
oc new-app https://github.com/example/my-python-app.gitIn both cases, OpenShift:
- Detects an S2I-capable builder image
- Creates the appropriate BuildConfig and ImageStream
- Kicks off an initial S2I build
- Sets up a deployment that watches the resulting image
To view the build:
oc get builds
oc logs build/<build-name>To start a new build manually:
oc start-build <buildconfig-name> --followIncremental Builds with S2I
Incremental builds allow S2I to reuse artifacts from a previous build image (e.g., downloaded dependencies) to speed up subsequent builds. This is useful for:
- Large dependency trees (e.g., Maven projects, large
npminstalls) - Rebuilds where most dependencies remain unchanged
Key aspects:
- The builder image must implement
save-artifacts. - This script collects cached assets (e.g.,
.m2/repository,node_modules) and makes them available to the next build. - The BuildConfig must be configured for incremental builds.
Example BuildConfig excerpt for enabling incremental builds (conceptually):
strategy:
type: Source
sourceStrategy:
from:
kind: ImageStreamTag
name: my-builder-image:latest
incremental: trueWith incremental builds enabled:
- The S2I build runs
save-artifactsagainst the previous build image. - The results are injected into the new build container.
assembleuses those artifacts, avoiding full dependency downloads/compiles.
Incremental builds improve build times but:
- Increase complexity in custom builder image design.
- Require careful management of cached artifacts to avoid bloated images.
Customizing S2I Builds
You can adjust S2I behavior without modifying the builder image by:
- Setting environment variables
- Providing specific files in your source repository
- Using BuildConfig configuration
Some common customization mechanisms:
Environment variables
Many official builder images support environment variables to tweak behavior:
MAVEN_MIRROR_URLfor Java buildsNPM_RUNfor Node.js (override defaultnpm runscript)APP_FILE,APP_MODULEfor Python entrypointsHTTP_PROXY,HTTPS_PROXY,NO_PROXYfor network configuration
You can define env vars:
- In the
BuildConfig(spec.strategy.sourceStrategy.env) - In the deployment (for runtime-only settings)
- Via
oc new-app -e KEY=VALUE
Example:
oc new-app \
registry.redhat.io/openjdk/openjdk-11-rhel8~https://github.com/example/my-java-app \
-e MAVEN_MIRROR_URL=http://my-maven-mirror:8081/repository/maven-publicUsing configuration files in your repo
Many S2I builder images look for specific files in your source tree:
requirements.txt,Pipfilefor Pythonpackage.jsonfor Node.jspom.xml,build.gradlefor Javanginx.confor similar for web front-end builders
Placing these files correctly influences:
- How dependencies are installed
- How the app is built and started
Overriding S2I scripts
If you need more control, you can override S2I scripts without rebuilding the builder image:
- Place custom scripts under
.s2i/binin your repo: .s2i/bin/assemble.s2i/bin/run.s2i/bin/save-artifacts- S2I will prefer these over the builder image’s default scripts.
This approach lets you:
- Add custom pre/post build steps
- Change how artifacts are assembled
- Adjust the runtime startup behavior specific to your application
S2I vs Dockerfile-Based Builds (in Practice)
While the detailed comparison of build strategies belongs elsewhere, some S2I-specific aspects are important when choosing between S2I and Dockerfile builds:
S2I strengths:
- No need to maintain a
Dockerfilefor typical language stacks. - Encourages standardized, repeatable build patterns.
- Builder images can be centrally maintained and hardened by platform teams.
- Integration with incremental builds can significantly speed up rebuilds.
- Easier onboarding: developers mainly focus on source and ecosystem-standard files.
Typical use cases where S2I is a good fit:
- Standard web and API services (Node.js, Python, Java, etc.)
- Teams that want a consistent build pipeline across many apps
- Scenarios where platform teams provide approved, secured builder images
Cases where Dockerfile-based builds might be preferred:
- Very custom runtimes or multi-stage builds that S2I patterns don’t fit well
- Highly specialized system dependencies or non-standard OS-level changes
Designing Custom S2I Builder Images
When the built-in builder images are not enough, you can build custom S2I builder images tailored to your environment. Designing one involves:
- Start from a base image
- Choose a minimal OS image approved by your organization.
- Install language runtime and build tools.
- Install the S2I scripts
- Install the S2I scripts package (e.g.,
s2itooling) or copy scripts into/usr/libexec/s2ior similar. - Implement:
assemblerunsave-artifacts(if you want incremental support)usage(optional)- Define conventions
- Decide where source code will live (e.g.,
/opt/app-root/src). - Define where caches and artifacts are stored (e.g.,
/opt/app-root/.cache). - Set environment variables that apps can rely on (
APP_ROOT,HOME, etc.). - Document usage
- Clearly document:
- Required env vars
- Expected project structure
- Supported frameworks or build tools
- Make this documentation available via
usageand internal docs. - Publish the image
- Push to your internal registry.
- Optionally expose it as an ImageStream in OpenShift.
- Optionally integrate it into the OpenShift web console catalog.
This pattern allows platform teams to standardize how specific technologies (e.g., internal frameworks, data science stacks, HPC toolchains) are built in a secure, repeatable way.
Integrating S2I with the Deployment Lifecycle
In OpenShift, S2I is tightly integrated with the application lifecycle:
- A BuildConfig of type
Source(S2I) feeds into an ImageStream. - The ImageStream is used by a
DeploymentorDeploymentConfig. - New builds automatically produce new image tags (e.g.,
:latest). - Those tags can automatically trigger new deployments.
Key triggers you can use with S2I:
- Source changes: Pushing to your Git repo can trigger a new S2I build (e.g., via webhooks).
- Config changes: Modifying the BuildConfig can trigger new builds.
- Base image changes: When the builder image is rebuilt (e.g., security updates), S2I can rebuild all dependent applications via image change triggers.
This linkage makes S2I a foundational piece for:
- Automated application rebuilds on base image updates
- CI/CD pipelines that only need to manage source changes
- Consistent promotion from development to production using image tags
Practical Tips and Common Pitfalls with S2I
Some practical considerations when using S2I:
- Keep builds deterministic
- Pin dependency versions where possible.
- Avoid build steps that rely on external mutable state unless tightly controlled.
- Manage build time and image size
- Use incremental builds when beneficial, but monitor image growth.
- Clean up unnecessary build artifacts in
assembleif not needed at runtime. - Separate build-time and runtime concerns
- Use environment variables and configuration files to avoid baking environment-specific settings into the image.
- Keep application configuration out of the image where possible; rely on ConfigMaps/Secrets for that.
- Monitor build logs
- Treat S2I builds as you would CI builds: inspect logs, fail fast on errors, and add tests where appropriate (some builder images support running tests during
assemble). - Understand builder image updates
- When a builder image is updated (e.g., for CVEs), applications using S2I can be automatically rebuilt. Ensure you have processes to validate rebuilt images before promotion to production.
Using S2I effectively means leveraging these conventions and tools so that developers can focus on writing code, while OpenShift and the platform team handle the complexity of building, packaging, and running containerized applications.