RUN: 可以有多个。越少越好
CMD:全篇只能有一个。
1. RUN will not ends with \, e.g.
RUN command1 \
command 2\ # 这里不要有这个 \
CMD ['tail', '-f', '/dev/null'] # wrong, CMS will be consider as the last line of RUN
2. only 1 CMD in 1 Dockerfile
example of Dockerfile:
FROM cr.loongnix.cn/library/debian:buster
RUN set -eux; \
apt-get update && \
apt-get install -y --no-install-recommends \
autoconf bison build-essential \
libssl-dev libreadline-dev zlib1g-dev \
libyaml-dev libncurses5-dev libffi-dev \
libgdbm-dev libdb-dev wget && \
\
wget https://cache.ruby-lang.org/pub/ruby/3.1/ruby-3.1.1.tar.gz && \
echo "fe6e4782de97443978ddba8ba4be38d222aa24dc3e3f02a6a8e7701c0eeb619d ruby-3.1.1.tar.gz" | sha256sum --check --strict && \
tar -xzf ruby-3.1.1.tar.gz && \
cd ruby-3.1.1 && \
./configure \
--prefix=/usr/local \
--enable-shared \
--disable-install-doc \
--with-opt-dir=/usr/local/lib \
--host=loongarch64-linux-gnu \
CFLAGS="-march=loongarch64 -O2" && \
make -j$(nproc) && \
make install && \
# remove useless files
cd / && \
rm -rf ruby-3.1.1 && \
rm -rf ruby-3.1.1.tar.gz && \
apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false && \
rm -rf /var/lib/apt/lists/*
# Default command (keep container running)
CMD ["irb"]
deepseek answer:
In Dockerfiles, the **`RUN` command ends when the current line ends** (unless you use line continuation with `\`). However, the behavior differs slightly depending on whether you use:
### **1. Shell Form (Default)**
```dockerfile
RUN command1 ; command2 ; command3
```
- **Ends at newline** unless escaped with `\`.
- **`;` separates multiple commands in a single `RUN`** (like in Bash).
- **Example**:
```dockerfile
RUN echo "Hello" ; echo "World"
```
→ Runs **two commands** in **one layer**.
### **2. Exec Form (JSON Array)**
```dockerfile
RUN ["executable", "arg1", "arg2"]
```
- **Must be a valid JSON array**.
- **No `;` needed** (each array element is an argument).
- **Example**:
```dockerfile
RUN ["/bin/bash", "-c", "echo Hello && echo World"]
```
### **Key Differences**
| Feature | Shell Form (`RUN cmd`) | Exec Form (`RUN ["cmd"]`) |
|-----------------|----------------------|--------------------------|
| **Command separator** | `;` or `&&` | Must use `&&` inside a shell |
| **Variable expansion** | Yes (e.g., `$PATH`) | No (unless via shell) |
| **Shell processing** | Uses `/bin/sh -c` | Direct execution |
| **Line continuation** | `\` at end of line | Not applicable (JSON) |
---
### **Best Practices**
1. **Prefer `&&` over `;`** (fails fast on errors):
```dockerfile
RUN apt-get update && \
apt-get install -y curl
```
2. **Use `\` for multi-line readability**:
```dockerfile
RUN apt-get update \
&& apt-get install -y \
curl \
wget
```
3. **Avoid unnecessary `;`** (can hide errors):
```dockerfile
# ❌ Risky (continues even if `cmd1` fails)
RUN cmd1 ; cmd2
# ✅ Better (stops if `cmd1` fails)
RUN cmd1 && cmd2
```
---
### **Your Case Fix**
Your original issue (`CMD` inside `RUN`) happened because:
```dockerfile
RUN make install; \
CMD ["tail", ...] # ❌ "CMD" is not a shell command
```
→ **Solution**: Move `CMD` outside `RUN`.
---
### **Summary**
- `RUN` ends at newline (unless `\` continues it).
- `;` separates commands **within** a single `RUN`.
- **For Docker instructions** (like `CMD`, `COPY`), always put them at the top level—never inside `RUN`.