Skip to content

Honor the active docker context when resolving the Docker host (not just DOCKER_HOST) #1069

Description

@mpasternak

Proposal

testcontainers should resolve the Docker daemon the way the docker CLI does — by honoring the active docker context — instead of only consulting DOCKER_HOST / ~/.testcontainers.properties and otherwise falling back to docker.from_env()'s default socket.

Why

get_docker_host() (in testcontainers/core/docker_client.py) is:

def get_docker_host() -> Optional[str]:
    return c.tc_properties_get_tc_host() or os.getenv("DOCKER_HOST")

When neither tc.host nor DOCKER_HOST is set, DockerClient.__init__ falls through to docker.from_env(), which connects to the platform-default socket (/var/run/docker.sock). But from_env() — unlike the docker CLI — ignores the active docker context.

On OrbStack, colima, rootless Docker, or a machine where Docker Desktop is installed but stopped, the daemon lives on a non-default socket and /var/run/docker.sock is absent or a dangling symlink. So PostgresContainer(...).start() (really, DockerClient() construction) raises:

docker.errors.DockerException: Error while fetching server API version:
('Connection aborted.', FileNotFoundError(2, 'No such file or directory'))

…even though docker ps works fine, because the CLI follows the active context. This appears to be the root cause behind a recurring class of reports — e.g. #757 (same error, reporter pasted docker context ls) and the Mac networking cases in #159.

To reproduce

# OrbStack (or colima) is the active context; Docker Desktop is installed but
# stopped, so /var/run/docker.sock is a dangling symlink to a non-existent socket.
$ docker context show
orbstack
$ docker ps            # works fine
$ unset DOCKER_HOST
$ python -c "from testcontainers.postgres import PostgresContainer as P; P('postgres:16').start()"
docker.errors.DockerException: Error while fetching server API version:
('Connection aborted.', FileNotFoundError(2, 'No such file or directory'))

Proposed fix

Have get_docker_host() consult the active context as a tier between DOCKER_HOST and the from_env() default, mirroring the CLI's precedence:

  1. tc.host (~/.testcontainers.properties)
  2. DOCKER_HOST
  3. active docker context endpoint ← new
  4. from_env() default socket

docker-py already exposes this:

from docker.context import ContextAPI

ctx = ContextAPI.get_current_context()   # reads ~/.docker/config.json -> currentContext
host = ctx.Host if ctx else None         # e.g. unix:///Users/me/.orbstack/run/docker.sock

Resolution failures (older SDK without context support, malformed ~/.docker/config.json, no current context) should be swallowed so behavior falls back to today's from_env().

Bonus: get_docker_socket() (used for Ryuk's socket bind-mount) also routes through from_env(), so honoring the context there too keeps the reaper's socket consistent with the client.

Runtime environment

  • testcontainers 4.14.2, docker (docker-py) 7.1.0, Python 3.13
  • macOS (Apple Silicon); OrbStack active, Docker Desktop installed-but-stopped
$ docker context ls
NAME            DESCRIPTION                               DOCKER ENDPOINT
default         Current DOCKER_HOST based configuration   unix:///var/run/docker.sock
orbstack *      OrbStack                                  unix:///Users/me/.orbstack/run/docker.sock

Workaround

Exporting DOCKER_HOST from the active context before any testcontainers client is built resolves both the client and the Ryuk socket:

import os
from docker.context import ContextAPI

if not os.getenv("DOCKER_HOST"):
    ctx = ContextAPI.get_current_context()
    if ctx and ctx.Host:
        os.environ["DOCKER_HOST"] = ctx.Host

Happy to open a PR along these lines if the maintainers agree on the approach.

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