Curl is my tool of choice for exploring new HTTP APIs. It's simple, full-featured, and I'm very productive with it. Naturally, when I want to learn the docker remote API, I turn to curl.

I am running boot2docker (installed via docker machine) on OS X Yosemite.

boot2docker runs docker over https, which uses TLS client authentication and signs certificates using a custom certificate authority (CA). In order to use the docker remote API with curl, I must supply the cert, key, and CA files. These files are conveniently located in $DOCKER_CERT_PATH/ca.pem, $DOCKER_CERT_PATH/cert.pem, and $DOCKER_CERT_PATH/key.pem, so here is my first attempt.

$ curl -i --cacert $DOCKER_CERT_PATH/ca.pem --cert $DOCKER_CERT_PATH/cert.pem --key $DOCKER_CERT_PATH/key.pem
curl: (58) SSL: Can't load the certificate "/Users/jordan/.docker/machine/machines/dev/cert.pem" and its private key: OSStatus -25299

That's weird. I supplied a private key, but curl can't load it. What's going on here?

After a quick search for the error message, I found this important note for curl users on OS X Yosemite.

2. You can now use the -E/--cert option, for the purpose of authenticating with a TLS host using a client certificate and private key. When using the option, you can either specify:

2a. The name of the certificate as it appears in your Keychain (the certificate's private key has to be present in the same Keychain in order for this to work),


2b. A path to a PKCS#12-encoded file on a disk, which contains both the certificate and the private key. (If it's in the present working directory, you need to add a ./ to the start of the path, or curl will assume you want to search the Keychain.)

Option 2b sounds like the choice in my case, so now I just need to figure out how to generate this PKCS#12-encoded file. Fortunately, another quick search reveals how to generate a PKCS12 via OpenSSL. So to combine my client cert and private key into a single file, I use the following command:

openssl pkcs12 -export -in $DOCKER_CERT_PATH/cert.pem -inkey $DOCKER_CERT_PATH/key.pem -out $DOCKER_CERT_PATH/cert.pfx -password pass:supersecret

Now I can successfully connect to the docker remote api with the following curl command.

curl --cacert $DOCKER_CERT_PATH/ca.pem --cert $DOCKER_CERT_PATH/cert.pfx --pass supersecret

That's a lot of arguments to remember and to type, so let's put them in a variable.

docker_api="--cacert $DOCKER_CERT_PATH/ca.pem --cert $DOCKER_CERT_PATH/cert.pfx --pass supersecret"
curl $docker_api/images/json

Now making requests to the API via curl is trvial, but we still have some gnarly JSON output to deal with. A new tool I've been using to work with large blobs from the command-line is jq. You can install it with homebrew brew install jq. And now you can actually read JSON responses in your terminal.

curl $docker_api/images/json | jq . | less
    "Id": "8c185118488e194f5d42fb7aa5239212b2f35d60f322905100092d68ecc5b4f6",
    "ParentId": "eb7e1e522da91d060b7ca6f137712c9c728e9ab9b98b5c6672cdd391a7b4960d",
    "RepoTags": [
    "RepoDigests": [],
    "Created": 1436412277,
    "Size": 0,
    "VirtualSize": 531649317,
    "Labels": {}

That's still a lot of data to process. I'm only interested in the human-readable names of the images for now, so I can use jq to find those for me.

curl $docker_api/images/json | jq '.[] | .RepoTags[0]' | grep -v none

Still with me? Good! Now it's time to go learn the docker remote API! See you later.