6

I can't seem to find any good documentation on how to use the docker command's "--format" option in real life. I know how to use Go templates. But specific to docker, how do I know what placeholders are available to use?

I tried docker inspect on a container and I see JSON output, but I can't use the names from that as Go template placeholders. I did find out that the placeholders are case-sensitive, which seems unfortunate. The default output isn't much help either, since (I assume) it isn't showing me everything I might use, the columns that are there are all uppercase, and they sometimes have spaces in them. docker container ls, for example, has a column called "CONTAINER ID". No combination of underscores, quoting schemes, or so on will make that name work. I figured out that it's "{{.ID}}".

How am I supposed to be using "--format"? How do I discover placeholders for any given docker object?

11

I take the risk of being downvoted here, but I find Go templates a nightmare to use beyond the most basic use cases. Don't take me wrong: they are extremely powerful as you can guess it by reading the docs, but I find the syntax unfriendly, and for the most complex cases, I prefer parsing the JSON output with jq. Anyway:

Formatting expect commands

sho$ CID=$(sudo docker run --rm -d busybox sleep 3000)                                                                                         
sh$ sudo docker inspect $CID
[
    {
        "Id": "0470a3549d2f41efd0477cf8fc4eb3a27aca592f089c018ad24704031bbef967",
        "Created": "2019-02-05T16:41:40.040349047Z",
        "Path": "sleep",
        "Args": [
            "3000"
        ],
        "State": {
            "Status": "running",
            "Running": true,
            "Paused": false,
            "Restarting": false,
            "OOMKilled": false,
            "Dead": false,
            "Pid": 26467,
            "ExitCode": 0,
            "Error": "",
            "StartedAt": "2019-02-05T16:41:40.659056612Z",
            "FinishedAt": "0001-01-01T00:00:00Z"
        },
        "Image": "sha256:3a093384ac306cbac30b67f1585e12b30ab1a899374dabc3170b9bca246f1444",
        "ResolvConfPath": "/var/lib/docker/containers/0470a3549d2f41efd0477cf8fc4eb3a27aca592f089c018ad24704031bbef967/resolv.conf",
        "HostnamePath": "/var/lib/docker/containers/0470a3549d2f41efd0477cf8fc4eb3a27aca592f089c018ad24704031bbef967/hostname",
        "HostsPath": "/var/lib/docker/containers/0470a3549d2f41efd0477cf8fc4eb3a27aca592f089c018ad24704031bbef967/hosts",
        "LogPath": "/var/lib/docker/containers/0470a3549d2f41efd0477cf8fc4eb3a27aca592f089c018ad24704031bbef967/0470a3549d2f41efd0477cf8fc4eb3a27aca592f089c018ad24704031bbef967-json.log",
        "Name": "/vibrant_shaw",
        "RestartCount": 0,
        "Driver": "aufs",
        "Platform": "linux",
        "MountLabel": "",
        "ProcessLabel": "",
        "AppArmorProfile": "docker-default",
        "ExecIDs": null,
        "HostConfig": {
            "Binds": null,
            "ContainerIDFile": "",
           "LogConfig": {
                "Type": "json-file",
                "Config": {}
            },
            "NetworkMode": "default",
            "PortBindings": {},
            "RestartPolicy": {
                "Name": "no",
                "MaximumRetryCount": 0
            },
            "AutoRemove": true,
            "VolumeDriver": "",
            "VolumesFrom": null,
            "CapAdd": null,
            "CapDrop": null,
            "Dns": [],
            "DnsOptions": [],
            "DnsSearch": [],
            "ExtraHosts": null,
            "GroupAdd": null,
            "IpcMode": "shareable",
            "Cgroup": "",
            "Links": null,
            "OomScoreAdj": 0,
            "PidMode": "",
            "Privileged": false,
            "PublishAllPorts": false,
            "ReadonlyRootfs": false,
            "SecurityOpt": null,
            "UTSMode": "",
            "UsernsMode": "",
            "ShmSize": 67108864,
            "Runtime": "runc",
            "ConsoleSize": [
                0,
                0
            ],
            "Isolation": "",
            "CpuShares": 0,
            "Memory": 0,
            "NanoCpus": 0,
            "CgroupParent": "",
            "BlkioWeight": 0,
            "BlkioWeightDevice": [],
            "BlkioDeviceReadBps": null,
            "BlkioDeviceWriteBps": null,
            "BlkioDeviceReadIOps": null,
            "BlkioDeviceWriteIOps": null,
            "CpuPeriod": 0,
            "CpuQuota": 0,
            "CpuRealtimePeriod": 0,
            "CpuRealtimeRuntime": 0,
            "CpusetCpus": "",
            "CpusetMems": "",
            "Devices": [],
            "DeviceCgroupRules": null,
            "DiskQuota": 0,
            "KernelMemory": 0,
            "MemoryReservation": 0,
            "MemorySwap": 0,
            "MemorySwappiness": null,
            "OomKillDisable": false,
            "PidsLimit": 0,
            "Ulimits": null,
            "CpuCount": 0,
            "CpuPercent": 0,
            "IOMaximumIOps": 0,
            "IOMaximumBandwidth": 0,
            "MaskedPaths": [
                "/proc/acpi",
                "/proc/kcore",
                "/proc/keys",
                "/proc/latency_stats",
                "/proc/timer_list",
                "/proc/timer_stats",
                "/proc/sched_debug",
                "/proc/scsi",
                "/sys/firmware"
            ],
            "ReadonlyPaths": [
                "/proc/asound",
                "/proc/bus",
                "/proc/fs",
                "/proc/irq",
                "/proc/sys",
                "/proc/sysrq-trigger"
            ]
        },
        "GraphDriver": {
            "Data": null,
            "Name": "aufs"
        },
        "Mounts": [],
        "Config": {
            "Hostname": "0470a3549d2f",
            "Domainname": "",
            "User": "",
            "AttachStdin": false,
            "AttachStdout": false,
            "AttachStderr": false,
            "Tty": false,
            "OpenStdin": false,
            "StdinOnce": false,
            "Env": [
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
            ],
            "Cmd": [
                "sleep",
                "3000"
            ],
            "Image": "busybox",
            "Volumes": null,
            "WorkingDir": "",
            "Entrypoint": null,
            "OnBuild": null,
            "Labels": {}
        },
        "NetworkSettings": {
            "Bridge": "",
            "SandboxID": "654da638965d9bb26e394ba61036ea713bdccb6304eb09bee86f3c0611a26ca5",
            "HairpinMode": false,
            "LinkLocalIPv6Address": "",
            "LinkLocalIPv6PrefixLen": 0,
            "Ports": {},
            "SandboxKey": "/var/run/docker/netns/654da638965d",
            "SecondaryIPAddresses": null,
            "SecondaryIPv6Addresses": null,
            "EndpointID": "c69ef9cb870a3c477105c7bc19e370683886e4e3c62fac8444d3e1e097551250",
            "Gateway": "172.17.0.1",
            "GlobalIPv6Address": "",
            "GlobalIPv6PrefixLen": 0,
            "IPAddress": "172.17.0.3",
            "IPPrefixLen": 16,
            "IPv6Gateway": "",
            "MacAddress": "02:42:ac:11:00:03",
            "Networks": {
                "bridge": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": null,
                    "NetworkID": "5ec8728b05efd5ce6bfde5cbe6e42f247348345e0fdfb305fda76566327fc151",
                    "EndpointID": "c69ef9cb870a3c477105c7bc19e370683886e4e3c62fac8444d3e1e097551250",
                    "Gateway": "172.17.0.1",
                    "IPAddress": "172.17.0.3",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "02:42:ac:11:00:03",
                    "DriverOpts": null
                }
            }
        }
    }
]

Returning scalar properties

You can query any scalar attribute by its name, prefixed by a dot:

sh$ sudo docker inspect $CID --format '{{.Id}}
0470a3549d2f41efd0477cf8fc4eb3a27aca592f089c018ad24704031bbef967
sh$ sudo docker inspect $CID --format '{{.Name}} {{.Driver}}'
/vibrant_shaw aufs

# jq notation
sh$ sudo docker inspect $CID | jq '.[].Id'
"0470a3549d2f41efd0477cf8fc4eb3a27aca592f089c018ad24704031bbef967"

sh$ sudo docker inspect $CID | jq '.[]|(.Name,.Driver)'
"/vibrant_shaw"
"aufs"

You can also query nested fields:

sh$ sudo docker inspect $CID --format '{{.State.Running}}'
true

# jq notation
sudo docker inspect $CID | jq '.[].State.Running'
true

Returning entire objects and arrays

But it does not work as expected for nested structures:

sh$ sudo docker inspect $CID --format '{{.State}}'
{running true false false false false 26467 0  2019-02-05T16:41:40.659056612Z 0001-01-01T00:00:00Z <nil>}

You end up having the data, but without the field names, it is pretty useless--unless you are assuming some predefined field order (something that is probably not safe).

When accessing entire objects, there are chances you want an explicit formatter:

sh$ sudo docker inspect $CID --format '{{json .State}}'
{"Status":"running","Running":true,"Paused":false,"Restarting":false,"OOMKilled":false,"Dead":false,"Pid":26467,"ExitCode":0,"Error":"","StartedAt":"2019-02-05T16:41:40.659056612Z","FinishedAt":"0001-01-01T00:00:00Z"}

# jq notation
sh$ sudo docker inspect $CID | jq '.[].State'
{
  "Status": "running",
  "Running": true,
  "Paused": false,
  "Restarting": false,
  "OOMKilled": false,
  "Dead": false,
  "Pid": 26467,
  "ExitCode": 0,
  "Error": "",
  "StartedAt": "2019-02-05T16:41:40.659056612Z",
  "FinishedAt": "0001-01-01T00:00:00Z"
}

Same thing for arrays:

sh$ sudo docker inspect $CID --format '{{.HostConfig.ReadonlyPaths}}'
[/proc/asound /proc/bus /proc/fs /proc/irq /proc/sys /proc/sysrq-trigger]

sh$ sudo docker inspect $CID --format '{{json .HostConfig.ReadonlyPaths}}'
["/proc/asound","/proc/bus","/proc/fs","/proc/irq","/proc/sys","/proc/sysrq-trigger"]

sh$ sudo docker inspect $CID --format '{{join .HostConfig.ReadonlyPaths ":"}}'
/proc/asound:/proc/bus:/proc/fs:/proc/irq:/proc/sys:/proc/sysrq-trigger

# jq notation
sh$ sudo docker inspect $CID | jq '.[].HostConfig.ReadonlyPaths'
[
  "/proc/asound",
  "/proc/bus",
  "/proc/fs",
  "/proc/irq",
  "/proc/sys",
  "/proc/sysrq-trigger"
]
sh$ sudo docker inspect $CID | jq '.[].HostConfig.ReadonlyPaths | join(":")'
"/proc/asound:/proc/bus:/proc/fs:/proc/irq:/proc/sys:/proc/sysrq-trigger"

Arrays of objects

More complicated (according to my limited Go Template skills) is accessing fields nested in an array of objects:

# This works as expected
sh$ sudo docker network inspect bridge --format '{{json .IPAM.Config}}'
[{"Subnet":"172.17.0.0/16","Gateway":"172.17.0.1"}]

# But not that
sudo docker network inspect bridge --format '{{.IPAM.Config.Gateway}}'

Template parsing error: template: :1:7: executing "" at <.IPAM.Config.Gateway>: can't evaluate field Gateway in type interface {}

When I want to extract such nested value, I resort on an explicit range loop:

sh$ sudo docker network inspect bridge --format '{{range .IPAM.Config}}{{.Gateway}}{{end}}'
172.17.0.1

# jq notation
sh$ sudo docker network inspect bridge | jq '.[].IPAM.Config[].Gateway'
"172.17.0.1"

sh$ sudo docker network inspect bridge | jq -r '.[].IPAM.Config[].Gateway'
172.17.0.1

Formatting ls commands

With the json formatter, we can obtain (some? all?) the possible selectors you can later use with --format:

sh$ sudo docker network ls --format '{{json .}}'
{"CreatedAt":"2019-01-07 14:28:55.63238018 +0100 CET","Driver":"bridge","ID":"5ec8728b05ef","IPv6":"false","Internal":"false","Labels":"","Name":"bridge","Scope":"local"}
{"CreatedAt":"2019-02-05 15:36:57.701603791 +0100 CET","Driver":"bridge","ID":"45d6f09ca923","IPv6":"false","Internal":"false","Labels":"com.docker.compose.network=default,com.docker.compose.project=compose-demo,com.docker.compose.version=1.23.2","Name":"compose-demo_default","Scope":"local"}
{"CreatedAt":"2019-02-05 17:30:47.98883918 +0100 CET","Driver":"bridge","ID":"aaff418d1c56","IPv6":"false","Internal":"false","Labels":"com.docker.compose.network=net,com.docker.compose.project=compose-demo,com.docker.compose.version=1.23.2","Name":"compose-demo_net","Scope":"local"}
{"CreatedAt":"2018-05-26 10:31:48.149819089 +0200 CEST","Driver":"host","ID":"988954899b53","IPv6":"false","Internal":"false","Labels":"","Name":"host","Scope":"local"}
{"CreatedAt":"2018-05-26 10:31:48.118573602 +0200 CEST","Driver":"null","ID":"ce1680ed377f","IPv6":"false","Internal":"false","Labels":"","Name":"none","Scope":"local"}
{"CreatedAt":"2019-02-05 19:44:21.286955573 +0100 CET","Driver":"bridge","ID":"41c62c023d16","IPv6":"false","Internal":"false","Labels":"com.docker.compose.version=1.23.2,com.docker.compose.network=default,com.docker.compose.project=test","Name":"test_default","Scope":"local"}

Here, you can see docker network ls exposes, among others, the "ID", "Driver" and "Scope" fields. You can use them in your format:

sh$ $ sudo docker network ls --format '{{.ID}}: {{.Driver}} ({{.Scope}})'
5ec8728b05ef: bridge (local)
45d6f09ca923: bridge (local)
aaff418d1c56: bridge (local)
988954899b53: host (local)
ce1680ed377f: null (local)
41c62c023d16: bridge (local)

The best of both worlds

The docker ... ls commands only exports a small subset of the docker object's properties. Depending your needs, you may prefer something along the lines of docker ... inspect $(docker ... ls -q).

For example, docker image ls does not export the image architecture. but you can obtain it with docker image inspect. If you want that information, you could use something like that:

sh$ sudo docker image inspect \
         --format '{{.Id}}: {{.RepoTags}} ({{.Architecture}})' \
         $(sudo docker image ls -q)
sha256:fc37804350a8adea0b41dba0c413a6c967140ca2ed6dfeefdbe88d7868902406: [compose-demo_monitor:latest] (amd64)
sha256:a6f30c0890bca94e3cf5930ad5c3358b06cb38450e92d4ddabeb208ee34383f8: [compose-demo_app:latest] (amd64)
sha256:a79892b33e0bb51598e9311cc1d23ca8d0e3013a5a15fd67d202d22ffd08a6af: [] (amd64)
sha256:394c82635f220db389b8532e316001674965b51679dbc8dff747d5f224e6d568: [] (amd64)
sha256:95f0c755feb65af441c0cb8727bb13b35517977a3bed06e4a3456c95d6a69dd4: [redis:5.0-alpine] (amd64)
sha256:1f6c34f7921c7b50ef4452737399b5cff0eb834fc183d188d82886fd23b7fc34: [node:8] (amd64)
sha256:3a093384ac306cbac30b67f1585e12b30ab1a899374dabc3170b9bca246f1444: [busybox:latest] (amd64)
  • 1
    It seems like your examples all depend on the command being "inspect". This works: docker inspect $CID --format '{{.Id}}'. But this doesn't work: docker container ls --format '{{.Id}}'. – Todd Walton Feb 5 at 19:26
  • 1
    @ToddWalton You can always use --format '{{json .}} to inspect the fields exported by an ls command. I've edited my answer to add a section at the end showing how to do that. – Sylvain Leroux Feb 5 at 19:44
  • 1
    @ToddWalton BTW, I suspect, like their shell counterpart, the output of the docker ... ls commands are for "human consumption". Depending on your use case, you might consider something along the lines of docker image inspect --format ... $(docker image ls -q) – Sylvain Leroux Feb 5 at 19:59
  • Exactly what I wanted, thanks! :D – Anirudh M Aug 9 at 6:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.