Model Software Products
Goal
Most products consist of multiple independently versioned services. This guide uses OCM component references to pin those services into a single Software Bill of Delivery — a versioned, auditable record of what a release contains.
You will end up with
- A
component-constructor.yamlthat defines service components and a product-level aggregator - Component references that pin exact version combinations
- A portable CTF archive ready for transfer across registries and air-gapped environments
Estimated time: ~15 minutes
Prerequisites
- OCM CLI installed
- Familiarity with the component constructor
Steps
Create a component per service
Each service gets its own component. Create a single
component-constructor.yaml— you will add all components to this one file. Start with the services:cat > component-constructor.yaml << 'EOF' # yaml-language-server: $schema=https://ocm.software/schemas/configuration-schema.yaml components: # -- Backend service - name: ocm.software/how-to/backend version: 3.1.0 provider: name: ocm.software resources: - name: image type: ociImage version: 3.1.0 relation: local access: type: OCIImage/v1 imageReference: ghcr.io/stefanprodan/podinfo:6.9.0 # -- Frontend service - name: ocm.software/how-to/frontend version: 1.5.0 provider: name: ocm.software resources: - name: image type: ociImage version: 1.5.0 relation: local access: type: OCIImage/v1 imageReference: ghcr.io/stefanprodan/podinfo:6.9.1 # -- Cache (Redis) - name: ocm.software/how-to/cache version: 2.0.0 provider: name: ocm.software resources: - name: image type: ociImage version: 7.2.4 relation: external access: type: OCIImage/v1 imageReference: docker.io/library/redis:7.2.4 EOFNote: This guide uses the same demo image (
ghcr.io/stefanprodan/podinfo) for frontend and backend services. In a real scenario, each service would reference its own container image.relation— Uselocalwhen the artifact is maintained by the team providing the component (e.g. your own service image). Useexternalwhen it comes from a third party (e.g. a public Redis image you depend on).Add a product component with references
Append the following product component to the same
component-constructor.yaml. It aggregates the service components viacomponentReferences, pinning the exact combination of service versions that were validated together:cat >> component-constructor.yaml << 'EOF' # -- Product component (aggregator) - name: ocm.software/how-to/product version: 2.0.0 provider: name: ocm.software componentReferences: - name: backend componentName: ocm.software/how-to/backend version: 3.1.0 - name: frontend componentName: ocm.software/how-to/frontend version: 1.5.0 - name: cache componentName: ocm.software/how-to/cache version: 2.0.0 EOFA product component uses
componentReferencesto declare which service versions belong together in a release. The product component itself carries no resources — it acts purely as a Software Bill of Delivery that pins a tested combination of versions. This gives release managers, auditors, and downstream consumers a clear, immutable record of what a release contains.Create all component versions
Run from the directory containing your
component-constructor.yaml:ocm add cvNote:
component-constructor.yamlis the default filename the CLI looks for when no--constructorflag is given. If your file has a different name or location, pass--constructor <path>explicitly.If the
--repositoryflag is not specified, the CLI creates a CTF archive calledtransport-archivein the current directory by default.Expected output
COMPONENT │ VERSION │ PROVIDER ──────────────────────────────┼─────────┼────────── ocm.software/how-to/product │ 2.0.0 │ ocm.software ocm.software/how-to/backend │ 3.1.0 │ ocm.software/how-to/frontend │ 1.5.0 │ ocm.software/how-to/cache │ 2.0.0 │Note: In the CLI table output, the
PROVIDERcolumn is printed only for the first component when all components share the same provider. The value still applies to every row.Verify the result
ocm get cv ./transport-archive//ocm.software/how-to/product:2.0.0 --recursive=-1 -o treeExpected output
NESTING COMPONENT VERSION PROVIDER IDENTITY └─ ● ocm.software/how-to/product 2.0.0 ocm.software name=ocm.software/how-to/product,version=2.0.0 ├─ ocm.software/how-to/backend 3.1.0 ocm.software name=ocm.software/how-to/backend,version=3.1.0 ├─ ocm.software/how-to/frontend 1.5.0 ocm.software name=ocm.software/how-to/frontend,version=1.5.0 └─ ocm.software/how-to/cache 2.0.0 ocm.software name=ocm.software/how-to/cache,version=2.0.0Create a New Version
Say the backend team releases a patch (
3.1.0→3.2.0). Replace the constructor with the new backend version, bump the product aggregator to2.1.0, and keep the remaining components unchanged:cat > component-constructor.yaml << 'EOF' # yaml-language-server: $schema=https://ocm.software/schemas/configuration-schema.yaml components: # -- Backend service (bumped) - name: ocm.software/how-to/backend version: 3.2.0 provider: name: ocm.software resources: - name: image type: ociImage version: 3.2.0 relation: external access: type: OCIImage/v1 imageReference: ghcr.io/stefanprodan/podinfo:6.9.1 # -- Frontend service (unchanged) - name: ocm.software/how-to/frontend version: 1.5.0 provider: name: ocm.software resources: - name: image type: ociImage version: 1.5.0 relation: local access: type: OCIImage/v1 imageReference: ghcr.io/stefanprodan/podinfo:6.9.1 # -- Cache (unchanged) - name: ocm.software/how-to/cache version: 2.0.0 provider: name: ocm.software resources: - name: image type: ociImage version: 7.2.4 relation: external access: type: OCIImage/v1 imageReference: docker.io/library/redis:7.2.4 # -- Product component (bumped) - name: ocm.software/how-to/product version: 2.1.0 provider: name: ocm.software componentReferences: - name: backend componentName: ocm.software/how-to/backend version: 3.2.0 # updated - name: frontend componentName: ocm.software/how-to/frontend version: 1.5.0 # unchanged - name: cache componentName: ocm.software/how-to/cache version: 2.0.0 # unchanged EOFRecreate
ocm add cv --repository transport-archive-updatedNote: This creates a new CTF in the
transport-archive-updatedfolder, keeping the originaltransport-archiveintact for comparison.Expected output
COMPONENT │ VERSION │ PROVIDER ──────────────────────────────┼─────────┼────────── ocm.software/how-to/product │ 2.1.0 │ ocm.software ocm.software/how-to/backend │ 3.2.0 │ ocm.software/how-to/frontend │ 1.5.0 │ ocm.software/how-to/cache │ 2.0.0 │
Cleanup
Remove everything created in this guide:
rm -rf transport-archive transport-archive-updated component-constructor.yamlRelated Documentation
- Create a Multi-Component Product — advanced constructor patterns with nesting, labels, and sources
- Component Descriptor Reference — understand the structure of the generated release metadata
- OCM Specification: References — how component references are resolved during transport and deployment