Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cmd/cli/commands/compose.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ func newUpCommand() *cobra.Command {
return errors.New("unable to determine standalone runner endpoint")
}

syncDockerConfigForRegistry(cmd.Context(), asPrinter(cmd))
if err := downloadModelsOnlyIfNotFound(desktopClient, models); err != nil {
return err
}
Expand Down
1 change: 1 addition & 0 deletions cmd/cli/commands/pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ func newPullCmd() *cobra.Command {

func pullModel(cmd *cobra.Command, desktopClient *desktop.Client, model string) error {
printer := asPrinter(cmd)
syncDockerConfigForRegistry(cmd.Context(), printer)
response, _, err := desktopClient.Pull(model, printer)

if err != nil {
Expand Down
1 change: 1 addition & 0 deletions cmd/cli/commands/push.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ func newPushCmd() *cobra.Command {

func pushModel(cmd *cobra.Command, desktopClient *desktop.Client, model string) error {
printer := asPrinter(cmd)
syncDockerConfigForRegistry(cmd.Context(), printer)
response, _, err := desktopClient.Push(model, printer)

if err != nil {
Expand Down
28 changes: 28 additions & 0 deletions cmd/cli/commands/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package commands

import (
"bytes"
"context"
"errors"
"fmt"
"io"
Expand All @@ -10,6 +11,7 @@ import (

"github.com/docker/model-runner/cmd/cli/desktop"
"github.com/docker/model-runner/cmd/cli/pkg/standalone"
"github.com/docker/model-runner/cmd/cli/pkg/types"
"github.com/docker/model-runner/pkg/distribution/distribution"
"github.com/docker/model-runner/pkg/distribution/oci/reference"
"github.com/docker/model-runner/pkg/inference/backends/vllm"
Expand Down Expand Up @@ -247,6 +249,32 @@ func addRunnerFlags(cmd *cobra.Command, opts runnerFlagOptions) {
}
}

// syncDockerConfigForRegistry copies the host's Docker config into the running
// container. Only applicable for Moby engine setups; a no-op otherwise.
func syncDockerConfigForRegistry(ctx context.Context, printer standalone.StatusPrinter) {
if modelRunner == nil {
return
}
engineKind := modelRunner.EngineKind()
if engineKind != types.ModelRunnerEngineKindMoby {
return
}
dockerClient, err := desktop.DockerClientForContext(dockerCLI, dockerCLI.CurrentContext())
Comment thread
areebahmeddd marked this conversation as resolved.
if err != nil {
return // non-fatal: proceed with potentially stale credentials
Comment thread
areebahmeddd marked this conversation as resolved.
Outdated
}
defer dockerClient.Close()

containerID, _, _, err := standalone.FindControllerContainer(ctx, dockerClient)
if err != nil || containerID == "" {
return // non-fatal: container may not be running yet
Comment thread
areebahmeddd marked this conversation as resolved.
Outdated
}

if err := standalone.SyncDockerConfigToContainer(ctx, dockerClient, containerID, engineKind); err != nil {
printer.Printf("Warning: failed to sync Docker credentials to runner: %v\n", err)
}
}

// newTable creates a new table with Docker CLI-style formatting:
// no borders, no column separators, no header line, left-aligned, and 2-space padding.
func newTable(w io.Writer) *tablewriter.Table {
Expand Down
12 changes: 6 additions & 6 deletions cmd/cli/pkg/standalone/containers.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"time"

"github.com/containerd/errdefs"
dockercliconfig "github.com/docker/cli/cli/config"
gpupkg "github.com/docker/model-runner/cmd/cli/pkg/gpu"
"github.com/docker/model-runner/cmd/cli/pkg/types"
"github.com/moby/moby/api/types/container"
Expand All @@ -28,17 +29,16 @@ import (
// controllerContainerName is the name to use for the controller container.
const controllerContainerName = "docker-model-runner"

// copyDockerConfigToContainer copies the Docker config file from the host to the container
// and sets up proper ownership and permissions for the modelrunner user.
// It does nothing for Desktop and Cloud engine kinds.
func copyDockerConfigToContainer(ctx context.Context, dockerClient *client.Client, containerID string, engineKind types.ModelRunnerEngineKind) error {
// SyncDockerConfigToContainer copies the host's ~/.docker/config.json into the
// running container. It is a no-op for Desktop and Cloud engine kinds.
func SyncDockerConfigToContainer(ctx context.Context, dockerClient *client.Client, containerID string, engineKind types.ModelRunnerEngineKind) error {
Comment thread
areebahmeddd marked this conversation as resolved.
Comment thread
areebahmeddd marked this conversation as resolved.
// Do nothing for Desktop and Cloud engine kinds
if engineKind == types.ModelRunnerEngineKindDesktop || engineKind == types.ModelRunnerEngineKindCloud ||
Comment thread
sourcery-ai[bot] marked this conversation as resolved.
os.Getenv("_MODEL_RUNNER_TREAT_DESKTOP_AS_MOBY") == "1" {
return nil
}

dockerConfigPath := os.ExpandEnv("$HOME/.docker/config.json")
dockerConfigPath := filepath.Join(dockercliconfig.Dir(), "config.json")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I checked with the previous implementation and the current one they have different ways internally for example if someone hase set DOCKER_CONFIG env it would break. May be this current change is the correct way need to check with the authors @ericcurtin @ilopezluna

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest comparing against what moby/containerd does and copying that

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i used config.Dir() from docker/cli here because that's the same logic docker uses to locate its config directory

for the moby/containerd point.. my understanding is that this part is handled on the cli side rather than by the daemon itself

Copy link
Copy Markdown
Contributor

@sathiraumesh sathiraumesh Jun 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@areebahmeddd yes this might be the normal way but always make sure nothing breaks for backwards compatibility. if you check the implementations they differ a bit that's why i pointed it out.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you point out for me? 🤔

Copy link
Copy Markdown
Contributor

@sathiraumesh sathiraumesh Jun 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so this is from the cli package config implementation what you are using now

const (
	// EnvOverrideConfigDir is the name of the environment variable that can be
	// used to override the location of the client configuration files (~/.docker).
	//
	// It takes priority over the default, but can be overridden by the "--config"
	// command line option.
	EnvOverrideConfigDir = "DOCKER_CONFIG"

	// ConfigFileName is the name of the client configuration file inside the
	// config-directory.
	ConfigFileName = "config.json"
	configFileDir  = ".docker"
	contextsDir    = "contexts"
)

...

func Dir() string {
	initConfigDir.Do(func() {
		configDir = os.Getenv(EnvOverrideConfigDir)
		if configDir == "" {
			configDir = filepath.Join(getHomeDir(), configFileDir)
		}
	})
	return configDir
}

You see the implementation is different. thats what i was pointing to like it considers some extra env overrides may be the users are using now which might break

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i see .. let me try something

if s, err := os.Stat(dockerConfigPath); err != nil || s.Mode()&os.ModeType != 0 {
return nil
}
Expand Down Expand Up @@ -622,7 +622,7 @@ func CreateControllerContainer(ctx context.Context, dockerClient *client.Client,

// Copy Docker config file if it exists and we're the container creator.
if created && !vllmOnWSL {
if err := copyDockerConfigToContainer(ctx, dockerClient, resp.ID, engineKind); err != nil {
if err := SyncDockerConfigToContainer(ctx, dockerClient, resp.ID, engineKind); err != nil {
// Log warning but continue - don't fail container creation
printer.Printf("Warning: failed to copy Docker config: %v\n", err)
}
Expand Down
48 changes: 48 additions & 0 deletions cmd/cli/pkg/standalone/containers_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package standalone

import (
"context"
"os"
"path/filepath"
"testing"

"github.com/docker/model-runner/cmd/cli/pkg/types"
)

// TestSyncDockerConfigToContainer_NoopForDesktopAndCloud verifies that
// SyncDockerConfigToContainer skips Desktop and Cloud engine kinds.
Comment thread
areebahmeddd marked this conversation as resolved.
func TestSyncDockerConfigToContainer_NoopForDesktopAndCloud(t *testing.T) {
tmpDir := t.TempDir()
dockerDir := filepath.Join(tmpDir, ".docker")
if err := os.MkdirAll(dockerDir, 0o700); err != nil {
t.Fatalf("failed to create .docker dir: %v", err)
}
if err := os.WriteFile(filepath.Join(dockerDir, "config.json"), []byte(`{}`), 0o600); err != nil {
t.Fatalf("failed to write config.json: %v", err)
}
t.Setenv("HOME", tmpDir)

for _, engineKind := range []types.ModelRunnerEngineKind{
types.ModelRunnerEngineKindDesktop,
types.ModelRunnerEngineKindCloud,
} {
t.Run(engineKind.String(), func(t *testing.T) {
err := SyncDockerConfigToContainer(context.Background(), nil, "container-id", engineKind)
if err != nil {
t.Fatalf("SyncDockerConfigToContainer(%v) returned unexpected error: %v", engineKind, err)
}
})
}
}

// TestSyncDockerConfigToContainer_NoopWhenConfigMissing verifies that
// SyncDockerConfigToContainer skips when the host config file is absent.
func TestSyncDockerConfigToContainer_NoopWhenConfigMissing(t *testing.T) {
tmpDir := t.TempDir()
t.Setenv("HOME", tmpDir)

err := SyncDockerConfigToContainer(context.Background(), nil, "container-id", types.ModelRunnerEngineKindMoby)
if err != nil {
t.Fatalf("SyncDockerConfigToContainer returned unexpected error for missing config: %v", err)
}
}