Couldn't connect to Docker daemon on Mac OS X

ndequeker picture ndequeker · Mar 13, 2016 · Viewed 59.4k times · Source

I would like to run multi-container application using docker-compose on Mac OS X El Capitan (v10.11.2).

However, the command $ docker-compose up command complains that it can't connect to the Docker daemon.

ERROR: Couldn't connect to Docker daemon - you might need to run docker-machine start default.

Only after executing $ eval "$(docker-machine env default)" I do have access to the docker-compose command.

Why is this and how can I overcome this extra step?

Answer

mklement0 picture mklement0 · Mar 14, 2016

Update for Docker versions that come with Docker.app

The Docker experience on macOS has improved since this answer was posted:

  • The only prerequisite is now for Docker.app to be running. Note that starting it on demand takes a while, because the underlying Linux VM must be started.

  • Any shell then has access to Docker functionality.

By default, Docker.app is launched at login time (you can change that via its preferences).
If you instead prefer starting and stopping Docker on demand from the command line, here are bash scripts that do that, docker-start and docker-stop; place them anywhere in your $PATH.

When docker-start launches Docker.app, it waits until Docker has finished starting up and is ready.


docker-start:

#!/usr/bin/env bash

case $1 in
  -h|--help)
    echo $'usage: docker-start\n\nStarts Docker (Docker.app) on macOS and waits until the Docker environment is initialized.'
    exit 0
    ;;
esac
(( $# )) && { echo "ARGUMENT ERROR: Unexpected argument(s) specified. Use -h for help." >&2; exit 2; }

[[ $(uname) == 'Darwin' ]] || { echo "This function only runs on macOS." >&2; exit 2; }

echo "-- Starting Docker.app, if necessary..."

open -g -a Docker.app || exit

# Wait for the server to start up, if applicable.  
i=0
while ! docker system info &>/dev/null; do
  (( i++ == 0 )) && printf %s '-- Waiting for Docker to finish starting up...' || printf '.'
  sleep 1
done
(( i )) && printf '\n'

echo "-- Docker is ready."

docker-stop:

#!/usr/bin/env bash

case $1 in
  -h|--help)
    echo $'usage: docker-stop\n\nStops Docker (Docker.app) on macOS.'    
    exit 0
    ;;
esac
(( $# )) && { echo "ARGUMENT ERROR: Unexpected argument(s) specified. Use -h for help." >&2; exit 2; }

[[ $(uname) == 'Darwin' ]] || { echo "This function only runs on macOS." >&2; exit 2; }

echo "-- Quitting Docker.app, if running..."

osascript - <<'EOF' || exit
tell application "Docker"
  if it is running then quit it
end tell
EOF

echo "-- Docker is stopped."
echo "Caveat: Restarting it too quickly can cause errors."

Original, obsolete answer:

Kevan Ahlquist's helpful answer shows what commands to add to your Bash profile (~/.bash_profile) to automatically initialize Docker on opening an interactive shell.

Note that you can always initialize Docker in a new shell tab/window by opening application /Applications/Docker/Docker Quickstart Terminal.app (e.g., via Spotlight).
From an existing shell, you can invoke it as open -a 'Docker Quickstart Terminal.app' (which also opens a new shell tab).
What this answer offers is a convenient way to start Docker in the current shell.

Adding the Bash shell functions below - docker-start and docker-stop - improves on Kevan's approach in the following respects:

  • You can run docker-start on demand, without the overhead of starting the VM on opening the shell (once the Docker VM is running, initialization is much faster, but still takes a noticeable amount of time).
    (Of course, you can still opt to invoke docker-start right from your profile.)

  • docker-stop allows stopping Docker and cleaning up the environment variables on demand.

  • The functions ensure that Docker's error messages are not suppressed, and they pass Docker error exit codes through.

  • Additional status information is provided.

  • You may pass a VM name as a parameter; default is default.

Example:

$ docker-start
-- Starting Docker VM 'default' (`docker-machine start default`; this will take a while)...
Starting "default"...
(default) Check network to re-create if needed...
(default) Waiting for an IP...
Machine "default" was started.
Waiting for SSH to be available...
Detecting the provisioner...
Started machines may have new IP addresses. You may need to re-run the `docker-machine env` command.
-- Setting DOCKER_* environment variables (`eval "$(docker-machine env default)"`)...
DOCKER_CERT_PATH="/Users/jdoe/.docker/machine/machines/default"
DOCKER_HOST="tcp://192.168.99.100:2376"
DOCKER_MACHINE_NAME="default"
DOCKER_TLS_VERIFY="1"
-- Docker VM 'default' is running.


$ docker-stop
-- Stopping Docker VM 'default' (`docker-machine stop default`)...
Stopping "default"...
Machine "default" was stopped.
-- Unsetting DOCKER_* environment variables (DOCKER_CERT_PATH, DOCKER_HOST, DOCKER_MACHINE_NAME, DOCKER_TLS_VERIFY)...
-- Docker VM 'default' is stopped.

Shell functions for on-demand starting and stopping of Docker (place them in, e.g., ~/.bash_profile for global availability in your interactive shells).

Note: The functions work in bash, ksh, and zsh, but in ksh you have to rename them so as not to include a '-' in the function names.

function docker-start {
  typeset vm=${1:-default} sts
  case $vm in
    -h|--help)
      echo $'usage: docker-start [<vm>]\n\nEnsures that the specified/default Docker VM is started\nand the environment is initialized.'
      return 0
      ;;
  esac
  sts=$(docker-machine status "$vm") || return
  [[ $sts == 'Running' ]] && echo "(Docker VM '$vm' is already running.)" || { 
    echo "-- Starting Docker VM '$vm' (\`docker-machine start "$vm"\`; this will take a while)..."; 
    docker-machine start "$vm" || return
  }
  echo "-- Setting DOCKER_* environment variables (\`eval \"\$(docker-machine env "$vm")\"\`)..."
  # Note: If the machine hasn't fully finished starting up yet from a
  #       previously launched-but-not-waited-for-completion `docker-machine status`,
  #       the following may output error messages; alas, without signaling failure
  #       via the exit code. Simply rerun this function to retry.
  eval "$(docker-machine env "$vm")" || return
  export | grep -o 'DOCKER_.*'
  echo "-- Docker VM '$vm' is running."
}

function docker-stop {
  typeset vm=${1:-default} sts envVarNames fndx
  case $vm in
    -h|--help)
      echo $'usage: docker-stop [<vm>]\n\nEnsures that the specified/default Docker VM is stopped\nand the environment is cleaned up.'
      return 0
      ;;
  esac
  sts=$(docker-machine status "$vm") || return
  [[ $sts == 'Running' ]] && { 
    echo "-- Stopping Docker VM '$vm' (\`docker-machine stop "$vm"\`)...";
    docker-machine stop "$vm" || return
  } || echo "(Docker VM '$vm' is not running.)"
  [[ -n $BASH_VERSION ]] && fndx=3 || fndx=1 # Bash prefixes defs. wit 'declare -x '
  envVarNames=( $(export | awk -v fndx="$fndx" '$fndx ~ /^DOCKER_/ { sub(/=.*/,"", $fndx); print $fndx }') )
  if [[ -n $envVarNames ]]; then
    echo "-- Unsetting DOCKER_* environment variables ($(echo "${envVarNames[@]}" | sed 's/ /, /g'))..."
    unset "${envVarNames[@]}"
  else
    echo "(No DOCKER_* environment variables to unset.)"
  fi
  echo "-- Docker VM '$vm' is stopped."
}