This error occurs when Docker's parser encounters text it doesn't recognize as a valid Dockerfile instruction. Common causes include incorrect file encoding, Windows line endings (CRLF), missing line continuation backslashes, or shell commands without the RUN prefix.
When you run `docker build`, Docker parses your Dockerfile line by line, expecting each line to begin with a valid instruction like FROM, RUN, COPY, CMD, etc. The "unknown instruction" error means Docker found text on a line that doesn't match any recognized Dockerfile command. The error message typically shows which "instruction" Docker couldn't recognize. Ironically, "unknown instruction: RUN" often means Docker didn't actually see "RUN" as the instruction—something invisible is corrupting the parsing. This is usually caused by: - **File encoding issues**: UTF-16 encoding or BOM (Byte Order Mark) characters insert invisible bytes that Docker interprets as part of the instruction name - **Line ending problems**: Windows CRLF (\r\n) endings can cause the previous line's content to bleed into the next line - **Missing line continuations**: When a multi-line command is missing its backslash, the continuation appears as a new (invalid) instruction - **Copy-paste artifacts**: Invisible characters from web pages, PDFs, or rich text editors Docker is intentionally strict about Dockerfile syntax to prevent ambiguous or incorrect builds. A malformed Dockerfile could create containers with missing dependencies, security vulnerabilities, or unpredictable behavior.
File encoding is the most common cause when Docker reports a seemingly valid instruction as "unknown."
In VS Code:
1. Open your Dockerfile
2. Look at the bottom-right status bar for the encoding (e.g., "UTF-8" or "UTF-16 LE")
3. If it's not "UTF-8", click on the encoding and select "Reopen with Encoding" → "UTF-8"
4. Then "Save with Encoding" → "UTF-8"
From the command line:
# Check current encoding
file DockerfileExpected output: Dockerfile: ASCII text or Dockerfile: UTF-8 Unicode text
If you see UTF-16, with BOM, or unusual encoding, convert it:
# Convert from UTF-16 to UTF-8
iconv -f UTF-16 -t UTF-8 Dockerfile > Dockerfile.tmp && mv Dockerfile.tmp Dockerfile
# Remove BOM if present (first 3 bytes of UTF-8 BOM)
sed -i '1s/^\xEF\xBB\xBF//' DockerfileWindows uses CRLF (\r\n) line endings, but Docker expects Unix-style LF (\n). CRLF can cause the carriage return to be interpreted as part of the instruction.
In VS Code:
1. Look at the bottom-right status bar for "CRLF" or "LF"
2. If it shows "CRLF", click it and select "LF"
3. Save the file
From the command line:
# Using dos2unix (install with apt/brew if needed)
dos2unix Dockerfile
# Or using sed
sed -i 's/\r$//' Dockerfile
# Or using tr
tr -d '\r' < Dockerfile > Dockerfile.tmp && mv Dockerfile.tmp DockerfileCheck for CRLF:
# Shows ^M at end of lines if CRLF is present
cat -A Dockerfile | head -5Prevent future issues with .gitattributes:
echo "Dockerfile text eol=lf" >> .gitattributesWhen splitting a RUN command across multiple lines, each line (except the last) must end with a backslash (\). Missing backslashes cause the next line to be interpreted as a new instruction.
Incorrect:
RUN apt-get update &&
apt-get install -y curlDocker sees "apt-get" as an unknown instruction on line 2.
Correct:
RUN apt-get update && \
apt-get install -y curlImportant rules:
- The backslash must be the LAST character on the line
- No spaces or tabs after the backslash
- Comments cannot appear after the backslash
Check for trailing spaces after backslashes:
# Find lines with backslash followed by spaces
grep -n '\\ \+$' DockerfileCopy-pasting from websites, PDFs, or word processors can introduce invisible characters that break parsing.
View hidden characters:
# Show all characters including non-printable ones
cat -A Dockerfile
# Or use hexdump to see exact bytes
hexdump -C Dockerfile | head -20Look for:
- M-BM- prefix (UTF-8 BOM or special characters)
- ^M at end of lines (CRLF)
- Unexpected characters at line beginnings
Clean the file:
# Remove all non-ASCII characters (aggressive)
LC_ALL=C tr -cd '\11\12\15\40-\176' < Dockerfile > Dockerfile.clean
mv Dockerfile.clean DockerfileBest practice: If you suspect copy-paste issues, create a new file and manually retype the content rather than pasting.
Every shell command in a Dockerfile must be prefixed with RUN. Without it, Docker interprets the command name as an instruction.
Incorrect:
FROM ubuntu:22.04
apt-get update
apt-get install -y nginxThis causes "unknown instruction: apt-get".
Correct:
FROM ubuntu:22.04
RUN apt-get update
RUN apt-get install -y nginxBetter (combined for fewer layers):
FROM ubuntu:22.04
RUN apt-get update && \
apt-get install -y nginx && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*Common commands that need RUN:
- Package managers: apt-get, yum, dnf, apk, pip, npm
- File operations: mkdir, chmod, chown, rm, mv, cp
- Downloads: curl, wget
- Any shell command or script
Multiple files named "Dockerfile" with different cases can cause Docker to use the wrong file.
Find all Dockerfile variants:
find . -maxdepth 2 -iname "*dockerfile*" -type fCommon conflicts:
- Dockerfile and dockerfile (case difference)
- Dockerfile in current directory and .docker/Dockerfile
- Backup files like Dockerfile.bak or Dockerfile.old
Solutions:
1. Remove or rename conflicting files:
mv dockerfile dockerfile.backup2. Explicitly specify which Dockerfile to use:
docker build -f ./Dockerfile -t myimage .3. Use a subdirectory with a clear name:
docker build -f docker/production.Dockerfile -t myimage .After making corrections, test your Dockerfile:
# Basic build test
docker build -t test-build .
# With BuildKit for better error messages
DOCKER_BUILDKIT=1 docker build -t test-build .
# Build with no cache to ensure fresh parsing
docker build --no-cache -t test-build .Validate syntax without building (using hadolint):
# Run hadolint linter
docker run --rm -i hadolint/hadolint < DockerfileIf the error persists:
1. Create a minimal test Dockerfile:
FROM alpine:latest
RUN echo "test"2. If this works, add your original content line by line until you find the problematic line
3. Compare the problematic line byte-by-byte:
echo 'RUN apt-get update' | hexdump -C
# Compare with your file
sed -n 'Xp' Dockerfile | hexdump -C # Replace X with line number### Understanding Docker's Parser Behavior
Docker's Dockerfile parser is intentionally strict. When it sees a line, it:
1. Strips leading whitespace
2. Checks if the first word matches a known instruction
3. If no match, reports "unknown instruction"
The parser recognizes these instructions: ADD, ARG, CMD, COPY, ENTRYPOINT, ENV, EXPOSE, FROM, HEALTHCHECK, LABEL, MAINTAINER, ONBUILD, RUN, SHELL, STOPSIGNAL, USER, VOLUME, WORKDIR.
### BuildKit Heredoc Syntax
With BuildKit enabled, you can use heredoc syntax for cleaner multi-line commands:
# syntax=docker/dockerfile:1.4
RUN <<EOF
apt-get update
apt-get install -y curl wget
apt-get clean
rm -rf /var/lib/apt/lists/*
EOFThis eliminates backslash continuation issues entirely.
### Editor Configuration for Consistency
VS Code settings.json:
{
"files.eol": "\n",
"[dockerfile]": {
"files.encoding": "utf8",
"files.eol": "\n"
}
}.editorconfig:
[Dockerfile*]
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true### Git Configuration
Prevent line ending issues in version control:
# Repository-level .gitattributes
echo "Dockerfile text eol=lf" >> .gitattributes
echo "*.sh text eol=lf" >> .gitattributes
# Global Git config (for Windows users)
git config --global core.autocrlf input### CI/CD Considerations
In CI environments, Dockerfiles are often checked out with different line endings:
# GitHub Actions example
- uses: actions/checkout@v4
with:
# Disable automatic line ending conversion
persist-credentials: false
- name: Fix line endings
run: |
sudo apt-get install -y dos2unix
dos2unix Dockerfile### Debugging with Docker BuildKit
BuildKit provides more detailed error messages:
# Enable BuildKit
export DOCKER_BUILDKIT=1
# Build with debug output
docker build --progress=plain -t myimage . 2>&1 | tee build.logThe --progress=plain flag shows complete output including which lines are being parsed.
Error response from daemon: client version X.XX is too new. Maximum supported API version is X.XX
How to fix 'client version is too new' in Docker
dial tcp: i/o timeout
How to fix 'dial tcp: i/o timeout' in Docker
image operating system "linux" cannot be used on this platform
How to fix 'image operating system linux cannot be used on this platform' in Docker
manifest unknown: manifest unknown
How to fix 'manifest unknown' in Docker
cannot open '/etc/passwd': Permission denied
How to fix 'cannot open: Permission denied' in Docker