Billionmail/BillionMail · error

docker.sock not mounted, cannot access Docker API

Error message

docker.sock not mounted, cannot access Docker API

What it means

ExecHostCommand runs host-side commands by spawning a throwaway alpine container with the host's docker.sock bind-mounted. Before doing anything it stats /var/run/docker.sock; if the socket isn't mounted into the calling container, the Docker API is unreachable for this trick and the error is returned with a -1 exit code.

Source

Thrown at core/internal/service/dockerapi/dockerapi.go:268

type HostCommandResult struct {
	ExitCode int    `json:"exit_code"` // Command exit status code
	Output   string `json:"output"`    // Command output
	Error    string `json:"error"`     // Execution error
	Duration int64  `json:"duration"`  // Execution time (milliseconds)
}

// ExecHostCommand executes a command on the host through Docker API
// Principle: Creates a temporary privileged container mounting the host's root directory,
// executing commands inside the container that actually operate on the host's file system
func (d *DockerAPI) ExecHostCommand(ctx context.Context, command []string) (*HostCommandResult, error) {
	startTime := time.Now()
	result := &HostCommandResult{
		ExitCode: -1,
	}

	// Check if docker.sock is mounted
	if _, err := os.Stat("/var/run/docker.sock"); os.IsNotExist(err) {
		return nil, fmt.Errorf("docker.sock not mounted, cannot access Docker API")
	}

	// Specify the base image to use
	baseImage := "alpine:latest"

	// Check if the image exists, pull it if it doesn't
	_, err := d.client.ImageInspect(ctx, baseImage)
	if err != nil {
		// Image doesn't exist, try to pull it
		reader, err := d.client.ImagePull(ctx, baseImage, image.PullOptions{})
		if err != nil {
			return nil, fmt.Errorf("failed to pull image: %w", err)
		}
		defer reader.Close()

		// Wait for the image pull to complete
		_, _ = io.Copy(io.Discard, reader)
	}

View on GitHub (pinned to fc36c76c05)

Solutions

  1. Mount the Docker socket: add `-v /var/run/docker.sock:/var/run/docker.sock` to the container run command or volumes in compose
  2. If socket is at a custom path, mount it to /var/run/docker.sock inside the container
  3. On K8s, use a hostPath volume (with the security tradeoffs that entails) or replace the feature with an explicit Docker API client pointing at DOCKER_HOST
  4. Move the firewall-rule logic to a host-level sidecar/service if socket mounting is disallowed

Example fix

// before (docker-compose.yml)
app:
  # no volumes
// after
app:
  volumes:
    - /var/run/docker.sock:/var/run/docker.sock
Defensive patterns

Strategy: validation

Validate before calling

if _, err := os.Stat("/var/run/docker.sock"); err != nil {
    return fmt.Errorf("docker.sock unavailable, mount it before use: %w", err)
}

Try / catch

res, err := api.ExecHostCommand(ctx, cmd)
if err != nil && strings.Contains(err.Error(), "docker.sock not mounted") {
    return fmt.Errorf("deploy without docker.sock volume; host command unavailable: %w", err)
}

Prevention

When it happens

Trigger: Calling ExecHostCommand / ExecHostShellCommand (or RBAC firewall helpers addNewRules / deleteOldRules) from inside a container started without `-v /var/run/docker.sock:/var/run/docker.sock`.

Common situations: Deploying the app container with a trimmed compose file that omits the docker.sock volume; hardened/K8s deployments that forbid socket mounting; socket path remapped to a nonstandard location.

Related errors


AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05). Data as JSON: /api/errors/f7e6c492d74758c6. Report an issue: GitHub.