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
- Mount the Docker socket: add `-v /var/run/docker.sock:/var/run/docker.sock` to the container run command or volumes in compose
- If socket is at a custom path, mount it to /var/run/docker.sock inside the container
- 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
- 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
- Always mount /var/run/docker.sock in the app container
- Check socket presence at container startup, not at call time
- Document the required volume in deployment templates
- Consider a dedicated host-command sidecar if socket mounting is restricted
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
- Failed to connect to Docker API: %v
- failed to connect to Docker API: %v
- failed to list containers: %w
- failed to read HTML template: %w
- failed to create Docker client: %v
AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05).
Data as JSON: /api/errors/f7e6c492d74758c6.
Report an issue: GitHub.