Skip to content

Instantly share code, notes, and snippets.

@efrecon
Last active May 8, 2024 22:55
Show Gist options
  • Save efrecon/8ce9c75d518b6eb863f667442d7bc679 to your computer and use it in GitHub Desktop.
Save efrecon/8ce9c75d518b6eb863f667442d7bc679 to your computer and use it in GitHub Desktop.
`docker inspect` template to regenerate the `docker run` command that created a container
docker run \
--name {{printf "%q" .Name}} \
{{- with .HostConfig}}
{{- if .Privileged}}
--privileged \
{{- end}}
{{- if .AutoRemove}}
--rm \
{{- end}}
{{- if .Runtime}}
--runtime {{printf "%q" .Runtime}} \
{{- end}}
{{- range $b := .Binds}}
--volume {{printf "%q" $b}} \
{{- end}}
{{- range $v := .VolumesFrom}}
--volumes-from {{printf "%q" $v}} \
{{- end}}
{{- range $l := .Links}}
--link {{printf "%q" $l}} \
{{- end}}
{{- if index . "Mounts"}}
{{- range $m := .Mounts}}
--mount type={{.Type}}
{{- if $s := index $m "Source"}},source={{$s}}{{- end}}
{{- if $t := index $m "Target"}},destination={{$t}}{{- end}}
{{- if index $m "ReadOnly"}},readonly{{- end}}
{{- if $vo := index $m "VolumeOptions"}}
{{- range $i, $v := $vo.Labels}}
{{- printf ",volume-label=%s=%s" $i $v}}
{{- end}}
{{- if $dc := index $vo "DriverConfig" }}
{{- if $n := index $dc "Name" }}
{{- printf ",volume-driver=%s" $n}}
{{- end}}
{{- range $i, $v := $dc.Options}}
{{- printf ",volume-opt=%s=%s" $i $v}}
{{- end}}
{{- end}}
{{- end}}
{{- if $bo := index $m "BindOptions"}}
{{- if $p := index $bo "Propagation" }}
{{- printf ",bind-propagation=%s" $p}}
{{- end}}
{{- end}} \
{{- end}}
{{- end}}
{{- if .PublishAllPorts}}
--publish-all \
{{- end}}
{{- if .UTSMode}}
--uts {{printf "%q" .UTSMode}} \
{{- end}}
{{- with .LogConfig}}
--log-driver {{printf "%q" .Type}} \
{{- range $o, $v := .Config}}
--log-opt {{$o}}={{printf "%q" $v}} \
{{- end}}
{{- end}}
{{- with .RestartPolicy}}
--restart "{{.Name -}}
{{- if eq .Name "on-failure"}}:{{.MaximumRetryCount}}
{{- end}}" \
{{- end}}
{{- range $e := .ExtraHosts}}
--add-host {{printf "%q" $e}} \
{{- end}}
{{- range $v := .CapAdd}}
--cap-add {{printf "%q" $v}} \
{{- end}}
{{- range $v := .CapDrop}}
--cap-drop {{printf "%q" $v}} \
{{- end}}
{{- range $d := .Devices}}
--device {{printf "%q" (index $d).PathOnHost}}:{{printf "%q" (index $d).PathInContainer}}:{{(index $d).CgroupPermissions}} \
{{- end}}
{{- end}}
{{- with .NetworkSettings -}}
{{- range $p, $conf := .Ports}}
{{- with $conf}}
--publish "
{{- if $h := (index $conf 0).HostIp}}{{$h}}:
{{- end}}
{{- (index $conf 0).HostPort}}:{{$p}}" \
{{- end}}
{{- end}}
{{- range $n, $conf := .Networks}}
{{- with $conf}}
--network {{printf "%q" $n}} \
{{- range $a := $conf.Aliases}}
--network-alias {{printf "%q" $a}} \
{{- end}}
{{- end}}
{{- end}}
{{- end}}
{{- with .Config}}
{{- if .Hostname}}
--hostname {{printf "%q" .Hostname}} \
{{- end}}
{{- if .Domainname}}
--domainname {{printf "%q" .Domainname}} \
{{- end}}
{{- if index . "ExposedPorts"}}
{{- range $p, $conf := .ExposedPorts}}
--expose {{printf "%q" $p}} \
{{- end}}
{{- end}}
{{- if .User}}
--user {{printf "%q" .User}} \
{{- end}}
{{- range $e := .Env}}
--env {{printf "%q" $e}} \
{{- end}}
{{- range $l, $v := .Labels}}
--label {{printf "%q" $l}}={{printf "%q" $v}} \
{{- end}}
{{- if not (or .AttachStdin (or .AttachStdout .AttachStderr))}}
--detach \
{{- end}}
{{- if .AttachStdin}}
--attach stdin \
{{- end}}
{{- if .AttachStdout}}
--attach stdout \
{{- end}}
{{- if .AttachStderr}}
--attach stderr \
{{- end}}
{{- if .Tty}}
--tty \
{{- end}}
{{- if .OpenStdin}}
--interactive \
{{- end}}
{{- if .Entrypoint}}
{{- /* Since the entry point cannot be overridden from the command line with an array of size over 1,
we are fine assuming the default value in such a case. */ -}}
{{- if eq (len .Entrypoint) 1 }}
--entrypoint "
{{- range $i, $v := .Entrypoint}}
{{- if $i}} {{end}}
{{- $v}}
{{- end}}" \
{{- end}}
{{- end}}
{{printf "%q" .Image}} \
{{range .Cmd}}{{printf "%q " .}}{{- end}}
{{- end}}
@ictus4u
Copy link

ictus4u commented May 25, 2020

@efrecon I refactored again the code. It now looks longer, but it's for sure more readable and easier to peer review and maintain. I hope this will encourage more people to get collaborative, and also, to understand and be safer about what is being done by this template. I took advantage of the left-trimming {{- feature for being able to format the template without affecting the output. No added features, but some typos came to light and were fixed in the way. Cheers!

@efrecon
Copy link
Author

efrecon commented May 31, 2020

@ictus4u: This is just... beautiful. It makes it so much more approachable. Very nice refactoring!

@kforner
Copy link

kforner commented Jul 22, 2020

very very cool indeed! Chapeau!

@jeanspector
Copy link

This is beautiful indeed! Thank you!

@dgoguerra
Copy link

dgoguerra commented Jan 5, 2021

Thank you! This is really useful! To use directly the most recent version of the template:

docker inspect \
  --format "$(curl -s https://gist.githubusercontent.com/efrecon/8ce9c75d518b6eb863f667442d7bc679/raw/run.tpl)" \
  name_or_id_of_running_container

@ikucheryavenkov
Copy link

I do sooo love this!!!

@barl0g
Copy link

barl0g commented Nov 6, 2021

If exists volume declaration I receive an error. Check this (mailu_mailstate):

# docker inspect --format "$(curl -s https://gist.githubusercontent.com/efrecon/8ce9c75d518b6eb863f667442d7bc679/raw/run.tpl)" 55a319b7e124
docker run \
  --name "/mail" \
  --runtime "runc" \
  --volume "mailu_mailstate:/var/mail-state:rw" \ # ERROR!
  --volume "/root/mailu/config:/tmp/docker-mailserver:rw" \
  --volume "mailu_maildata:/var/mail:rw" \ # ERROR!
# docker inspect 55a319b7e124 | less
            {
                "Type": "volume",
                "Name": "mailu_mailstate",
                "Source": "/var/lib/docker/volumes/mailu_mailstate/_data",
                "Destination": "/var/mail-state",
                "Driver": "local",
                "Mode": "rw",
                "RW": true,
                "Propagation": ""
            }

@bulletcomes
Copy link

Thank you soooo much for this. I am reverse enginering a docker registry a consultant setup (and he is long gone) and this helps a lot!

Good work!

@ToroNZ
Copy link

ToroNZ commented Jan 31, 2022

hell yeah

@sunds
Copy link

sunds commented Aug 1, 2022

Still very useful in mid 2022. Thanks!

@pcsmith811
Copy link

Thank you! This is really useful! To use directly the most recent version of the template:

docker inspect \
  --format "$(curl -s https://gist.githubusercontent.com/efrecon/8ce9c75d518b6eb863f667442d7bc679/raw/run.tpl)" \
  name_or_id_of_running_container

Thanks, works great and made it very simple!

@griloHBG
Copy link

griloHBG commented Feb 1, 2023

Hi @efrecon!

Thanks for this great tool!

I would like to contribute with a starting point for the --mount option of docker run.

The addition is available in my fork: https://gist.github.com/griloHBG/5c46567b813f6ec2e4bc04454837faae

Motivation: I am studying how the x11docker works and the mounts weren't showing up.

Full disclaimer: this is the first time I put my hands in this Go Text Template stuff, so I expect my addition to not be as good as it could 😛

@efrecon
Copy link
Author

efrecon commented Feb 3, 2023

I've merged your version @griloHBG . It looks fine to me and has worked for me when tested against a few containers that I had that were using the --mount option. Thanks!

@matheusyl
Copy link

matheusyl commented Feb 4, 2023

I got an error after the update:

Template parsing error: template: :19:18: executing "" at <.Mounts>: map has no entry for key "Mounts"

Could not investigate further right now since I'm in the middle of a production change.

@efrecon
Copy link
Author

efrecon commented Feb 4, 2023

hm... @matheusyl would be nice if you can reproduce this, because I haven't been able to reproduce it, event on containers that did not have any Mounts. Could it be a version thing, and could it be that you are running with an older version of docker? When I inspect without the template, the JSON returned always contains a Mounts array, albeit empty sometimes.

@oparin-alexander
Copy link

I had the same issue
Template parsing error: template: :19:18: executing "" at <.Mounts>: map has no entry for key "Mounts"
Docker version is Docker version 1.13.1, build 7d71120/1.13.1

In my version .Mounts is located in the root of docker inspect. Something like that:

[
    {
        "HostConfig": {
        ...
        },
        "Mounts": [
            {
                "Type": "bind",
                "Source": "example",
                "Destination": "example",
                "Mode": "rw",
                "RW": true,
                "Propagation": "rprivate"
            },
            ...
        ],
        ...
    }
]

So moving

          {{- range .Mounts}}
  --mount type={{.Type}},source={{.Source}},destination={{.Destination}}{{- if .Propagation}},bind-propagation={{.Propagation}}{{- end}}{{- if not .RW}},readonly{{- end}} \
        {{- end}}

out of .HostConfig scope has helped me

@ictus4u
Copy link

ictus4u commented Feb 6, 2023

Hi @efrecon , I'd be pleased if you'd review my just-added support for --mount: https://gist.github.com/ictus4u/e28b47dc826644412629093d5c9185be .

@oparin-alexander I've seen two .Mounts, one inside the HostConfig and the other outside, as you noticed. After some play around, I've found the outer is the effective configuration. But the inner is reflecting the specified --mount options, thus, likely this is the one we want to reverse engineer to get the generating command.

@matheusyl probably in your container the --mount command was not used, so the .Mounts config was not found. In my current proposal, I consider that case and check if it exists before the loop.

@griloHBG Thank you for pushing it forward.

@efrecon
Copy link
Author

efrecon commented Feb 10, 2023

@ictus4u You are right, there are two .Mounts. The one inside HostConfig seems to reflect what you provided at the command-line (or docker compose, or ...) and is the one that we want to work with I think. For example, if you use secrets, they will also appear there (marked as read only). I have tried to fix with an extra "if" to see if that helps. @matheusyl can you check if the new version fixes your problems?

@MarcelloPercoco
Copy link

@ictus4u You are right, there are two .Mounts. The one inside HostConfig seems to reflect what you provided at the command-line (or docker compose, or ...) and is the one that we want to work with I think. For example, if you use secrets, they will also appear there (marked as read only). I have tried to fix with an extra "if" to see if that helps. @matheusyl can you check if the new version fixes your problems?

Hi.

I'm facing the exact same problem, and the last update dosen't seem to have fixed it; i see those errors and some run file are still empty

Thanks and best regards

@donny-son
Copy link

For those who are restricted to public internet access can use cat instead of curl.

docker inspect --format "$(cat /path_to_file/run.tpl)" name_or_id_of_running_container

@uvlad7
Copy link

uvlad7 commented May 10, 2023

Please add

--user {{.Config.User}}

@QGB
Copy link

QGB commented May 16, 2023

docker inspect --format "$(curl -s https://gist.githubusercontent.com/efrecon/8ce9c75d518b6eb863f667442d7bc679/raw/run.tpl)" ded0bdd038e4

Template parsing error: template: :22:15: executing "" at <.Mounts>: map has no entry for key "Mounts"

@efrecon
Copy link
Author

efrecon commented May 16, 2023

@uvlad7 --user now supported. @QGB try again, I have merged and adapted the changes from @ictus4u

@htjain
Copy link

htjain commented Jun 5, 2023

docker inspect --format "$(curl -s https://gist.githubusercontent.com/efrecon/8ce9c75d518b6eb863f667442d7bc679/raw/run.tpl)" ded0bdd038e4

Template parsing error: template: :22:15: executing "" at <.Mounts>: map has no entry for key "Mounts"

Same error

@saschabrockel
Copy link

Thank you for the great work! Unfortunately, I recognized that everything regarding health checks is missing:

  --health-cmd="redis-cli
  --raw incr ping"
  --health-interval=30s
  --health-timeout=10s
  --health-retries=3

Would it be possible to add them too?

@xuganyu96
Copy link

This is fantastically simple and effective. Thank you so much!

@snowdream
Copy link

thank you so much!

@SimonWoidig
Copy link

Starred! Simply amazing!

@yousri666
Copy link

great work! so helpful

@insinfo
Copy link

insinfo commented May 6, 2024

great work! so helpful

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment