Build step

Building Docker images in Codefresh pipelines

Use Docker to build an image and store it in Codefresh or push it to an external registry. Create multi-platform images with BuildX and QEMU (Quick EMUlator).
In Codefresh, Docker containers are first-class citizens, and we offer special typed steps for the most frequently used Docker commands through our build step.

Purpose of build steps

Build steps are a secure replacement for docker build commands.

Therefore, this command on your local workstation:

docker build . -t my-app-image:1.0.1

is converted in Codefresh into the following build step:

BuildMyImage:
  title: Building My Docker image
  type: build
  image_name: my-app-image
  tag: 1.0.1

Usage

YAML

step_name:
  type: build
  title: Step Title
  description: Free text description
  working_directory: ${{clone_step_name}}
  dockerfile: path/to/Dockerfile
  image_name: owner/new-image-name
  tag: develop
  platform: 'linux/arm64'
  buildx: true
  build_arguments:
    - key=value
  cache_from:
    - owner/image-name:$
    - owner/image-name:main
  target: stage1
  no_cache: false
  no_cf_cache: false
  tag_policy: original
  fail_fast: false
  metadata:
    set:
      - qa: pending
  when:
    condition:
      all:
        noDetectedSkipCI: "includes('${{CF_COMMIT_MESSAGE}}', '[skip ci]') == false"
  on_success:
    ...
  on_fail:
    ...
  on_finish:
    ...
  retry:
    ...

Fields

The default behavior of the build step is defined a

Field Description Required/Optional/Default
title The free-text display name of the step. Optional
description A basic, free-text description of the step. Optional
stage Parent group of this step. For more information, see Stages in pipelines. Optional
working_directory The directory in which the build command is executed. It can be an explicit path in the container’s file system, or a variable that references another step.
The default is ${{main_clone}} . Note that the working_directory when defined changes only the Docker build context. It is unrelated to the WORKDIR in the Dockerile.
Default
dockerfile The path to the Dockerfile from which the image is built. The default is Dockerfile. Default
image_name The name of the image that is built. Required
region Relevant only for Amazon ECR integrations using either service accounts or explicit credentials.
The names of the regions for which to perform cross-region replication. The names of the source region and the destination region name must be defined in separate steps.
Optional
role_arn Relevant only for Amazon ECR integrations using either service accounts or explicit credentials.
The Amazon Resource Name (ARN) of the IAM role to be assumed to push the built image to the ECR repository, in the format arn:aws:iam::<cross-account-id>:role/<role-name>, where:
<account-id> is the ID of the AWS account where the ECR repository is hosted.
<role-name> is the specified role with the required permissions within this account to access and manage the ECR repository.
Required
tag The single tag to assign to the built image. To assign multiple tags, use tags (see below).
The default tag is the name of the branch or revision that is built.
Default
tags Multiple tags to assign to the built image.
To assign a single tag, use tag (see above).
This is an array, and should conform to the following syntax:
tags:
- tag1
- tag2
- ${{CF_BRANCH_TAG_NORMALIZED}}
- tag4


OR
tags: [ 'tag1', 'tag2', '${{CF_BRANCH_TAG_NORMALIZED}}', 'tag4' ]
Optional
cache_from The list of cache sources to use as Docker cache when building the image. Every source in the list is passed to the build command using the --cache-from flag. See Docker documentation for more info. Optional
registry The name of the registry to which to push the built image. You can define any registry that is integrated with Codefresh.
When not defined, and you have multiple registry contexts, Codefresh uses the one set as the default registry.
Optional
registry_contexts Advanced property for resolving Docker images when working with multiple registries with the same domain. When defined, pulls the image from the specified context.
NOTE: When using buildx to build and push multi-platform images, registry_contexts cannot be assigned a different registry from the same domain as the target registry defined for registry, as Docker does not support being logged in to multiple Docker registries that share the same domain at the same time.
Optional
disable_push Defines if to automatically push the built image to the registry or not. When set to false, the default, the image is automatically pushed to the registry.
This setting overrides the default behavior set at the account leve. See Default behavior for buld steps.

NOTE: Because of Docker’s limitation on loading multi-platform images to the local Docker instance, unless the built image is pushed as part of the build step, you cannot reference the same image in subsequent steps using the ${{build_step_name}} variable.
Optional
tag_policy The case-transformation policy for the tag name.
originalpushes the tag name as is, without changing it.
lowercase, the default, automatically converts the tag name to lowercase.

Tags in mixed case are pushed as image_name:<tagname> when set to the default value.
Default
no_cache Defines if to enable or disable Docker engine cache for the build. When set to false, the default, enables Docker engine cache. To disable, set to true. See more info. Optional
no_cf_cache Defines if to enable or disable Codefresh build optimization for the build. When set to false, the default, enables Codefresh build optimization. See more info.  
build_arguments The set of Docker build arguments to pass to the build process. Optional
target The target stage at which to stop the build in a multistage build. Optional
timeout The maximum duration permitted to complete step execution in seconds (s), minutes (m), or hours (h), after which to automatically terminate step execution. For example, timeout: 1.5h.
The timeout supports integers and floating numbers, and can be set to a maximum of 2147483647ms (approximately 24.8 days).

If defined and set to either 0s/m/h or null, the timeout is ignored and step execution is not terminated.
See Add a timeout to terminate step execution.
Optional
fail_fast Determines pipeline execution behavior in case of step failure.
  • true: The default, terminates pipeline execution upon step failure. The Build status returns `Failed to execute`.
  • false: Continues pipeline execution upon step failure. The Build status returns Build completed successfully.
    To change the Build status, set strict_fail_fast to true.
Optional
strict_fail_fast Specifies how to report the Build status when fail_fast is set to false.
NOTE:
Requires Runner chart upgrade to v6.3.9 or higher.

You can set the Build status reporting behavior at the root-level or at the step-level for the pipeline.
  • true:
    • When set at the root-level, returns a Build status of failed when any step in the pipeline with fail_fast=false fails to execute.
    • When set at the step-level, returns a Build status of failed when any step in the pipeline with fail_fast=false and strict_fail_fast=true fails to execute.
  • false:
    • When set at the root-level, returns a Build status of successful when any step in the pipeline with fail_fast=false fails to execute.
    • When set at the step-level, returns a Build status of successful when any step in the pipeline with fail_fast=false fails to execute.

NOTES:
strict_fail_fast does not impact the Build status reported for parallel steps with fail_fast enabled. Even if a child step fails, the parallel step itself is considered successful. See also Handling error conditions in a pipeline.
Optional
when The set of conditions that need to be satisfied in order to execute this step.
For more information, see Conditional execution of steps .
Optional
metadata Annotate the built image with key-value metadata. Optional
on_success, on_fail and on_finish Define operations to perform upon step completion using a set of predefined Post-step operations. Optional
retry Define retry behavior for the build step, as described in Retrying a step. Optional
buildkit When set to true, enables Buildkit and all of its enhancements. When using buildkit with cache_from, to allow the built image to be used as cache for future images, you must specify BUILDKIT_INLINE_CACHE=1 in the build_arguments. See more info Optional
ssh Available when using Buildkit for ssh keys. See more info Optional
secrets Available when using Buildkit for secret mounting. See more info Optional
platform The target platform or platforms to which to push the image. For example, linux/amd64. To target multiple platforms, separate them with commas, as in linux/amd64,linux/arm64.
NOTE: To use this property, you must enable buildx.
Optional
buildx Build and push Docker images, including multi-platform images, with Buildx. Disabled by default.
  • To enable with default configuration, set to true. You do not have to add any other parameters.
  • When set to true, caching is disabled.
  • To enable with custom configuration, set to an object with custom configuration. With custom configuration, you can configure settings for qemu and builder.
    • qemu
      • image: The Docker image to use to install the QEMU static binaries. Currently, Codefresh supports the tonistiigi/binfmt Docker image.
        By default, installs the binaries from the tonistiigi/binfmt:latest Docker image.
      • platforms: The binaries of platform emulators to install with the Docker image defined for image. The default value is all.
    • builder:
      • driver: The builder driver to use. By default, uses docker-container driver to build multi-platform images and export cache using a BuildKit container.
      • driver_opts: Additional driver-specific configuration options to customize the driver. For example, image=moby/buildkit:master.
Optional

Exported resources

Build image step examples

Add a timeout to terminate step execution

To prevent steps from running beyond a specific duration if so required, you can add the timeout flag to the step.
When defined:

  • The timeout is activated at the beginning of the step, before the step pulls images.
  • When the step’s execution duration exceeds the duration defined for the timeout, the step is automatically terminated.

NOTE
To define timeouts for parallel steps, see Adding timeouts for parallel steps.

Here’s an example of the timeout field in the step:

YAML

step_name:
  type: build
  title: Step Title
  description: Free text description
  working_directory: ${{clone_step_name}}
  dockerfile: path/to/Dockerfile
  image_name: owner/new-image-name
  tag: develop
  platform: 'linux/arm64'
  buildx: true
  build_arguments:
    - key=value
  cache_from:
    - owner/image-name:$
    - owner/image-name:main
  target: stage1
  no_cache: false
  no_cf_cache: false
  tag_policy: original
  timeout: 45m
  fail_fast: false
  strict_fail_fast: true
  metadata:
    set:
      - qa: pending
  when:
    condition:
      all:
        noDetectedSkipCI: "includes('${{CF_COMMIT_MESSAGE}}', '[skip ci]') == false"
  on_success:
    ...
  on_fail:
    ...
  on_finish:
    ...
  retry:
    ...

Timeout info in logs
Timeout information is displayed in the logs, as in the example below.

Step termination due to timeout in logs

Step termination due to timeout in logs

Using Dockerfile in root project folder

codefresh.yml

version: '1.0'
steps:
  BuildMyImage:
    title: Building My Docker image
    image_name: my-app-image
    type: build

Using different Dockerfile and specific version tag

codefresh.yml

version: '1.0'
steps:
  BuildMyImage:
    title: Building My Docker image
    type: build
    image_name: my-app-image
    dockerfile: my-custom.Dockerfile
    tag: 1.0.1

Using different Dockerfile and pushing multiple tags to default registry

codefresh.yml

version: '1.0'
steps:
  BuildMyImage:
    title: Building My Docker image
    type: build
    image_name: my-app-image
    dockerfile: my-custom.Dockerfile
    tags:
      - latest
      - ${{CF_BRANCH_TAG_NORMALIZED_LOWER_CASE}}
      - v1.1

Automatically push to registry with name my-registry

For information on registries, see Docker registries for pipeline integrations.

codefresh.yml

version: '1.0'
steps:
  BuildMyImage:
    title: Building My Docker image
    type: build
    image_name: my-app-image
    dockerfile: my-custom.Dockerfile
    tag: 1.0.1
    registry: my-registry

Two images in two different folders using Codefresh variables as tags

For information on Codefresh variables, see Variables in pipelines.

codefresh.yml

version: '1.0'
steps:
  BuildNodeImage:
    title: Building My Node app
    type: build
    image_name: my-department/my-team/my-node-image
    dockerfile: Dockerfile
    working_directory: ./project1
    tag: ${{CF_BRANCH_TAG_NORMALIZED}}-${{CF_SHORT_REVISION}}
  BuildGoImage:
    title: Building My Go app
    type: build
    image_name: my-company/my-go-image
    dockerfile: Dockerfile
    working_directory: ./project2
    tag: ${{CF_BRANCH_TAG_NORMALIZED_LOWER_CASE}}

Using cache_from with buildkit

codefresh.yml

version: '1.0'
steps:
  BuildMyImage:
    title: Building My Docker image
    type: build
    image_name: my-app-image
    dockerfile: my-custom.Dockerfile
    tag: 1.0.1
    buildkit: true
    build_arguments:
    - BUILDKIT_INLINE_CACHE=1
    cache_from:
    - my-registry/my-app-image:${{CF_BRANCH}}
    - my-registry/my-app-image:master

Multi-platform images with platform and buildx

Docker images can support multiple platforms and architectures, meaning that you can create an image once, and reuse the same image on different platforms. Docker automatically selects the image that matches the target OS and architecture.

For more on the architectures supported, see the Docker Official Images README. For more on multi-platform images in Docker, see the official documentation.

NOTE
Caching is disabled when buildx is set to true.

Build image for ARM

codefresh.yml

version: '1.0'
steps:
  BuildMyImage:
    title: Building My Docker image
    type: build
    working_directory: '${{clone}}'
    image_name: my-app-image
    tag: latest
    platform: 'linux/arm64'
    buildx: true

Build image for multiple platforms

codefresh.yml

version: '1.0'
steps:
  BuildMyImage:
    title: Building My Docker image
    type: build
    working_directory: '${{clone}}'
    image_name: my-app-image
    tag: latest
    platform: 'linux/amd64,linux/arm64'
    buildx: true

Build image for multiple platforms with custom buildx configuration

codefresh.yml

version: '1.0'
steps:
  BuildMyImage:
    title: Building My Docker image
    type: build
    working_directory: '${{clone}}'
    image_name: my-app-image
    tag: latest
    platform: 'linux/amd64,linux/arm64'
    buildx:
      qemu:
        # image used for installing QEMU static binaries to provide cross-platform emulator support
        image: 'tonistiigi/binfmt:latest' # default
      builder:
        # driver to use to create the builder instance
        driver: 'docker-container' # default

For faster builds, you can also build Docker images in parallel.

Inline Dockerfile

If your project does not already have a Dockerfile, you can also define one within the pipeline:

codefresh.yml

version: '1.0'
steps:
  BuildingDockerImage:
    title: Building Docker Image
    type: build
    image_name: my-own-go-app
    working_directory: ./
    tag: '${{CF_BRANCH_TAG_NORMALIZED}}'
    dockerfile:
      content: |-
       # ---
       # Go Builder Image
       FROM golang:1.8-alpine AS builder
       # set build arguments: GitHub user and repository
       ARG GH_USER
       ARG GH_REPO
       # Create and set working directory
       RUN mkdir -p /go/src/github.com/$GH_USER/$GH_REPO
       # copy file from builder image
       COPY --from=builder /go/src/github.com/$GH_USER/$GH_REPO/dist/myapp
       /usr/bin/myapp
       CMD ["myapp", "--help"]

Use this technique only as a last resort. It is better if the Dockerfile exists as an actual file in source control.

Signing container images with Sigstore

Signing container images created and distributed by pipelines and verifying their authenticity is essential for maintaining software security.
Sigstore, a trusted authority for signing container images, offers two methods to secure images: key-based signing, the traditional method, and keyless signing, a new method using the OpenID Connect (OIDC) protocol.

  • Key-based signing
    Key-based signing relies on private-public key pairs to authenticate and verify container images. It comes with inherent security and maintenance challenges in managing the key pairs.

  • Keyless signing
    Keyless signing is now the default method due to significant advantages:

    • No long-term key management: No need to generate, store, or rotate private keys.
    • Improved security: Reduces the risk of key theft or misuse, by eliminating the need for long-term private keys.
    • Simplified automation: Ideal for CI/CD pipelines, as OIDC tokens are automatically retrieved and used for signing, with no manual intervention.

Codefresh supports both both key-based and keyless signing to secure container images generated by pipelines.

How Codefresh handles container image signing

Codefresh removes the complexity of setting up and managing both key-based and keyless signing by integrating the necessary components directly into the pipeline’s build step. This automation enables you to sign container images with minimal configuration, simplifying the entire signing process.

See Keyless signing for container images and Key-based signing for container images.

Keyless signing for container images

Keyless signing in Codefresh utilizes the Codefresh OIDC provider during the artifact signing process. This integration simplifies authentication and ensures the integrity of the signing process by embedding claims within the signature. For detailed information on how to integrate Codefresh as an OIDC provider, see OIDC for pipelines.

Benefits of Codefresh OIDC Provider for keyless signing

  • Secure authentication
    Codefresh acting as the OIDC provider for keyless signing eliminates the need for long-term private keys. By using the OIDC protocol, Codefresh securely authenticates the pipeline at runtime, ensuring that only authorized pipelines can sign the artifacts.

  • Claims identifying pipeline and build
    The Codefresh OIDC provider generates claims that uniquely identify both the pipeline and the build in the token issued for signing. These claims are automatically embedded in the OIDC token and passed to the signing process, ensuring that each container image’s signature is tied to a specific build in a specific pipeline.

  • Robust verification process using claims
    The claims provided by the Codefresh OIDC provider are used in the signature for a robust verification process. When verifying signed container images, external systems can use the embedded claims to confirm the origin and authenticity of the artifact, ensuring that the image was signed by a trusted pipeline and build.

Keyless signing: components and roles

The table below outlines the key components involved in keyless signing and their roles.

For detailed information, including keyless-signing architecture, read our blog.

Component Description Responsible party
OIDC provider The organization creating the container images must act an OIDC provider.
Codefresh is an official OIDC provider, setting up an integration with Codefresh provides added benefits for keyless signing. See Benefits of Codefresh OIDC Provider for keyless signing.
Customer
ID token An ID token is created for the OIDC provider to authenticate and authorize pipeline actions.
Codefresh automatically retrieves the ID token through the obtain-oidc-id-token step, with no manual action required.
Codefresh
ID token verification A certificate authority verifies the ID token against the OIDC provider, and issues a short-lived cryptographic certificate based on the ID token. Fulcio
Image signing Codefresh integrates with Cosign to sign the container image.
Simply add the cosign: sign: true attribute to your pipeline’s build step.
Cosign
Authenticity verification The certificate (public key) along with the signature/digest is stored in an append-only transparency log. Any external organization can verify the authenticity of the signed container image.
For details on image verification, see Verifying artifacts signed with Codefresh pipelines.
Rekor

Adding keyless signing to your pipeline

To enable keyless signing, add the following attribute to your pipeline’s build step:

cosign: 
  sign: true

Codefresh retrieves the OIDC token, invokes Cosign, and securely signs the container image without the need for additional setup.

Here’s an example of a build step with this attribute:

scenario_multiplatform_keyless_sign:
  type: "build"
  stage: "multiplatform-keyless"
  image_name: $
  tag: "$-scenario_multiplatform_keyless_sign"
  ...
  cosign:
    sign: true
  ...

Key-based signing for container images

Key-based signing uses a private key to sign container images. There are two options for key-based signing:

  • Signing without a password: The private key is used directly to sign the image.
  • Signing with a password/passphrase: The private key is first unlocked using a passphrase for additional security.
Adding key-based signing to your pipeline

To enable key-based signing, add the following to your pipeline’s build step:

cosign: 
  sign: true
  options: 
    key: "$/$-with-pass.key"
    key-pass: "test-password"  # only for password-based signing 

where:

  • cosign: sign: true indicates that the Cosign will sign the container image generated by the pipeline.
  • options.key is path to the private key used for signing.
  • options.key-pass is required only for password-based signing, and is the password or passphrase for unlocking the private key.

NOTE
options can include any Cosign-supported command.

Example: Key-based signing with empty password
# key-based signing with empty password

scenario_simple_key_nopass_sign:
  type: "build"
  ...
    cosign:
      sign: true
      options:
        key: "$/$.key"
  ...
Example: Key-based signing with non-empty password
# key-based signing with non-empty password
scenario_simple_key_pass_sign:
  type: "build"
  ...
    cosign:
      sign: true
      options:
        key: "$/$-with-pass.key"
        key-pass: "test-password"
  ...

Automatic pushing

All images built successfully with the build step are automatically pushed to the default Docker registry defined for your account. This behavior is completely automatic and happens without any extra configuration on your part. To disable this, add the disable_push property set to true to your build step. Remember that if you are using buildx, you cannot set the disable_push property to true.

NOTE
The push step in Codefresh is optional, and is only needed if you want to push to external Docker registries.

Docker Images pushed automatically

Docker Images pushed automatically

Buildkit support

Codefresh also allows you to use buildkit with all its enhancements and experimental features.

Using buildkit you can get:

  • Improved build output logs
  • Mounting of external secrets that will never be stored in the image
  • Access to SSH keys and sockets from within the Dockerfile
  • Use cache and bind-mounts at build time

These capabilities are offered as extra arguments in the build step, and using any of them will automatically enable buildkit. You can utilize the different mount-options for the Dockerfile instruction RUN as long as buildkit is enabled for your build step. Mounts of type cache work out of the box and are persisted between pipeline runs.

The simplest way to use buildkit is by enabling it explicitly:

codefresh.yml

version: '1.0'
steps:
  BuildMyImage:
    title: Building My Docker image
    image_name: my-app-image
    type: build
    buildkit: true

codefresh.yml

version: '1.0'
steps:
  BuildMyImage:
    title: Building My Docker image
    image_name: my-app-image      

For secrets, you can either mention them in a single line:

codefresh.yml

version: '1.0'
steps:
  BuildMyImage:
    title: Building My Docker image
    image_name: my-app-image
    type: build
    secrets:
      - id=secret1,src=./my-secret-file1.txt
      - id=secret2,src=./my-secret-file2.txt

or multiple lines:

codefresh.yml

version: '1.0'
steps:
  BuildMyImage:
    title: Building My Docker image
    image_name: my-app-image
    type: build
    secrets:
      - id: secret1
        src: ./my-secret-file1.txt
      - id: secret2
        src: ./my-secret-file2.txt

For the SSH connection you can either use the default:

codefresh.yml

version: '1.0'
steps:
  BuildMyImage:
    title: Building My Docker image
    image_name: my-app-image
    type: build
    ssh: default

or define different keys:

codefresh.yml

version: '1.0'
steps:
  BuildMyImage:
    title: Building My Docker image
    image_name: my-app-image
    type: build
    ssh:
      - github=~/.ssh/github_rsa
      - bitbucket=~/.ssh/bitbucket_rsa

You might want to use an environment variable to store and retrieve an SSH key. This can be achieved by converting your SSH key into a one-line string:

tr '\n' ',' < /path/to/id_rsa

Copy the output and place it an environment variable. To make the SSH key availabe to the build step, you can write it to the Codefresh volume: codefresh.yml

version: '1.0'
steps:
  SetupSshKeys:
    title: Setting up ssh key
    image: alpine:latest
    commands:
      - mkdir /codefresh/volume/keys
      - echo "${SSH_KEY}" | tr  ',' '\n' > /codefresh/volume/keys/github_rsa

  BuildMyImage:
    title: Building My Docker image
    image_name: my-app-image
    type: build
    tag: latest
    ssh:
      - github=/codefresh/volume/keys/github_rsa

You can combine all options (ssh, secrets) in a single build step if desired.

Default behavior for build steps
Codefresh YAML for pipeline definitions
Steps in pipelines