Use Expressions for Build Args in Docker Build and Push
pending feedback
W
Willow Llama
Currently, the Docker Build and Push step does not support adding an expression for the entire Build Args field. You can set an expression in one of the argument's values but, not for the whole Build Args. For example, this works
buildArgs:
key: <+pipeline.variables.someValue>
But, we'd like the ability to provide a comma separated list of key/value pairs to this field like this instead,
bulidArgs: <+pipeline.variables.buildArgs>
Log In
ompragash
Hi mlino, thank you for the additional details. That confirms the issue clearly. When buildArgs receives a value through an expression, the platform treats it as a string rather than a map, so the key-value pairs don't resolve as expected.
As a workaround, you can bypass the buildArgs field entirely and pass build args through envVariables using the plugin's native env var:
- step:
type: BuildAndPushDockerRegistry
spec:
connectorRef: <your_connector>
repo: <your_repo>
tags:
- latest
envVariables:
PLUGIN_BUILD_ARGS: <+pipeline.variables.buildArgs>
The expression should resolve to a comma-separated string in key=value format, e.g., ARG1=val1,ARG2=val2. If any of your values contain commas, use PLUGIN_BUILD_ARGS_NEW with semicolons as delimiters instead, and set PLUGIN_MULTIPLE_BUILD_ARGS to "true":
envVariables:
PLUGIN_BUILD_ARGS_NEW: <+pipeline.variables.buildArgs>
PLUGIN_MULTIPLE_BUILD_ARGS: "true"
# Expression should resolve to: ARG1=val1;ARG2=value,with,comma;ARG3=val3
This works because envVariables accepts string values where expressions resolve normally, and the underlying plugin picks up these PLUGIN_* env vars as its build arg configuration.
A couple of things to note: if you use this approach, avoid setting build args through the buildArgs field at the same time, as the env var will override those values. Also, secret masking won't apply to individual arg values passed this way, so avoid including sensitive data in the expression.
As for the UI-level support, allowing the buildArgs field itself to accept a full expression, we'll track that as an enhancement.
Ryan, appreciate the detailed write-up on the Golden Template impact. The ask around passing map-typed fields dynamically through template layers is related but distinct from the expression resolution issue here, as it touches the inputs model more broadly. We'll track that one separately. The post you referenced (Docker Image Push template with dynamic build args and labels) is already tracked on our side and I'll have the team update it with progress.
Let us know if the workaround helps in the meantime!
P
Prospective Walrus
To add more context on why this matters for platform teams:
Harness recommends the Golden Template pattern where a platform team maintains centralized pipeline templates composed of step group templates, and application teams consume them via templateRef. The template chain looks like:
Consuming pipeline → Pipeline template → Step group template → Step
The buildArgs field on BuildAndPushDockerRegistry is internally a map type, but Harness variables only support String, Number, and Secret. This creates a type system gap - there's no way to pass a map through a variable at any layer of the template chain.
The only working approach is buildArgs: <+input> on the step, with <+input> passed through every intermediate templateInputs layer. This forces every consuming pipeline, even those that don't use build args, to include a deeply nested templateInputs.steps[].spec.buildArgs block, or reconciliation flags the pipeline as out of sync.
This undermines the Golden Template value proposition. Platform teams want to add optional capabilities to shared templates without imposing YAML overhead on every consumer. Supporting expression-to-map coercion (e.g., buildArgs: <+pipeline.variables.build_args> where the String variable contains {"key":"value"}) would solve this cleanly.
We'd also note this isn't the first time this request has been made, see: Docker Image Push template with dynamic build args and labels (April 2024, marked long-term priority), https://ideas.harness.io/feature-request/p/docker-image-push-template-with-dynamic-build-args-and-labels
P
Possible Rook
Please prioritize this
W
Willow Llama
Hello, is the information provided sufficient?
W
Willow Llama
Hello,
if i set buildArgs: {"abc":"123","def":"456"} in my step group it works, while using it like: buildArgs: <+expression.isResolved(matrix.container.buildArgs)?matrix.container.buildArgs:{}> and setting value buildArgs: {"abc":"123","def":"456"} in my pipeline is not working because its treated like a string and not a map
ompragash
marked this post as
pending feedback
ompragash
Hi, thank you for raising this request. We appreciate the feedback!
Before we dive in, I'd like to clarify your use case a bit further. Are you looking to:
(a) Store multiple build args as a single string (e.g.,
key1=value1,key2=value2
) in a pipeline variable and pass that entire value to the Build Args field using a single expression like <+pipeline.variables.buildargs>
?(b) Something else we might be missing?
Understanding your exact workflow will help us provide the best guidance.
That said, I'd like to share how the Build Args field is designed today in the Docker Build and Push step:
The Build Args field accepts individually defined key-value pairs, where each key maps to a value. Expressions are fully supported at the value level, meaning you can reference pipeline variables, secrets, or any other Harness expression within each argument's value. For example:
ARG_1: <+pipeline.variables.val1>
ARG_2: <+pipeline.variables.val2>
However, passing a single expression that resolves to the
entire set of build args
is not supported by design. The underlying plugins (Docker/Kaniko/Buildx) expect each build arg to be parsed and passed individually as separate --build-arg
flags. Accepting a bulk string would require the platform to split and interpret the value at runtime, which introduces ambiguity around delimiters, escaping, and, importantly, secret masking. When args are defined individually, the platform can correctly mask each sensitive value in logs. With a single concatenated string, the masker would only know the full combined value, and individual fragments could leak unmasked.Recommended approach: Define each build arg as a separate entry in the Build Args field and use expressions in the values. If your build args are driven by different environments or configurations, pipeline variables or input sets work well to keep things dynamic without packing multiple args into a single variable.
We understand this requires defining args individually rather than in bulk, but this design ensures reliable expression resolution, proper secret masking, and predictable behavior across build infrastructures.
Hope that helps, please let us know if you have any follow-up questions or if your use case differs from what we've described above!
W
Willow Llama
Hello,
Please can you let us know when this fix is in place?