How to Use Docker Inspect Command
One of the essential Docker commands is docker inspect. It lets you extract information about various docker objects, knowing how to use it is something EVERYONE should know about.
In case you were wondering, Docker objects or resources are simply things like containers, volumes, networks etc.
The main strength of inspect
comes from its formatting capabilities.
For example, you can extract the IP address of a running container by inspecting it, and formatting in a specific way.
➟ docker container inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' nginx
172.17.0.2
Docker uses go-templates for formatting its output.
In this article, first I'll go through the basics of Docker inspect command, and then I'll focus on how to format the output to your specific needs.
What does Docker inspect do?
Inspect provides you with a bunch of metadata about all the different objects managed by docker. The kind of information varies from object to object.
For example, if you inspect a volume, you'll get information related to when it was created, the volume driver in use, location in the host filesystem, labels etc.
If it is a network that you're inspecting, then you'll get things like its subnet, gateway, connected containers and their IP addresses, labels and other information.
To understand better what inspect provides for a given object, I recommend you run the commands and see for yourself.
What are the objects that can be inspected?
In docker, an object or object type is all the constructs that are controlled by docker. This includes the following:-
- Containers.
- Images.
- Networks.
- Volumes.
- Contexts.
- Plugins.
- Nodes (swarm object).
- Services (swarm object).
- Secrets (swarm object).
- Configs (swarm object).
Using Docker inspect command
There are two ways you can use the inspect
sub-command.
docker inspect [object] [options]
docker [object_type] inspect [object] [options]
The second method is the one you should be using always. The inspect
sub-command provides a pretty formatted JSON output, I'll get into that in a moment.
Create a volume named unique
.
docker volume create unique
Now create a network named the same, unique
.
docker network create unique
Now let's try to inspect the object named unique
using the first syntax.
docker inspect unique
My output:-
➟ docker inspect unique
[
{
"Name": "unique",
"Id": "09a7e2163ee058b1057d95599f764d571ec6a42a5792803dc125e706caa525b0",
"Created": "2021-05-07T15:47:20.341493099+05:30",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.21.0.0/16",
"Gateway": "172.21.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {},
"Options": {},
"Labels": {}
}
]
In my system, as you can see inspect
inspected the network, but what if I intended to inspect the volume?
This is the problem with docker inspect
, when you have two different objects named the same, you can't just use docker inspect [object_name]
. To inspect exactly what you want, you'll need to either use the ID of the object, or use the --type=[object_type]
option. You can write the previous command with the --type
option like so:-
docker inspect --type=volume unique
Although this works, I believe this is unnecessary since we already have the other syntax. You can just use the object specific sub-command like I'm doing here:-
docker volume inspect unique
It is less to write, and a lot easier to read.
Some useful Docker inspect command examples
In this section, I'm going to record a list of common queries and what the relevant inspect command would look like to get those information.
Container queries
For the examples, I'll have a sample nginx container running, and all the commands will be run against this running container. The command I used to run this container:-
docker container run \
--rm --name nginx \
-p target=80,published=127.0.0.1:8081,protocol=tcp \
-p target=80,published=[::1]:8081,protocol=tcp \
-e ENV_VAR=somevalue \
-e ENV_VAR2=linux \
-v $PWD:/mnt:ro \
-v /tmp:/tmp:ro \
-d nginx
1. ID of a container by name
You can get the ID of the container using the following command:-
docker container inspect -f '{{.Id}}' [container_name]
Example:-
➟ docker container inspect -f '{{.Id}}' nginx
0409779fc2d976387170d664a6aed5ee80a460f8a8dd02c44a02af97df0bb956
2. Container's main process
The main container process is basically ENTRYPOINT
+ CMD
. The printf command will format the output to give the required information.
docker container inspect -f '{{printf "%s " .Path}}{{range .Args}}{{printf "%s " .}}{{end}}' [container_name|id]
Example:-
➟ docker container inspect -f '{{printf "%s " .Path}}{{range .Args}}{{printf "%s " .}}{{end}}' nginx
/docker-entrypoint.sh nginx -g daemon off;
3. Listing the port bindings
The following command lists all the container-to-host port bindings.
docker container inspect -f '{{range $target, $published := .NetworkSettings.Ports}}{{range $published}}{{printf "%s -> %s:%s\n" $target .HostIp .HostPort}}{{end}}{{end}}' [container_name|id]
Example:-
➟ docker container inspect -f '{{range $target, $published := .NetworkSettings.Ports}}{{range $published}}{{printf "%s -> %s:%s\n" $target .HostIp .HostPort}}{{end}}{{end}}' nginx
80/tcp -> ::1:8081
80/tcp -> 127.0.0.1:8081
You can achieve the same with docker container port
command.
4. Listing its IP addresses
A container can be connected to multiple networks, instead of printing one of those many IP addresses, you can print all of those IP addresses with this command.
docker container inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' [container_name|id]
Example:-
➟ docker container inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' nginx
172.17.0.2
5. Listing the environment variables
You can list the environment variable of a container as well.
docker container inspect -f '{{range .Config.Env}}{{printf "%s\n" .}}{{end}}' [container_name|id]
Example:-
➟ docker container inspect -f '{{range .Config.Env}}{{printf "%s\n" .}}{{end}}' nginx
ENV_VAR=somevalue
ENV_VAR2=linux
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
NGINX_VERSION=1.19.10
NJS_VERSION=0.5.3
PKG_RELEASE=1~buster
6. Listing the volumes/bind mounts along with the mode
The following command prints the bind mounts in this format, "[source] -> [destination], mode: [mode]".
docker container inspect -f '{{range .Mounts}}{{printf "%s -> %s, mode: %s\n" .Source .Destination .Mode}}{{end}}' [container_name|id]
Example:-
➟ docker container inspect -f '{{range .Mounts}}{{printf "%s -> %s, mode: %s\n" .Source .Destination .Mode}}{{end}}' nginx
/home/debdut -> /mnt, mode: ro
/tmp -> /tmp, mode: ro
Volume queries
There's not much to inspecting a volume except for knowing the host location, which is in data-dir/volumes
. You can get that information with the following command:-
docker volume inspect -f '{{.Mountpoint}}' [volume_name|id]
Example:-
➟ docker volume create unique
unique
~
➟ docker volume inspect -f '{{.Mountpoint}}' unique
/var/lib/docker/volumes/unique/_data
Network queries
There are two queries that I personally find myself going for frequently, one is knowing a networks subnet and all the containers that are connected to that network and the IPs associated with them.
For this I created a simple network with the docker network create unique
command.
1. Getting the subnet
To get the subnet, use the following command:-
docker network inspect -f '{{range .IPAM.Config}}{{.Subnet}}{{end}}' [network_name|id]
Example:-
➟ docker network inspect -f '{{range .IPAM.Config}}{{.Subnet}}{{end}}' unique
172.21.0.0/16
2. Listing the containers connected along with their IP addresses
The command looks like this,
docker network inspect -f '{{range .Containers}}{{printf "%s -> %s\n" .Name .IPv4Address}}{{end}}' [network_name|id]
Example:-
➟ docker network inspect -f '{{range .Containers}}{{printf "%s -> %s\n" .Name .IPv4Address}}{{end}}' unique
cranky_wescoff -> 172.21.0.5/16
nginx -> 172.21.0.2/16
upbeat_carson -> 172.21.0.3/16
objective_jones -> 172.21.0.4/16
Formatting the output of Docker inspect command
inspect
provides us with a JSON array for the output, which you can filter using something like jq
. So if you're experienced in jq
, you might want to just use it. The problem with jq
is that it doesn't come preinstalled in most of the Linux distributions, while the default formatting mechanism of docker .. inspect
is already there, and it is very powerful.
Docker uses go-templates to format its output. This article is not going to be about go-templates, but if you want to learn more, you can read about it here.
Internally JSONs are represented using various Go data structures. That's why go templates actually work with go data types. Since I don't want to explain what these data structures are, instead of using those terms, I'll use JSON terms to make it more understandable.
Extracting simple fields
Consider a JSON object like the following:-
{
"mary": 43,
"john": 44
}
Let's say you want to extract the information that is associated with the key mary
. To do so what you use is the dot [.] notation, where you prefix the key with a dot, and add keys recursively for any nested keys. This is the same as jq
. So .mary
here would be 43.
Consider the following JSON now.
{
"mary": {
"jane": 43,
"soyas": 56
},
"john": 65
}
In this case .mary.jane
would be 43, and similarly .mary.soyas
would be 56.
A similar syntax can be used with go-templates. To format the output, you need to pass the template to -f
or --format
option of the inspect
sub-command. Let's revise the output of the volume inspection.
[
{
"CreatedAt": "2021-05-07T15:53:10+05:30",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/unique/_data",
"Name": "unique",
"Options": {},
"Scope": "local"
}
]
If you want to know the Mountpoint
, you'd use the following command:-
docker volume inspect -f '{{.Mountpoint}}' unique
➟ docker volume inspect -f '{{.Mountpoint}}' unique
/var/lib/docker/volumes/unique/_data
You're probably noticing the curly braces right there, these are like blocks, expressions are encapsulated inside these blocks.
Let's try something nested now. Inspect the network, and look for the IPAM
section.
➟ docker network inspect unique
<snipped>
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
<snipped>
Looking at this you can pretty easily figure out what this network's driver is. But instead of looking for it like this, you can format it out of the whole JSON output.
Notice the Driver
key is nested within IPAM
. So the dot expression to extract the driver would be .IPAM.Driver
. See it in action:-
➟ docker network inspect -f '{{.IPAM.Driver}}' unique
default
Looping over objects or lists (range)
JSON objects are like associative arrays in Bash, or Hashes, where the keys are strings and the values can be of any data type.
To make you understand this bit easier I'll start with a practical example. Consider the .NetworkSettings.Networks
section in an inspect result of a container. It lists the networks the container is connected to, and for each network, some more associated details like the IP address.
Think if someone asks you to tell him/her the IP address of a container. Choosing just one network and the associated IP doesn't make much sense, what would be better is to list all the IP addresses that are associated with all the networks.
You can achieve this with a simple bash for loop if you already know the network names. Like in my case I can do something like the following:-
for network in bridge unique; do
docker container inspect -f \
"{{.NetworkSettings.Networks.$network.IPAddress}}" nginx
done
But obviously this is limiting in a large scale since we can't possibly always remember all the network names.
You can mitigate this problem by using the template action range
. range
goes over a map (an associative array or JSON object) and provides us with not the key, but the values for each iteration (this behavior is changeable).
So in this case, you can write a block like {{range .NetworkSettings.Networks}}
to loop over the values of each networks or the data associated with each networks, and from that you can extract the IP address like you'd from a normal JSON-like structure, i.e. {{.IPAddress}}}
. One thing to remember is to always end the whole template that starts with range
, with {{end}}
.
Putting all that together, you can rewrite the previous for loop right this way:-
docker container inspect -f \
'{{range .NetworkSettings.Networks}}
{{.IPAddress}}{{end}}' nginx
Sample output:-
➟ docker container inspect -f \
> '{{range .NetworkSettings.Networks}}
> {{.IPAddress}}{{end}}' nginx
172.17.0.2
172.21.0.2
Using the index
function on arrays & objects
You can use the index
function to extract parts from your JSON object or array. If the structure is a JSON object, you'd use {{index .Field "key"}}
, if the structure is a JSON array, you'd use {{index .Field index}}
.
In the previous example you printed all the IP addresses of a container. Let's say you know one of the networks it is connected to (bridge) and want to print the IP address associated with that network. You can do that with index
function like this:-
docker container inspect -f '{{(index .NetworkSettings.Networks "bridge").IPAddress}}' nginx
Output:-
➟ docker container inspect -f '{{(index .NetworkSettings.Networks "bridge").IPAddress}}' nginx
172.17.0.2
Using json
function
The data exported after formatting is not in JSON, it's in some go data structure. But you can convert that to JSON by using the json
function.
The top level of the JSON object is .
. So to print that you'd do something like this:-
docker network inspect -f '{{.}}' unique
➟ docker network inspect -f '{{.}}' unique
{unique 09a7e2163ee058b1057d95599f764d571ec6a42a5792803dc125e706caa525b0 2021-05-07 15:47:20.341493099 +0530 IST local bridge false {default map[] [{172.21.0.0/16 172.21.0.1 map[]}]} false false false {} false map[2646cbbde5efc218bb6f3a5c882f8eb9e3e4331d090ad46ccc0a2eec9c2eea1b:{nginx c0291394a48f7e8e8aa98fd31631eb00e68daacbee9cf24bac530f16359d051d 02:42:ac:15:00:02 172.21.0.2/16 }] map[] map[] [] map[]}
What you're seeing here is a big struct consisting of other structs basic data types and maps. These are not very readable, nor usable outside of go. But you can convert these to JSON using the json
function. Just prepend a field with json
like I'm doing here:-
➟ docker network inspect -f '{{json .}}' unique
➟ docker network inspect -f '{{json .}}' unique
{"Name":"unique","Id":"09a7e2163ee058b1057d95599f764d571ec6a42a5792803dc125e706caa525b0","Created":"2021-05-07T15:47:20.341493099+05:30","Scope":"local","Driver":"bridge","EnableIPv6":false,"IPAM":{"Driver":"default","Options":{},"Config":[{"Subnet":"172.21.0.0/16","Gateway":"172.21.0.1"}]},"Internal":false,"Attachable":false,"Ingress":false,"ConfigFrom":{"Network":""},"ConfigOnly":false,"Containers":{"2646cbbde5efc218bb6f3a5c882f8eb9e3e4331d090ad46ccc0a2eec9c2eea1b":{"Name":"nginx","EndpointID":"c0291394a48f7e8e8aa98fd31631eb00e68daacbee9cf24bac530f16359d051d","MacAddress":"02:42:ac:15:00:02","IPv4Address":"172.21.0.2/16","IPv6Address":""}},"Options":{},"Labels":{}}
To make it look a bit better pipe it to jq
.
➟ docker network inspect -f '{{json .}}' unique | jq
{
"Name": "unique",
"Id": "09a7e2163ee058b1057d95599f764d571ec6a42a5792803dc125e706caa525b0",
"Created": "2021-05-07T15:47:20.341493099+05:30",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.21.0.0/16",
"Gateway": "172.21.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"2646cbbde5efc218bb6f3a5c882f8eb9e3e4331d090ad46ccc0a2eec9c2eea1b": {
"Name": "nginx",
"EndpointID": "c0291394a48f7e8e8aa98fd31631eb00e68daacbee9cf24bac530f16359d051d",
"MacAddress": "02:42:ac:15:00:02",
"IPv4Address": "172.21.0.2/16",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {}
}
Is that it? Absolutely not. I highly recommend you read about how to use go templates.
Here I've tried to get to started on using it without having to know much about go templates at all. I hope I succeeded.
For any clarifications, feel free to use the comments section.