<p>A solution is to remove the <code>not empty directory</code> using <code>find</code>:</p>

<p><strong>Dockerfile</strong></p>

<pre><code>FROM alpine

RUN mkdir dir &amp;&amp; cd dir &amp;&amp; wget http://google.com &amp;&amp; cd / &amp;&amp; echo -e "BEFORE\n" &amp;&amp; ls &amp;&amp; find /dir -delete &amp;&amp; echo -e "\nAFTER\n" &amp;&amp; ls
</code></pre>

<p><strong>Outcome</strong></p>

<pre><code>Sending build context to Docker daemon  2.048kB
Step 1/2 : FROM alpine
 ---&gt; 4a415e366388
Step 2/2 : RUN mkdir dir &amp;&amp; cd dir &amp;&amp; wget http://google.com &amp;&amp; cd / &amp;&amp; echo -e "BEFORE\n" &amp;&amp; ls &amp;&amp; find /dir -delete &amp;&amp; echo -e "\nAFTER\n" &amp;&amp; ls
 ---&gt; Running in ba93a1742a76
Connecting to google.com (172.217.20.110:80)
Connecting to www.google.nl (172.217.20.99:80)
index.html           100% |*******************************| 11092   0:00:00 ETA

BEFORE

bin
dev
dir
etc
home
lib
media
mnt
proc
root
run
sbin
srv
sys
tmp
usr
var

AFTER

bin
dev
etc
home
lib
media
mnt
proc
root
run
sbin
srv
sys
tmp
usr
var
 ---&gt; 2e3109285a2d
Removing intermediate container ba93a1742a76
Successfully built 2e3109285a2d
</code></pre>

CC BY-SA 3.0
2017-04-03T14:11:59.220Z
You have unnecessary stuff in there, I believe you missed the point in Xiong's answer - the problem is not the `-rf` options to the `rm` cmd.
Dan Cornilescu
CC BY-SA 3.0
2017-04-04T10:18:06.600Z
If I specify the whole path /path/to/dir and run it from / it still fails even with rf. I had to remove all the content from the folder.
030
CC BY-SA 3.0
2017-04-04T12:21:53.460Z
Are you saying `rm -rf` doesn't work and one has to manually empty every dir before deleting the dir? Very odd.
Dan Cornilescu
CC BY-SA 3.0
2017-04-06T08:07:50.173Z
The `rm -rf` does not work indeed. I had to use `find /path -delete`
030
CC BY-SA 3.0
2017-04-02T12:51:06.963Z
2017-04-14T07:52:48.080Z
2017-04-14T07:52:48.080Z
030
<p>Recent Dockerfile spec allows for multiple FROM statements.</p>

<p><a href="https://docs.docker.com/engine/userguide/eng-image/multistage-build/" rel="noreferrer">https://docs.docker.com/engine/userguide/eng-image/multistage-build/</a></p>

<p>Using this approach, you can retain your compilation results to a pure runtime environment.</p>

CC BY-SA 4.0
2020-06-03T12:19:41.187Z
My situation is that - due to security reasons I am downloading the packages in the intermediate layer. Then, ADD'ing them to the final image. Followed by using RUN command to install the packages and delete the packages. My goal is to delete the downloaded packages once the RUN command installs them. Is there any way to a) combine the ADD and RUN into one layer; b) remove the ADD layer?
variable
CC BY-SA 3.0
2017-12-30T11:02:13.467Z
2017-12-30T11:02:13.467Z
Ta Mu
<p>Building on Xiong Chiamiov's answer, which correctly identified the root cause of the problem - the <code>dir</code> reference by relative path when attempting to empty or delete that directory depends on the working directory at the time, which was not correctly set in the cases mentioned in the OP.</p>
<p>So there are 2 solutions available:</p>
<ul>
<li><p>set the proper working dir prior to executing the <code>dir</code> removal:</p>
<pre><code>  RUN mkdir dir &amp;&amp; cd dir &amp;&amp; wget http://google.com &amp;&amp; cd .. &amp;&amp; rm -rf dir
</code></pre>
</li>
<li><p>use a <code>/dir</code> full path reference to that directory instead. But with care (adjust if needed) if used in cases just similar, but not identical to this one, since this approach assumes the initial working dir was <code>/</code>:</p>
<pre><code>  RUN mkdir dir &amp;&amp; cd dir &amp;&amp; wget http://google.com &amp;&amp; rm -rf /dir
</code></pre>
</li>
</ul>

CC BY-SA 3.0
2017-04-03T17:28:46.300Z
Hmm. Why not `rm -rf ../dir`? That avoids the absolute path reference
jpaugh
CC BY-SA 3.0
2017-04-03T18:29:47.330Z
@jpaugh it *might* work, but it feels weird (cutting the branch you're sitting on?)
Dan Cornilescu
CC BY-SA 3.0
2017-04-03T18:39:10.793Z
That's true; but it works essentially the same way when you specify an absolute path. FWIW, I've done this before, in an interactive shell. `ls` tends to lose its meaning... ;-) You can even `rm -rf ../dir && mkdir ../dir && cd ../dir`, which puts you in an new, empty (valid) folder
jpaugh
CC BY-SA 3.0
2017-04-03T20:06:38.470Z
@jpaugh You're right, that's why I said `it feels` - it's just a feeling :)
Dan Cornilescu
CC BY-SA 4.0
2017-04-03T14:09:19.167Z
2022-12-06T20:40:49.817Z
2022-12-06T20:40:49.817Z
Dan Cornilescu
<p><code>mkdir dir &amp;&amp; cd dir &amp;&amp; wget http://google.com &amp;&amp; rm -rf dir</code> doesn't do what you think it does.</p>

<p>Let's break it down:</p>

<ol>
<li><code>mkdir dir</code> creates <code>dir</code>.</li>
<li><code>cd dir</code> changes directory into it.</li>
<li><code>wget http://google.com</code> downloads google.com inside <code>dir/</code>.</li>
<li><code>rm -rf dir</code> attempts to delete a directory called <code>dir</code> that's in the current directory.</li>
</ol>

<p>The problem is that there is not <code>dir</code> in the current directory, because you're already <code>cd</code>ed into it.  And since you've added the <code>-f</code> flag, the command doesn't produce an error.  This is one of the reasons why you shouldn't use force flags without being very sure you need them.</p>

<p>Since you didn't use the <code>WORKDIR</code> command, the directory change <a href="https://stackoverflow.com/q/17891981/120999">is discarded at the end of the <code>RUN</code></a>.  Thus, the next line executes in the original working directory, where it tries to <code>mkdir dir</code> but, as we've previously discussed, that directory still exists with the wgetted contents inside it.</p>

CC BY-SA 3.0
2017-04-02T16:34:04.420Z
Sure it's possible to remove directories; you just specified the wrong path.
Xiong Chiamiov
CC BY-SA 3.0
2017-04-02T18:59:27.123Z
It's a bit of a strange error, considering that that's precisely what `rm -r` is intended to address.  You can probably add `-f` in this case, but I would do some further investigation to see what exactly is in that directory (e.g. an `ls -A`).  But you've gone past the original question at this point.
Xiong Chiamiov
CC BY-SA 3.0
2017-04-02T04:01:12.017Z
2017-04-02T04:01:12.017Z
2017-05-23T12:39:34.013Z
Xiong Chiamiov
<p><strong>Why this question?</strong></p>

<p>The reason for posting this Q&amp;A is that sometimes certain software is required to compile software in a docker image. Once compiled, these packages are superfluous and should be removed in order to reduce the image size. In some cases images that were more than 1.5GB where reduced to less than 300MB. The following use case does not represent a real world scenario, but provides an example of this use case.</p>

<p><strong>Attempts to solve the issue</strong></p>

<p>It works to remove a directory when the remove command is defined in the same layer as the creation of the folder:</p>

<pre><code>FROM alpine
RUN mkdir dir &amp;&amp; cd dir &amp;&amp; wget http://google.com &amp;&amp; rm -rf dir
</code></pre>

<p>This results in:</p>

<pre><code>user@host$ docker build -t dir .
Sending build context to Docker daemon 1.336 MB
Step 1/2 : FROM alpine
 ---&gt; 4a415e366388
Step 2/2 : RUN mkdir dir &amp;&amp; cd dir &amp;&amp; wget http://google.com &amp;&amp; rm -rf dir
 ---&gt; Using cache
 ---&gt; c283805e687f
Successfully built c283805e687f
</code></pre>

<p>When the aim is to remove the directory in another layer, this fails:</p>

<pre><code>FROM alpine
RUN mkdir dir &amp;&amp; cd dir &amp;&amp; wget http://google.com
RUN rm -r dir
</code></pre>

<p>And this fails:</p>

<pre><code>FROM alpine
RUN mkdir dir &amp;&amp; cd dir &amp;&amp; wget http://google.com
RUN rm -rf dir
</code></pre>

<p>Another attempt was using workdir:</p>

<pre><code>FROM alpine

RUN mkdir dir
WORKDIR dir
RUN wget http://google.com
WORKDIR /
RUN ls
RUN rm -r dir
</code></pre>

<p>Building the image resulted in:</p>

<pre><code>Sending build context to Docker daemon 2.048 kB
Step 1/7 : FROM alpine
 ---&gt; 4a415e366388
Step 2/7 : RUN mkdir dir
 ---&gt; Using cache
 ---&gt; aed5c75218cb
Step 3/7 : WORKDIR dir
 ---&gt; Using cache
 ---&gt; 715de09e7e08
Step 4/7 : RUN wget http://google.com
 ---&gt; Using cache
 ---&gt; c07bfb3e1133
Step 5/7 : WORKDIR /
 ---&gt; Using cache
 ---&gt; c6de86d0191a
Step 6/7 : RUN ls
 ---&gt; Using cache
 ---&gt; 9567593cce39
Step 7/7 : RUN rm -r dir
 ---&gt; Running in 7fe7b28294bf
rm: can't remove 'dir': Directory not empty
The command '/bin/sh -c rm -r dir' returned a non-zero code: 1
</code></pre>

CC BY-SA 3.0
2017-04-02T03:20:10.567Z
You most certainly can have multiple RUN statements in your Dockerfile.
David Ficociello
CC BY-SA 3.0
2017-04-02T03:21:04.487Z
Check your Dockerfile. Looks like you copy pasted the same line twice. If you look at step 2 and 3 they are both executing the same code. That's why you're getting the no dir found. You've already deleted it in step 2
David Ficociello
<dockerfile>
How to remove directories and files in another layer using Docker?
030
# How to remove directories and files in another layer using Docker?

(Asked by: username_0 on 2017-03-31)

Why this question?
The reason for posting this Q&A is that sometimes certain software is required to compile software in a docker image. Once compiled, these packages are superfluous and should be removed in order to reduce the image size. In some cases images that were more than 1.5GB where reduced to less than 300MB. The following use case does not represent a real world scenario, but provides an example of this use case.
Attempts to solve the issue
It works to remove a directory when the remove command is defined in the same layer as the creation of the folder:
FROM alpine
RUN mkdir dir && cd dir && wget http://google.com && rm -rf dir

This results in:
user@host$ docker build -t dir .
Sending build context to Docker daemon 1.336 MB
Step 1/2 : FROM alpine
 ---> 4a415e366388
Step 2/2 : RUN mkdir dir && cd dir && wget http://google.com && rm -rf dir
 ---> Using cache
 ---> c283805e687f
Successfully built c283805e687f

When the aim is to remove the directory in another layer, this fails:
FROM alpine
RUN mkdir dir && cd dir && wget http://google.com
RUN rm -r dir

And this fails:
FROM alpine
RUN mkdir dir && cd dir && wget http://google.com
RUN rm -rf dir

Another attempt was using workdir:
FROM alpine

RUN mkdir dir
WORKDIR dir
RUN wget http://google.com
WORKDIR /
RUN ls
RUN rm -r dir

Building the image resulted in:
Sending build context to Docker daemon 2.048 kB
Step 1/7 : FROM alpine
 ---> 4a415e366388
Step 2/7 : RUN mkdir dir
 ---> Using cache
 ---> aed5c75218cb
Step 3/7 : WORKDIR dir
 ---> Using cache
 ---> 715de09e7e08
Step 4/7 : RUN wget http://google.com
 ---> Using cache
 ---> c07bfb3e1133
Step 5/7 : WORKDIR /
 ---> Using cache
 ---> c6de86d0191a
Step 6/7 : RUN ls
 ---> Using cache
 ---> 9567593cce39
Step 7/7 : RUN rm -r dir
 ---> Running in 7fe7b28294bf
rm: can't remove 'dir': Directory not empty
The command '/bin/sh -c rm -r dir' returned a non-zero code: 1

**Tags:** dockerfile

> **Comments:**
> **username_1**: You most certainly can have multiple RUN statements in your Dockerfile.  
> **username_1**: Check your Dockerfile. Looks like you copy pasted the same line twice. If you look at step 2 and 3 they are both executing the same code. That's why you're getting the no dir found. You've already deleted it in step 2  

---

## 4 answers

### Answer by username_0 on 2017-04-02. Score: 1 | Accepted answer

A solution is to remove the not empty directory using find:
Dockerfile
FROM alpine

RUN mkdir dir && cd dir && wget http://google.com && cd / && echo -e "BEFORE\n" && ls && find /dir -delete && echo -e "\nAFTER\n" && ls

Outcome
Sending build context to Docker daemon  2.048kB
Step 1/2 : FROM alpine
 ---> 4a415e366388
Step 2/2 : RUN mkdir dir && cd dir && wget http://google.com && cd / && echo -e "BEFORE\n" && ls && find /dir -delete && echo -e "\nAFTER\n" && ls
 ---> Running in ba93a1742a76
Connecting to google.com (172.217.20.110:80)
Connecting to www.google.nl (172.217.20.99:80)
index.html           100% |*******************************| 11092   0:00:00 ETA

BEFORE

bin
dev
dir
etc
home
lib
media
mnt
proc
root
run
sbin
srv
sys
tmp
usr
var

AFTER

bin
dev
etc
home
lib
media
mnt
proc
root
run
sbin
srv
sys
tmp
usr
var
 ---> 2e3109285a2d
Removing intermediate container ba93a1742a76
Successfully built 2e3109285a2d

> **Comments:**
> **username_2**: You have unnecessary stuff in there, I believe you missed the point in Xiong's answer - the problem is not the `-rf` options to the `rm` cmd.  
> **username_0**: If I specify the whole path /path/to/dir and run it from / it still fails even with rf. I had to remove all the content from the folder.  
> **username_2**: Are you saying `rm -rf` doesn't work and one has to manually empty every dir before deleting the dir? Very odd.  
> **username_0**: The `rm -rf` does not work indeed. I had to use `find /path -delete`  

---

### Answer by username_6 on 2017-04-02. Score: 19

`mkdir dir && cd dir && wget http://google.com && rm -rf dir` doesn't do what you think it does.

Let's break it down:

1. `mkdir dir` creates `dir`.
2. `cd dir` changes directory into it.
3. `wget http://google.com` downloads google.com inside `dir/`.
4. `rm -rf dir` attempts to delete a directory called `dir` that's in the current directory.

The problem is that there is not `dir` in the current directory, because you're already `cd`ed into it. And since you've added the `-f` flag, the command doesn't produce an error. This is one of the reasons why you shouldn't use force flags without being very sure you need them.

Since you didn't use the `WORKDIR` command, the directory change [is discarded at the end of the `RUN`](https://stackoverflow.com/q/17891981/120999). Thus, the next line executes in the original working directory, where it tries to `mkdir dir` but, as we've previously discussed, that directory still exists with the wgetted contents inside it.

> **Comments:**
> **username_6**: Sure it's possible to remove directories; you just specified the wrong path.  
> **username_6**: It's a bit of a strange error, considering that that's precisely what `rm -r` is intended to address.  You can probably add `-f` in this case, but I would do some further investigation to see what exactly is in that directory (e.g. an `ls -A`).  But you've gone past the original question at this point.  

---

### Answer by username_3 on 2017-12-30. Score: 7

Recent Dockerfile spec allows for multiple FROM statements.

<https://docs.docker.com/engine/userguide/eng-image/multistage-build/>

Using this approach, you can retain your compilation results to a pure runtime environment.

> **Comments:**
> **username_4**: My situation is that - due to security reasons I am downloading the packages in the intermediate layer. Then, ADD'ing them to the final image. Followed by using RUN command to install the packages and delete the packages. My goal is to delete the downloaded packages once the RUN command installs them. Is there any way to a) combine the ADD and RUN into one layer; b) remove the ADD layer?  

---

### Answer by username_2 on 2017-04-03. Score: 6

Building on username_6's answer, which correctly identified the root cause of the problem - the dir reference by relative path when attempting to empty or delete that directory depends on the working directory at the time, which was not correctly set in the cases mentioned in the OP.
So there are 2 solutions available:

set the proper working dir prior to executing the dir removal:
  RUN mkdir dir && cd dir && wget http://google.com && cd .. && rm -rf dir


use a /dir full path reference to that directory instead. But with care (adjust if needed) if used in cases just similar, but not identical to this one, since this approach assumes the initial working dir was /:
  RUN mkdir dir && cd dir && wget http://google.com && rm -rf /dir

> **Comments:**
> **username_5**: Hmm. Why not `rm -rf ../dir`? That avoids the absolute path reference  
> **username_2**: @username_5 it *might* work, but it feels weird (cutting the branch you're sitting on?)  
> **username_5**: That's true; but it works essentially the same way when you specify an absolute path. FWIW, I've done this before, in an interactive shell. `ls` tends to lose its meaning... ;-) You can even `rm -rf ../dir && mkdir ../dir && cd ../dir`, which puts you in an new, empty (valid) folder  
> **username_2**: @username_5 You're right, that's why I said `it feels` - it's just a feeling :)  

---

