diff --git a/Dockerfile b/Dockerfile index 9a4ca9cbdc..e3ccf89152 100644 --- a/Dockerfile +++ b/Dockerfile @@ -66,6 +66,7 @@ RUN apt-get update && apt-get install -y \ ubuntu-zfs \ xfsprogs \ libzfs-dev \ + tar \ --no-install-recommends \ && ln -snf /usr/bin/clang-3.8 /usr/local/bin/clang \ && ln -snf /usr/bin/clang++-3.8 /usr/local/bin/clang++ diff --git a/contrib/completion/bash/docker b/contrib/completion/bash/docker index f2648f70c0..ced3724e53 100644 --- a/contrib/completion/bash/docker +++ b/contrib/completion/bash/docker @@ -1394,6 +1394,7 @@ _docker_run() { --restart --security-opt --stop-signal + --tmpfs --ulimit --user -u --uts @@ -1443,7 +1444,7 @@ _docker_run() { _filedir return ;; - --device|--volume|-v) + --device|--tmpfs|--volume|-v) case "$cur" in *:*) # TODO somehow do _filedir for stuff inside the image, if it's already specified (which is also somewhat difficult to determine) diff --git a/contrib/completion/fish/docker.fish b/contrib/completion/fish/docker.fish index 04a1844388..33abfd0d37 100644 --- a/contrib/completion/fish/docker.fish +++ b/contrib/completion/fish/docker.fish @@ -339,6 +339,7 @@ complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l sig-proxy -d 'P complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l stop-signal -d 'Signal to kill a container' complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s t -l tty -d 'Allocate a pseudo-TTY' complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s u -l user -d 'Username or UID' +complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l tmpfs -d 'Mount tmpfs on a directory' complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s v -l volume -d 'Bind mount a volume (e.g., from the host: -v /host:/container, from Docker: -v /container)' complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l volumes-from -d 'Mount volumes from the specified container(s)' complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s w -l workdir -d 'Working directory inside the container' diff --git a/contrib/completion/zsh/_docker b/contrib/completion/zsh/_docker index 91eb563584..cde026b7eb 100644 --- a/contrib/completion/zsh/_docker +++ b/contrib/completion/zsh/_docker @@ -491,6 +491,7 @@ __docker_subcommand() { "($help)*--security-opt=[Security options]:security option: " "($help -t --tty)"{-t,--tty}"[Allocate a pseudo-tty]" "($help -u --user)"{-u=,--user=}"[Username or UID]:user:_users" + "($help)--tmpfs[mount tmpfs] " "($help)*-v[Bind mount a volume]:volume: " "($help)--volume-driver=[Optional volume driver for the container]:volume driver:(local)" "($help)*--volumes-from=[Mount volumes from the specified container]:volume: " diff --git a/daemon/container_unix.go b/daemon/container_unix.go index af45891800..e390820486 100644 --- a/daemon/container_unix.go +++ b/daemon/container_unix.go @@ -1534,3 +1534,15 @@ func (container *Container) unmountVolumes(forceSyscall bool) error { return nil } + +func (container *Container) tmpfsMounts() []execdriver.Mount { + var mounts []execdriver.Mount + for dest, data := range container.hostConfig.Tmpfs { + mounts = append(mounts, execdriver.Mount{ + Source: "tmpfs", + Destination: dest, + Data: data, + }) + } + return mounts +} diff --git a/daemon/container_windows.go b/daemon/container_windows.go index 9563f0d262..2586273fa8 100644 --- a/daemon/container_windows.go +++ b/daemon/container_windows.go @@ -191,6 +191,10 @@ func (container *Container) ipcMounts() []execdriver.Mount { return nil } +func (container *Container) tmpfsMounts() []execdriver.Mount { + return nil +} + func getDefaultRouteMtu() (int, error) { return -1, errSystemNotSupported } diff --git a/daemon/execdriver/driver.go b/daemon/execdriver/driver.go index 4a9d7d6e06..cb4ba62cec 100644 --- a/daemon/execdriver/driver.go +++ b/daemon/execdriver/driver.go @@ -137,4 +137,5 @@ type CommonCommand struct { Resources *Resources `json:"resources"` Rootfs string `json:"rootfs"` // root fs of the container WorkingDir string `json:"working_dir"` + TmpDir string `json:"tmpdir"` // Directory used to store docker tmpdirs. } diff --git a/daemon/execdriver/driver_unix.go b/daemon/execdriver/driver_unix.go index 528f06dc74..a302d1d7d4 100644 --- a/daemon/execdriver/driver_unix.go +++ b/daemon/execdriver/driver_unix.go @@ -28,6 +28,7 @@ type Mount struct { Writable bool `json:"writable"` Private bool `json:"private"` Slave bool `json:"slave"` + Data string `json:"data"` } // Resources contains all resource configs for a driver. diff --git a/daemon/execdriver/native/create.go b/daemon/execdriver/native/create.go index 03f89f371b..dc8522293b 100644 --- a/daemon/execdriver/native/create.go +++ b/daemon/execdriver/native/create.go @@ -4,10 +4,13 @@ package native import ( "fmt" + "path/filepath" "strings" "syscall" "github.com/docker/docker/daemon/execdriver" + derr "github.com/docker/docker/errors" + "github.com/docker/docker/pkg/mount" "github.com/opencontainers/runc/libcontainer/apparmor" "github.com/opencontainers/runc/libcontainer/configs" @@ -288,6 +291,36 @@ func (d *Driver) setupMounts(container *configs.Config, c *execdriver.Command) e container.Mounts = defaultMounts for _, m := range c.Mounts { + for _, cm := range container.Mounts { + if cm.Destination == m.Destination { + return derr.ErrorCodeMountDup.WithArgs(m.Destination) + } + } + + if m.Source == "tmpfs" { + var ( + data = "size=65536k" + flags = syscall.MS_NOEXEC | syscall.MS_NOSUID | syscall.MS_NODEV + err error + ) + fulldest := filepath.Join(c.Rootfs, m.Destination) + if m.Data != "" { + flags, data, err = mount.ParseTmpfsOptions(m.Data) + if err != nil { + return err + } + } + container.Mounts = append(container.Mounts, &configs.Mount{ + Source: m.Source, + Destination: m.Destination, + Data: data, + Device: "tmpfs", + Flags: flags, + PremountCmds: genTmpfsPremountCmd(c.TmpDir, fulldest, m.Destination), + PostmountCmds: genTmpfsPostmountCmd(c.TmpDir, fulldest, m.Destination), + }) + continue + } flags := syscall.MS_BIND | syscall.MS_REC if !m.Writable { flags |= syscall.MS_RDONLY diff --git a/daemon/execdriver/native/driver.go b/daemon/execdriver/native/driver.go index b88966ba31..f480e05e73 100644 --- a/daemon/execdriver/native/driver.go +++ b/daemon/execdriver/native/driver.go @@ -5,6 +5,7 @@ package native import ( "fmt" "io" + "io/ioutil" "os" "os/exec" "path/filepath" @@ -128,6 +129,13 @@ type execOutput struct { // it calls libcontainer APIs to run a container. func (d *Driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, hooks execdriver.Hooks) (execdriver.ExitStatus, error) { destroyed := false + var err error + c.TmpDir, err = ioutil.TempDir("", c.ID) + if err != nil { + return execdriver.ExitStatus{ExitCode: -1}, err + } + defer os.RemoveAll(c.TmpDir) + // take the Command and populate the libcontainer.Config from it container, err := d.createContainer(c, hooks) if err != nil { diff --git a/daemon/execdriver/native/tmpfs.go b/daemon/execdriver/native/tmpfs.go new file mode 100644 index 0000000000..89f7f4ae7f --- /dev/null +++ b/daemon/execdriver/native/tmpfs.go @@ -0,0 +1,56 @@ +package native + +import ( + "fmt" + "os" + "os/exec" + "strings" + + "github.com/Sirupsen/logrus" + "github.com/opencontainers/runc/libcontainer/configs" +) + +func genTmpfsPremountCmd(tmpDir string, fullDest string, dest string) []configs.Command { + var premount []configs.Command + tarPath, err := exec.LookPath("tar") + if err != nil { + logrus.Warn("tar command is not available for tmpfs mount: %s", err) + return premount + } + if _, err = exec.LookPath("rm"); err != nil { + logrus.Warn("rm command is not available for tmpfs mount: %s", err) + return premount + } + tarFile := fmt.Sprintf("%s/%s.tar", tmpDir, strings.Replace(dest, "/", "_", -1)) + if _, err := os.Stat(fullDest); err == nil { + premount = append(premount, configs.Command{ + Path: tarPath, + Args: []string{"-cf", tarFile, "-C", fullDest, "."}, + }) + } + return premount +} + +func genTmpfsPostmountCmd(tmpDir string, fullDest string, dest string) []configs.Command { + var postmount []configs.Command + tarPath, err := exec.LookPath("tar") + if err != nil { + return postmount + } + rmPath, err := exec.LookPath("rm") + if err != nil { + return postmount + } + if _, err := os.Stat(fullDest); os.IsNotExist(err) { + return postmount + } + tarFile := fmt.Sprintf("%s/%s.tar", tmpDir, strings.Replace(dest, "/", "_", -1)) + postmount = append(postmount, configs.Command{ + Path: tarPath, + Args: []string{"-xf", tarFile, "-C", fullDest, "."}, + }) + return append(postmount, configs.Command{ + Path: rmPath, + Args: []string{"-f", tarFile}, + }) +} diff --git a/daemon/start.go b/daemon/start.go index a8545de122..0a1f61ccc9 100644 --- a/daemon/start.go +++ b/daemon/start.go @@ -130,6 +130,7 @@ func (daemon *Daemon) containerStart(container *Container) (err error) { return err } mounts = append(mounts, container.ipcMounts()...) + mounts = append(mounts, container.tmpfsMounts()...) container.command.Mounts = mounts if err := daemon.waitForStart(container); err != nil { diff --git a/daemon/volumes.go b/daemon/volumes.go index 981c3254a1..028affac65 100644 --- a/daemon/volumes.go +++ b/daemon/volumes.go @@ -121,7 +121,7 @@ func (daemon *Daemon) registerMountPoints(container *Container, hostConfig *runc } if binds[bind.Destination] { - return derr.ErrorCodeVolumeDup.WithArgs(bind.Destination) + return derr.ErrorCodeMountDup.WithArgs(bind.Destination) } if len(bind.Name) > 0 && len(bind.Driver) > 0 { diff --git a/docs/reference/commandline/run.md b/docs/reference/commandline/run.md index 56f5bab25b..87cef35445 100644 --- a/docs/reference/commandline/run.md +++ b/docs/reference/commandline/run.md @@ -153,6 +153,14 @@ flag exists to allow special use-cases, like running Docker within Docker. The `-w` lets the command being executed inside directory given, here `/path/to/dir/`. If the path does not exists it is created inside the container. +### mount tmpfs (--tmpfs) + + $ docker run -d --tmpfs /run:rw,noexec,nosuid,size=65536k my_image + + The --tmpfs flag mounts a tmpfs into the container with the rw,noexec,nosuid,size=65536k options. + + Underlying content from the /run in the my_image image is copied into tmpfs. + ### Mount volume (-v, --read-only) $ docker run -v `pwd`:`pwd` -w `pwd` -i -t ubuntu pwd diff --git a/docs/reference/run.md b/docs/reference/run.md index e9b3a85aaa..b40888bf6d 100644 --- a/docs/reference/run.md +++ b/docs/reference/run.md @@ -1298,6 +1298,14 @@ above, or already defined by the developer with a Dockerfile `ENV`: Similarly the operator can set the **hostname** with `-h`. +### TMPFS (mount tmpfs filesystems) + + --tmpfs=[]: Create a tmpfs mount with: container-dir[:], where the options are identical to the Linux `mount -t tmpfs -o` command. + + Underlying content from the "container-dir" is copied into tmpfs. + + $ docker run -d --tmpfs /run:rw,noexec,nosuid,size=65536k my_image + ### VOLUME (shared filesystems) -v=[]: Create a bind mount with: [host-src:]container-dest[:], where diff --git a/errors/daemon.go b/errors/daemon.go index 72ddf278df..0ae089f214 100644 --- a/errors/daemon.go +++ b/errors/daemon.go @@ -444,12 +444,12 @@ var ( HTTPStatusCode: http.StatusInternalServerError, }) - // ErrorCodeVolumeDup is generated when we try to mount two volumes + // ErrorCodeMountDup is generated when we try to mount two mounts points // to the same path. - ErrorCodeVolumeDup = errcode.Register(errGroup, errcode.ErrorDescriptor{ - Value: "VOLUMEDUP", - Message: "Duplicate bind mount '%s'", - Description: "An attempt was made to mount a volume but the specified destination location is already used in a previous mount", + ErrorCodeMountDup = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "MOUNTDUP", + Message: "Duplicate mount point '%s'", + Description: "An attempt was made to mount a content but the specified destination location is already used in a previous mount", HTTPStatusCode: http.StatusInternalServerError, }) diff --git a/integration-cli/docker_api_containers_test.go b/integration-cli/docker_api_containers_test.go index 2e7c59936d..c623a748ad 100644 --- a/integration-cli/docker_api_containers_test.go +++ b/integration-cli/docker_api_containers_test.go @@ -212,7 +212,7 @@ func (s *DockerSuite) TestContainerApiStartDupVolumeBinds(c *check.C) { status, body, err := sockRequest("POST", "/containers/"+name+"/start", config) c.Assert(err, checker.IsNil) c.Assert(status, checker.Equals, http.StatusInternalServerError) - c.Assert(string(body), checker.Contains, "Duplicate bind", check.Commentf("Expected failure due to duplicate bind mounts to same path, instead got: %q with error: %v", string(body), err)) + c.Assert(string(body), checker.Contains, "Duplicate mount point", check.Commentf("Expected failure due to duplicate bind mounts to same path, instead got: %q with error: %v", string(body), err)) } func (s *DockerSuite) TestContainerApiStartVolumesFrom(c *check.C) { diff --git a/integration-cli/docker_cli_run_test.go b/integration-cli/docker_cli_run_test.go index 880c2f9eea..0e012fde58 100644 --- a/integration-cli/docker_cli_run_test.go +++ b/integration-cli/docker_cli_run_test.go @@ -375,10 +375,10 @@ func (s *DockerSuite) TestRunNoDupVolumes(c *check.C) { mountstr2 := path2 + someplace if out, _, err := dockerCmdWithError("run", "-v", mountstr1, "-v", mountstr2, "busybox", "true"); err == nil { - c.Fatal("Expected error about duplicate volume definitions") + c.Fatal("Expected error about duplicate mount definitions") } else { - if !strings.Contains(out, "Duplicate bind mount") { - c.Fatalf("Expected 'duplicate volume' error, got %v", out) + if !strings.Contains(out, "Duplicate mount point") { + c.Fatalf("Expected 'duplicate mount point' error, got %v", out) } } } diff --git a/integration-cli/docker_cli_run_unix_test.go b/integration-cli/docker_cli_run_unix_test.go index eea4d3344a..c0f09a3043 100644 --- a/integration-cli/docker_cli_run_unix_test.go +++ b/integration-cli/docker_cli_run_unix_test.go @@ -438,3 +438,21 @@ func (s *DockerSuite) TestRunWithShmSize(c *check.C) { c.Assert(err, check.IsNil) c.Assert(shmSize, check.Equals, "1073741824") } + +func (s *DockerSuite) TestRunTmpfsMounts(c *check.C) { + // TODO Windows (Post TP4): This test cannot run on a Windows daemon as + // Windows does not support tmpfs mounts. + testRequires(c, DaemonIsLinux) + if out, _, err := dockerCmdWithError("run", "--tmpfs", "/run", "busybox", "touch", "/run/somefile"); err != nil { + c.Fatalf("/run directory not mounted on tmpfs %q %s", err, out) + } + if out, _, err := dockerCmdWithError("run", "--tmpfs", "/run:noexec,nosuid,rw,size=5k,mode=700", "busybox", "touch", "/run/somefile"); err != nil { + c.Fatalf("/run failed to mount on tmpfs with valid options %q %s", err, out) + } + if _, _, err := dockerCmdWithError("run", "--tmpfs", "/run:foobar", "busybox", "touch", "/run/somefile"); err == nil { + c.Fatalf("/run mounted on tmpfs when it should have vailed within invalid mount option") + } + if _, _, err := dockerCmdWithError("run", "--tmpfs", "/run", "-v", "/run:/run", "busybox", "touch", "/run/somefile"); err == nil { + c.Fatalf("Should have generated an error saying Duplicate mount points") + } +} diff --git a/man/docker-create.1.md b/man/docker-create.1.md index bd143639e4..dc6891da62 100644 --- a/man/docker-create.1.md +++ b/man/docker-create.1.md @@ -57,6 +57,7 @@ docker-create - Create a new container [**--stop-signal**[=*SIGNAL*]] [**--shm-size**[=*[]*]] [**-t**|**--tty**[=*false*]] +[**--tmpfs**[=*[CONTAINER-DIR[:]*]] [**-u**|**--user**[=*USER*]] [**--ulimit**[=*[]*]] [**--uts**[=*[]*]] @@ -271,6 +272,20 @@ This value should always larger than **-m**, so you should always use this with **-t**, **--tty**=*true*|*false* Allocate a pseudo-TTY. The default is *false*. +**--tmpfs**=[] Create a tmpfs mount + + Mount a temporary filesystem (`tmpfs`) mount into a container, for example: + + $ docker run -d --tmpfs /tmp:rw,size=787448k,mode=1777 my_image + + This command mounts a `tmpfs` at `/tmp` within the container. The mount copies +the underlying content of `my_image` into `/tmp`. For example if there was a +directory `/tmp/content` in the base image, docker will copy this directory and +all of its content on top of the tmpfs mounted on `/tmp`. The supported mount +options are the same as the Linux default `mount` flags. If you do not specify +any options, the systems uses the following options: +`rw,noexec,nosuid,nodev,size=65536k`. + **-u**, **--user**="" Username or UID diff --git a/man/docker-run.1.md b/man/docker-run.1.md index 03eb1b66c0..7c56790f9f 100644 --- a/man/docker-run.1.md +++ b/man/docker-run.1.md @@ -60,6 +60,7 @@ docker-run - Run a command in a new container [**--shm-size**[=*[]*]] [**--sig-proxy**[=*true*]] [**-t**|**--tty**[=*false*]] +[**--tmpfs**[=*[CONTAINER-DIR[:]*]] [**-u**|**--user**[=*USER*]] [**-v**|**--volume**[=*[]*]] [**--ulimit**[=*[]*]] @@ -436,6 +437,20 @@ interactive shell. The default is false. The **-t** option is incompatible with a redirection of the docker client standard input. +**--tmpfs**=[] Create a tmpfs mount + + Mount a temporary filesystem (`tmpfs`) mount into a container, for example: + + $ docker run -d --tmpfs /tmp:rw,size=787448k,mode=1777 my_image + + This command mounts a `tmpfs` at `/tmp` within the container. The mount copies +the underlying content of `my_image` into `/tmp`. For example if there was a +directory `/tmp/content` in the base image, docker will copy this directory and +all of its content on top of the tmpfs mounted on `/tmp`. The supported mount +options are the same as the Linux default `mount` flags. If you do not specify +any options, the systems uses the following options: +`rw,noexec,nosuid,nodev,size=65536k`. + **-u**, **--user**="" Sets the username or UID used and optionally the groupname or GID for the specified command. @@ -552,6 +567,19 @@ the exit codes follow the `chroot` standard, see below: # EXAMPLES +## Running container in read-only mode + +During container image development, containers often need to write to the image +content. Installing packages into /usr, for example. In production, +applications seldom need to write to the image. Container applications write +to volumes if they need to write to file systems at all. Applications can be +made more secure by running them in read-only mode using the --read-only switch. +This protects the containers image from modification. Read only containers may +still need to write temporary data. The best way to handle this is to mount +tmpfs directories on /run and /tmp. + + # docker run --read-only --tmpfs /run --tmpfs /tmp -i -t fedora /bin/bash + ## Exposing log messages from the container to the host's log If you want messages that are logged in your container to show up in the host's diff --git a/pkg/mount/flags.go b/pkg/mount/flags.go index 17dbd7a64c..4305d15561 100644 --- a/pkg/mount/flags.go +++ b/pkg/mount/flags.go @@ -1,6 +1,7 @@ package mount import ( + "fmt" "strings" ) @@ -67,3 +68,24 @@ func parseOptions(options string) (int, string) { } return flag, strings.Join(data, ",") } + +// ParseTmpfsOptions parse fstab type mount options into flags and data +func ParseTmpfsOptions(options string) (int, string, error) { + flags, data := parseOptions(options) + validFlags := map[string]bool{ + "size": true, + "mode": true, + "uid": true, + "gid": true, + "nr_inodes": true, + "nr_blocks": true, + "mpol": true, + } + for _, o := range strings.Split(data, ",") { + opt := strings.SplitN(o, "=", 2) + if !validFlags[opt[0]] { + return 0, "", fmt.Errorf("Invalid tmpfs option %q", opt) + } + } + return flags, data, nil +} diff --git a/runconfig/hostconfig.go b/runconfig/hostconfig.go index 9936d095bc..80ffec8709 100644 --- a/runconfig/hostconfig.go +++ b/runconfig/hostconfig.go @@ -217,6 +217,7 @@ type HostConfig struct { PublishAllPorts bool // Should docker publish all exposed port for the container ReadonlyRootfs bool // Is the container root filesystem in read-only SecurityOpt []string // List of string values to customize labels for MLS systems, such as SELinux. + Tmpfs map[string]string `json:",omitempty"` // List of tmpfs (mounts) used for the container UTSMode UTSMode // UTS namespace to use for the container ShmSize *int64 // Total shm memory usage diff --git a/runconfig/parse.go b/runconfig/parse.go index c6c10a9aab..4a6e8b07fb 100644 --- a/runconfig/parse.go +++ b/runconfig/parse.go @@ -7,6 +7,7 @@ import ( "github.com/docker/docker/opts" flag "github.com/docker/docker/pkg/mflag" + "github.com/docker/docker/pkg/mount" "github.com/docker/docker/pkg/nat" "github.com/docker/docker/pkg/parsers" "github.com/docker/docker/pkg/signal" @@ -50,6 +51,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe // FIXME: use utils.ListOpts for attach and volumes? flAttach = opts.NewListOpts(opts.ValidateAttach) flVolumes = opts.NewListOpts(nil) + flTmpfs = opts.NewListOpts(nil) flBlkioWeightDevice = opts.NewWeightdeviceOpt(opts.ValidateWeightDevice) flLinks = opts.NewListOpts(opts.ValidateLink) flEnv = opts.NewListOpts(opts.ValidateEnv) @@ -111,6 +113,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe cmd.Var(&flAttach, []string{"a", "-attach"}, "Attach to STDIN, STDOUT or STDERR") cmd.Var(&flBlkioWeightDevice, []string{"-blkio-weight-device"}, "Block IO weight (relative device weight)") cmd.Var(&flVolumes, []string{"v", "-volume"}, "Bind mount a volume") + cmd.Var(&flTmpfs, []string{"-tmpfs"}, "Mount a tmpfs directory") cmd.Var(&flLinks, []string{"-link"}, "Add link to another container") cmd.Var(&flDevices, []string{"-device"}, "Add a host device to the container") cmd.Var(&flLabels, []string{"l", "-label"}, "Set meta data on a container") @@ -221,6 +224,19 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe } } + // Can't evalute options passed into --tmpfs until we actually mount + tmpfs := make(map[string]string) + for _, t := range flTmpfs.GetAll() { + if arr := strings.SplitN(t, ":", 2); len(arr) > 1 { + if _, _, err := mount.ParseTmpfsOptions(arr[1]); err != nil { + return nil, nil, cmd, err + } + tmpfs[arr[0]] = arr[1] + } else { + tmpfs[arr[0]] = "" + } + } + var ( parsedArgs = cmd.Args() runCmd *stringutils.StrSlice @@ -396,6 +412,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe Isolation: IsolationLevel(*flIsolation), ShmSize: parsedShm, Resources: resources, + Tmpfs: tmpfs, } // When allocating stdin in attached mode, close stdin at client disconnect