feat: add localFallback option for Docker remote build#7041
feat: add localFallback option for Docker remote build#7041
Conversation
When remoteBuild is true and localFallback is true in azure.yaml, azd automatically falls back to a local Docker build if the remote ACR build fails. Displays a WARNING message when fallback triggers. This helps users on subscriptions that don't support ACR Tasks (e.g., free trial) by gracefully degrading to local Docker builds instead of failing outright. Changes: - Add localFallback field to DockerProjectOptions struct - Add fallback logic in ContainerHelper.Publish() - Update proto definition and generated code - Update azure.yaml JSON schema (service-level and docker-level) - Add mapper registry mappings - Add unit test for fallback behavior Fixes #4618 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR implements a localFallback option for Docker remote builds in azd. When remoteBuild: true and localFallback: true are set in azure.yaml, if the remote ACR build fails, azd automatically retries with a local Docker build and displays a warning. This addresses #4618, where users on free-trial subscriptions (which don't support ACR Tasks) had to manually remove remoteBuild: true to work around deployment failures.
Changes:
- Adds
LocalFallbackfield toDockerProjectOptionsstruct and propagates it through proto/mapper layers - Implements fallback logic in
ContainerHelper.Publish()usingux.WarningMessage - Adds
localFallbackto the JSON schema at both docker-level and service-level
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
schemas/v1.0/azure.yaml.json |
Adds localFallback to both the service-level and the docker definition blocks |
cli/azd/pkg/project/framework_service_docker.go |
Adds LocalFallback bool field to DockerProjectOptions struct |
cli/azd/pkg/project/container_helper.go |
Adds fallback logic: if runRemoteBuild fails and LocalFallback is set, displays a warning and calls publishLocalImage |
cli/azd/pkg/project/container_helper_test.go |
New test verifying warning is shown and local build runs when remote build fails |
cli/azd/pkg/project/mapper_registry.go |
Maps LocalFallback field in all three proto↔Go conversion functions |
cli/azd/grpc/proto/models.proto |
Adds local_fallback as field number 10 to DockerProjectOptions message |
cli/azd/pkg/azdext/models.pb.go |
Generated protobuf code: adds LocalFallback field and GetLocalFallback() accessor |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Include original error in fallback warning message - Fix proto field ordering (local_fallback after build_args) - Align struct tag spacing for LocalFallback - Remove service-level localFallback from schema (only docker-level) - Fix lint: break long test line under 125 chars Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Since adding a new field would require to update all templates to benefit from this, I would consider making the fallback the default behavior for However, we could also make azd to check the SKU from the ACR defined in the infrastructure when remoteBuild is enabled and warn folks about it. Maybe as part of |
|
schemas/v1.0/azure.yaml.json
Outdated
| "title": "Optional. Whether to build the image remotely", | ||
| "description": "If set to true, the image will be built remotely using the Azure Container Registry remote build feature. If set to false, the image will be built locally using Docker." | ||
| }, | ||
| "localFallback": { |
There was a problem hiding this comment.
I’m wondering if remoteBuild might work better as a mode rather than a boolean paired with localFallback.
Where the semantics are clearer:
prefer→ try remote build first, fall back to local if it failstrue→ require remote build, fail if unavailablefalse→ use local build only
The current shape:
docker:
remoteBuild: true
localFallback: trueintroduces some configuration coupling and possible ambiguous combinations (remoteBuild: false with localFallback: true, etc.). A mode-based approach could make the behavior more explicit and easier to reason about.
I know this was mentioned in the desigh consideration for backwards compatibility and general implementation ease. FWIW -- for gRPC changes, I'm thinking it's perfectly reasonable to deprecate the current bool and introduce a new field.
There was a problem hiding this comment.
100%
I was suggesting to make the fallback the default behavior. This mode idea is also good!
I would imagine auto as the mode you would want to let azd just find/pick something to build the container, maybe even allowing other tools in the future.
There was a problem hiding this comment.
prefer turns this field into complex non boolean check, the better approach here is to make localFallback the default as @vhvb1989 stated. So that keeps the remoteBuild the same operation. If true and fails then try local.
There was a problem hiding this comment.
Just to clarify the suggestion here: prefer makes the field from boolean (2-values) into an enum (3-values). The check is still done against enumerated fields.
I'm not a huge fan of introducing fallback behavior into main paths in general, but I can see why we might want it for completely new azd users
Remove the localFallback field from DockerProjectOptions and make fallback-to-local the default behavior when remoteBuild is true and the remote build fails. Before attempting the local build, azd now checks if Docker or Podman is installed and running via CheckInstalled, providing a clear error if neither is available. Changes: - Remove LocalFallback from DockerProjectOptions struct, proto, schema - Always fall back to local build on remote build failure - Validate Docker/Podman availability before local fallback - Reserve proto field number 10 to prevent reuse - Update remoteBuild schema description to document fallback behavior Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
wbreza
left a comment
There was a problem hiding this comment.
📋 Code Review — PR #7041
Thanks for taking the time to better our docs! Great work pivoting to automatic fallback without a separate config field — this is cleaner and matches the team's consensus.
I verified the implementation against the codebase and found one significant concern around CI behavior, plus a few cleanup items.
✅ What Looks Good
- Automatic fallback without new config is the right design — avoids ambiguous flag combinations
- Docker/Podman availability check before fallback is solid
- Original remote error preserved in the warning message
- Proto field properly reserved (field 10 + name)
- Test correctly uses
linux/arm64platform to trigger remote build failure
Summary
| Priority | Count |
|---|---|
| High | 1 |
| Medium | 2 |
| Low | 1 |
| Total | 4 |
Overall Assessment: Comment — the CI fallback behavior deserves discussion, and there are a few cleanup items. The core approach is sound.
Note: The PR description still references the localFallback: true configuration that has been removed — consider updating it to reflect the current automatic fallback design.
|
|
||
| if serviceConfig.Docker.RemoteBuild { | ||
| remoteImage, err = ch.runRemoteBuild(ctx, serviceConfig, targetResource, env, progress, imageOverride) | ||
| if err != nil { |
There was a problem hiding this comment.
[High] Consider failing fast in CI instead of silently falling back
The project already has IsRunningOnCI() infrastructure (internal/tracing/resource/ci.go) that detects Azure Pipelines, GitHub Actions, and other CI providers. In CI, silently falling back to local Docker can mask configuration issues — operators should fix their remoteBuild setup rather than have deployments silently change build strategy.
Consider something like:
if err != nil {
// In CI, fail fast rather than silently changing build strategy
if resource.IsRunningOnCI() {
return nil, fmt.Errorf(
"remote build failed: %w\n\n"+
"Running in CI — not falling back to local build. "+
"Fix the remote build configuration or set remoteBuild to false.", err)
}
// Interactive: check for local runtime and fall back
...| Image osutil.ExpandableString `yaml:"image,omitempty" json:"image,omitempty"` | ||
| Tag osutil.ExpandableString `yaml:"tag,omitempty" json:"tag,omitempty"` | ||
| RemoteBuild bool `yaml:"remoteBuild,omitempty" json:"remoteBuild,omitempty"` | ||
| Path string `yaml:"path,omitempty" json:"path,omitempty"` |
There was a problem hiding this comment.
[Medium] Residual whitespace-only changes
This file and mapper_registry.go contain only field-alignment whitespace changes with no functional modification. This looks like residual formatting from when LocalFallback was a struct field — the alignment was adjusted for the longer name, then the field was removed but the alignment kept.
Consider reverting these whitespace changes to reduce diff noise and keep git blame clean.
| "type": "boolean", | ||
| "title": "Optional. Whether to build the image remotely", | ||
| "description": "If set to true, the image will be built remotely using the Azure Container Registry remote build feature. If set to false, the image will be built locally using Docker." | ||
| "description": "If set to true, the image will be built remotely using the Azure Container Registry remote build feature. If the remote build fails, azd automatically falls back to building locally using Docker or Podman if available. If set to false, the image will be built locally." |
There was a problem hiding this comment.
[Medium] Design thread on remoteBuild semantics needs resolution
There's an active discussion between reviewers about whether remoteBuild should become a mode enum (prefer/true/false/auto) vs keeping it boolean with implicit fallback. The current boolean approach with automatic fallback is my preference — it's simpler and the right default UX. But the thread should be resolved explicitly before merge.
The schema description update here nicely documents the fallback behavior, so if the boolean approach is chosen, it's ready to go.
Azure Dev CLI Install InstructionsInstall scriptsMacOS/Linux
bash: pwsh: WindowsPowerShell install MSI install Standalone Binary
MSI
Documentationlearn.microsoft.com documentationtitle: Azure Developer CLI reference
|
Problem
azd deployfails for users on free trial subscriptions whenremoteBuild: trueis set in azure.yaml, because ACR Tasks are not available on free trial SKUs. Users must manually remove theremoteBuildline to work around this (#4618).Solution
Add a new
localFallbackboolean field to Docker service configuration in azure.yaml:When
remoteBuild: trueandlocalFallback: true, if the remote ACR build fails for any reason, azd automatically retries with a local Docker build and displays a warning:Changes
pkg/project/framework_service_docker.goLocalFallbackfield toDockerProjectOptionspkg/project/container_helper.goPublish()withux.WarningMessagepkg/project/mapper_registry.goLocalFallbackin proto conversions (3 locations)grpc/proto/models.protolocal_fallbackfield to proto messagepkg/azdext/models.pb.goschemas/v1.0/azure.yaml.jsonlocalFallbackto schema (service-level and docker-level)pkg/project/container_helper_test.goDesign Decisions
remoteBuildto a string enum ("prefer"): A separate field is lower risk — doesn't change existingremoteBuildtype or semanticsux.WarningMessagematching existing azd warning patternsFixes #4618