Skip to content

remote.client_registry silently accepts an embedded :tag, producing an invalid double-tagged Docker reference in astro remote deploy #2223

Description

@seanmuth

Summary

astro remote deploy builds its remote Docker push tag as:

remoteImage := fmt.Sprintf("%s:%s", registryEndpoint, imageTag)

(cloud/deploy/deploy.go, around lines 1071-1084), where imageTag is "deploy-" + <UTC timestamp> and registryEndpoint comes straight from the remote.client_registry config value.

If a user sets remote.client_registry to a value that itself already contains a :tag -- for example by copy-pasting a full tagged image reference out of a Dockerfile FROM line -- the CLI blindly appends its own :deploy-<timestamp> on top of it, producing a Docker reference with two tags, e.g.:

myregistry.example.com:7990/repository/path/my-image:1.0:deploy-2026-07-22T16:23

Docker then rejects this at build/push time with a raw invalid reference format error, which gives the user no indication that the problem is their remote.client_registry config value rather than something wrong with the build itself. This happened live to a customer mid-PoV and took real time to root-cause because the CLI-level error surfaced as an opaque Docker error.

Root cause

ValidateRegistryEndpoint in config/validators.go (currently lines ~29-51) only checks that the value:

  • is non-empty
  • contains a /
  • has no spaces
  • doesn't start or end with /

It never rejects an embedded :tag component on the repository path, so a value like myregistry.example.com:7990/repository/path/my-image:1.0 passes validation cleanly and only blows up later, inside Docker, when the CLI's own tag gets appended.

Vulnerable call sites

This validator is the single shared code path for every way a user can set remote.client_registry, so both of these are affected:

  1. astro dev init --remote-execution-enabled -- via getRegistryEndpoint() in cmd/airflow.go, whether the value comes from the --remote-image-repository flag or the interactive prompt.
  2. astro config set remote.client_registry <value> -- in cmd/config.go (~line 112), via cfg.Validate().

There is no separate astro remote init command; both paths funnel into the same ValidateRegistryEndpoint registered against remote.client_registry in config/validators.go (line ~17).

Proposed fix

Extend ValidateRegistryEndpoint to reject any value where the repository-path portion (everything after the first /) contains a :. This correctly still allows a :port on the registry host itself (before the first /), e.g. myregistry.example.com:7990/repository/path remains valid, while rejecting myregistry.example.com:7990/repository/path/my-image:1.0.

Suggested error message:

registry endpoint must not include an image tag -- got %q, use 'registry[:port]/repository/path' without a trailing ':tag' (the CLI appends its own deploy tag automatically)

I have a fix + unit tests ready and will open a PR referencing this issue.


🤖 Filed with the help of Claude Sonnet 5 (Claude Code)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions