Skip to content
Latchkey LogoLatchkey home

Docker error during connect docker_engine in CI

Docker error during connect docker_engine on a Windows named pipe means the CLI never reached a daemon at all: it tried to open the pipe the engine listens on and found nothing there. On a Windows runner the reasons are short: the engine is not running yet, the client is pointed somewhere else, or the job wanted Linux containers and is on the wrong runner.

Diagram of the Windows named pipe transport and the three reasons a job cannot reach it
The client picks a transport from the context or the environment. On Windows that is a named pipe, and the pipe exists only while the engine is running.

What this error means

Every Docker command in the job fails the same way and fails instantly, including docker version and docker info, because nothing ever reached the engine. The URL in the message is the pipe path percent-encoded, which is what makes it look exotic: %2F%2F.%2Fpipe%2Fdocker_engine is just //./pipe/docker_engine. The Windows CLI talks to the engine over the npipe transport, written npipe:////./pipe/docker_engine, where Linux and macOS use unix:///var/run/docker.sock. The API version in that URL is whatever the client speaks, so it varies with the Docker version on the runner; the line below shows v1.24, because it is quoted from docker/for-win#13137 rather than from a run of ours. We have no recorded run for this one: our harness runs Linux latchkey-small runners.

The line docker/for-win#13137 reports (illustrative, not a recorded run)
error during connect: This error may indicate that the docker daemon is not running.: Get "http://%2F%2F.%2Fpipe%2Fdocker_engine/v1.24/containers/json": open //./pipe/docker_engine: The system cannot find the file specified.

What the client is actually trying to open

The Docker CLI decides where to send a request from the active context, or from DOCKER_HOST, and DOCKER_CONTEXT overrides DOCKER_HOST and the context set with docker context use. On Windows the endpoint is a named pipe; the documented scheme is npipe://[<name>], with npipe:////./pipe/docker_engine as the example. A pipe is created by the process that serves it, so it is absent whenever the engine is not up.

That gives you two checks and they take seconds. Ask which endpoint the client would use, then ask whether the service behind it is running.

CheckWhat it tells you
docker context lsWhich endpoint the client will use, and which context is active
Get-Service dockerWhether the engine is running at all on this machine
echo $env:DOCKER_HOSTWhether something earlier in the job redirected the client
docker versionWhether the failure is transport or something later in the API call

Common causes

The engine is not running yet

The most common cause on a hosted Windows runner, and the reason the error can be intermittent: the first step runs before the service is ready, and a rerun of the same workflow passes. The error text itself hints at it, telling you the daemon may not be running.

The client is pointed at another endpoint

A DOCKER_HOST exported by an earlier step, a context created for a remote builder, or a leftover context from a tool that set one all send the request somewhere with no listener. DOCKER_CONTEXT wins over DOCKER_HOST, so both have to be checked rather than one.

The job needs Linux containers and is on Windows

Service containers, job containers and Docker-based actions are Linux-only on GitHub runners. In our experience this is the cause when the error appears the same day somebody added services: to a Windows job, and no amount of engine restarting changes it.

The user in the step cannot open the pipe

A variant of the same message reads "Access is denied" instead of "cannot find the file specified", which is a permission problem rather than a missing engine. docker/for-win#14716 reports it for a secondary user account. The fix is the account or group membership, not the service.

How to fix it

Start the engine as the first step, and wait for it to answer

  1. Run Start-Service docker before anything that uses Docker.
  2. Poll docker version until it returns, with a short sleep between attempts.
  3. Print docker info once so the log records which container mode is active.
PowerShell
Start-Service docker
docker version
docker info --format "{{.OSType}}"

Check the context before blaming the daemon

List the contexts and read the endpoint column. If the active context points at anything other than the local pipe, switch it explicitly in the job rather than relying on whatever the image or a previous step left behind.

PowerShell
docker context ls
docker context use default

Move Linux-container work to a Linux runner

Service containers, job containers and container actions need a Linux runner on GitHub. Split the job: keep the Windows-specific steps on windows-latest and run the containerized parts on ubuntu-latest, rather than trying to reproduce Linux behavior on Windows.

.github/workflows/ci.yml
jobs:
  windows-build:
    runs-on: windows-latest
  linux-integration:
    runs-on: ubuntu-latest
    services:
      redis:
        image: redis:7

Read which half of the message you have

The two endings are two different problems. "The system cannot find the file specified" means no pipe, so the engine is not running. "Access is denied" means the pipe is there and this account may not open it, which points at the user the step runs as.

PowerShell
docker version 2>&1 | Select-String "pipe/docker_engine"

On a Windows runner, start the service and wait for it

The runner image ships Docker: the windows-2025 image README listed Docker 29.7.2 and Docker Compose 2.40.3 when we read it on 2026-09-20. What it does not promise is that the service is running and ready at the moment your first step executes. A start plus a short wait removes the whole class of flaky first-step failures, and it is idempotent, so it costs nothing when the engine was already up.

Wait for the engine to answer rather than for a fixed number of seconds. A sleep long enough to be safe is long enough to be a tax on every job.

.github/workflows/ci.yml
- name: Make sure the engine is up
  shell: pwsh
  run: |
    Start-Service docker -ErrorAction SilentlyContinue
    $deadline = (Get-Date).AddMinutes(2)
    while ((Get-Date) -lt $deadline) {
      docker version --format "{{.Server.Version}}" 2>&1 | Out-Host
      if ($LASTEXITCODE -eq 0) { break }
      Start-Sleep -Seconds 3
    }
    docker info --format "{{.OSType}} containers"

Many jobs that hit this belong on a Linux runner

GitHub is explicit about the limits, in the page we read on 2026-09-20: if your workflows use Docker container actions, job containers or service containers, then you must use a Linux runner, and on GitHub-hosted runners that means an Ubuntu runner. A workflow that grew a container: key or a services: block on a Windows job has a problem no engine start will fix.

The question to ask is what the containers are for. Building or testing Windows containers needs a Windows runner. Everything else, including almost every service container and every Docker-based action, runs on Linux, and moving the job is less work than making Windows behave like Linux.

.github/workflows/ci.yml
jobs:
  integration:
    runs-on: ubuntu-latest   # services: and container: need Linux
    services:
      postgres:
        image: postgres:17

What the runner does about it

Nothing here, and the honest reason is that this is not a failure our runners see: Latchkey runner labels are Linux, latchkey-small through latchkey-xlarge, so a job on one of them talks to a unix socket and never opens a named pipe. That is also why this page carries no reproduction and does not pretend to.

If your Windows jobs exist to build Windows containers, keep them on Windows and add the engine check above as the first step. If they exist because the workflow was written on a Windows laptop, moving them to Linux removes this error and usually several minutes of startup with it.

How to prevent it

  • Start and health-check the engine as the first step of every Windows job.
  • Keep service containers and container actions on Linux runners.
  • Set the context explicitly when any step in the job creates one.
  • Print the Docker version and OS type once per job, so the log answers the first question.

Frequently asked questions

What is //./pipe/docker_engine?
It is the named pipe the Docker engine listens on for Windows clients, written in URL form as npipe:////./pipe/docker_engine. The CLI percent-encodes it into the request URL, which is why the error looks like a broken HTTP address. A pipe only exists while the process serving it is running.
Can I run Linux containers on a windows-latest runner?
Not for the things GitHub Actions builds around them. The documentation states that Docker container actions, job containers and service containers require a Linux runner, and an Ubuntu runner on GitHub-hosted machines. Windows runners are for Windows containers and for work that does not need containers at all.
Why does the error appear in one step and not the next?
Because the first step ran before the engine was ready, or because a step changed where the client points. Environment set inside one step does not carry to the next unless it was written to the job environment, so a DOCKER_HOST exported in a script affects only that step.
Is this the same as permission denied on docker.sock?
It is the same class of failure with a different transport. On Linux the client talks to a unix socket and a missing or unreadable socket produces a permission or connection error; on Windows the transport is a named pipe and you get this message. Both mean the CLI never reached a daemon.

Related guides

References

Linux containers need a Linux runner. Latchkey runs them at $0.0025/min at 2 vCPU. Start free → 30-day trial · No credit card