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.
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
Error response from daemon: failed to create the ipvlan port
How to fix 'failed to create the ipvlan port' in Docker
toomanyrequests: Rate exceeded for anonymous users
How to fix 'Rate exceeded for anonymous users' in Docker Hub