This document describes the design principles behind the S2I OpenStack container images. For repository structure, build commands, and contributor workflows, see the developer guide.
The full upstream design document, covering the broader container-images vision including CI integration, operator workflows, and tested-tag promotion, lives at dev-docs/container-images-design.md. This document focuses on the Containerfile design principles and build-time concerns specific to this repository.
-
Source-based builds -- Build OpenStack services directly from upstream git repositories rather than pre-built packages. This allows building from any combination of branches, tags, or in-review patches for the main service, its plugins, and its dependencies.
-
Kolla-compatible runtime interface -- The kolla entrypoint, configuration, and user management scripts are available in the base image but their usage is optional. Services can use them for compatibility with existing openstack-k8s-operators deployments or provide their own entrypoint and configuration files in the expected locations.
-
Flat image hierarchy -- A single
openstack-baseimage, with all service images built directly on top. No intermediate per-project layers. -
Smart consolidation -- Services sharing the same Python package and similar non-Python dependencies share a single image. Services with significantly different system dependencies get separate images.
-
Multi-stage builds -- Build tools, compilers, and source code are confined to the build stage and discarded. The final image contains only runtime dependencies.
-
Reproducibility -- Every source repo is pinned to a specific commit hash. Upper-constraints are pinned to a specific commit. Dependency lockfiles are generated with
pip-compileand committed. Builds from the same inputs produce the same dependency set. -
CI integration -- The repository can be easily integrated into CI workflows to validate changes both in the openstack-k8s-operators and in third-party jobs for upstream OpenStack repositories.
-
Full source build support -- Containerfiles accept a
PIP_NO_BINARYbuild argument so that all Python dependencies can be compiled from source rather than installed from pre-built wheels. This is not the default but enables use cases that require building every dependency from source (e.g., for license compliance or platform-specific optimizations). SetPIP_NO_BINARY=:all:at build time to activate this mode.
The openstack-base image is a single-stage build on UBI 10 (ubi-minimal). It
installs system packages, Python, pip, the kolla helper scripts, and the common
Python dependencies shared across all services. Every service image inherits from
it via --build-arg BASE_IMAGE.
Service images use a multi-stage build. All compilation, wheel building, and source handling happen in the build stage. The runtime stage carries only the pre-built artifacts and runtime dependencies, keeping the final image free of compilers, headers, and source code.
Each image declares its binary (system) and Python dependencies in a set of pre-defined plain-text files, separated by build-time vs. runtime scope. See the developer guide for the full list and format.
Patched or replaced dependencies can be placed under src/overrides/<pkg>/
without any sources.txt entry. The filtered constraints file excludes these
packages so the overridden version takes precedence.
Config files come from three sources: the upstream source tree (copied during the
build stage), generated artifacts (e.g., oslo-config-generator output), and
manually maintained files in config/ directories. Each file is listed
explicitly in the Containerfile with full source and destination paths -- no
directory-level copies -- so the build fails immediately if a file is missing.
Examples of files to include: sudoers rules, rootwrap configs, api-paste.ini, default service config, policy files, etc.
Examples of files to exclude: systemd units, logrotate configs, sysv init scripts, tmpfiles.d entries, etc.
The repository provides tooling to run periodic CI jobs that propose PRs
updating the pinned commit hashes in sources.txt to the latest upstream
commits. These PRs trigger builds and validation against the
openstack-k8s-operators environment, so that container images pushed to the
registry have been verified to be functional.
Container images are published by the post-merge job to
quay.io/openstack-s2i-containers/ with three tags per image:
<stream>-latest-- rolling tag, always pointing to the last successfully built and validated containers for that stream.<stream>-<sha>-- immutable tag tied to the specific commit in this repository that produced the build.sha256-<manifest-digest>-- content-addressed tag for the pushed manifest. It preserves the manifest and its layers when the rolling tag moves to a later build.
For example, for the master stream an image is tagged as master-latest,
master-<commit-sha>, and sha256-<manifest-digest>. No publication tag is
assigned a Quay expiration.
Every image also carries OCI labels set at build time:
org.opencontainers.image.revision-- the s2i-openstack-containers git commit that produced the build (GIT_REVISION, Zuulzuul.newrev, or GHAgithub.sha).s2i.openstack.org/stream-- the build stream (for examplemaster).
The post-merge push writes a digest catalog (content-set.yaml) next to the
build logs so consumers can pin by digest instead of racing :master-latest.