Day 45: Dockerfile Debugging - Fixing Build Errors 🔍

Welcome back! 👋 Day 45 of the 100 Days Cloud DevOps Challenge, and today we're mastering Dockerfile debugging! This is real-world troubleshooting - fixing syntax errors, configuration issues, and build failures. Let's debug! 🎯
🎯 The Mission - Fix Broken Dockerfile
It's troubleshooting day - fixing a failing Docker build:
📋 TASK TICKET #DEV-8045 - Dockerfile Build Failure Fix
Priority: URGENT
Type: Dockerfile Debugging & Fix
Status: Build Failing - Blocking Deployment
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
PROJECT: Image Build Pipeline
Team: Nautilus DevOps
Developer: Team Member (Build Failing)
Server: App Server 1
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
BACKGROUND:
└─ Team member creating custom Docker image
└─ Dockerfile written but build fails
└─ Errors during docker build process
└─ Blocking application deployment
REQUIREMENTS:
1. Dockerfile Location:
└─ Path: /opt/docker/Dockerfile
└─ Server: App Server 1 (stapp01)
└─ Already exists (broken)
2. Fix Build Errors:
└─ Identify issues in Dockerfile
└─ Fix syntax errors
└─ Fix configuration issues
└─ Make build successful
3. Constraints (DO NOT CHANGE):
⚠️ Base image (FROM instruction)
⚠️ Valid configurations
⚠️ Data files (index.html, etc.)
⚠️ Functional requirements
4. Success Criteria:
└─ docker build completes successfully
└─ No errors in build output
└─ Image created and usable
└─ All functionality preserved
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
STATUS: URGENT - Build Broken
DEADLINE: ASAP
CRITICAL: Fix only errors, preserve functionality
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
This is production troubleshooting! Real-world Dockerfile debugging! 🚨
🤔 Why Dockerfile Debugging Skills Matter
The Build Failure Problem
Common scenario:
Developer writes Dockerfile:
├─ Typos in instructions
├─ Syntax errors
├─ Wrong paths
├─ Missing files
└─ Build fails!
Impact:
❌ Deployment blocked
❌ Team productivity down
❌ Release delayed
❌ Customer impact
With debugging skills:
DevOps engineer:
├─ Reads error messages
├─ Identifies root cause
├─ Fixes issues
├─ Build succeeds!
Impact:
✅ Deployment unblocked
✅ Team productive
✅ Release on time
✅ Customers happy
Real stat: 78% of Dockerfile issues are simple syntax errors or typos! 📊
Common Dockerfile Errors
Category 1: Syntax Errors
# Wrong: Missing space
FROMubuntu:20.04
# Right:
FROM ubuntu:20.04
# Wrong: Typo in instruction
RRUN apt-get update
# Right:
RUN apt-get update
# Wrong: Invalid format
COPY file.txt
# Right:
COPY file.txt /app/
Category 2: Path Errors
# Wrong: File doesn't exist
COPY missing.txt /app/
# Wrong: Incorrect path
COPY /absolute/path/file.txt /app/
# Right: Relative to build context
COPY file.txt /app/
Category 3: Instruction Order
# Wrong: CMD before RUN
CMD ["nginx"]
RUN apt-get install nginx
# Right: RUN before CMD
RUN apt-get install nginx
CMD ["nginx"]
Category 4: Missing Continuation
# Wrong: Missing backslash
RUN apt-get update
apt-get install nginx
# Right: Backslash continuation
RUN apt-get update && \
apt-get install nginx
🏗️ Understanding the Setup
App Server 1 Details:
Server: stapp01
User: tony
Password: Ir0nM@n
Role: Application Server 1
Current State:
├─ Docker installed
├─ /opt/docker/Dockerfile exists (broken!)
├─ Build context files present
└─ docker build fails
Target State:
├─ Dockerfile fixed
├─ Build completes successfully
├─ Image created
└─ Ready for deployment
Debugging Approach:
Step 1: Access server and locate Dockerfile
Step 2: Read Dockerfile and identify issues
Step 3: Test build to see error messages
Step 4: Fix identified issues
Step 5: Re-test build
Step 6: Verify image works
🛠️ Complete Step-by-Step Implementation
Phase 1: Access and Examine Dockerfile
Step 1.1: SSH to App Server 1
# Connect to App Server 1
ssh tony@stapp01
# Password: Ir0nM@n
You're logged in! ✅
Step 1.2: Navigate to Dockerfile Location
# Change to directory
cd /opt/docker
In correct directory! ✅
Step 1.3: List Files in Directory
# See what files are present
ls -la
Expected output (example):
total 20
drwxr-xr-x 2 root root 4096 Dec 12 10:00 .
drwxr-xr-x 5 root root 4096 Dec 12 10:00 ..
-rw-r--r-- 1 root root 456 Dec 12 10:00 Dockerfile
-rw-r--r-- 1 root root 215 Dec 12 10:00 index.html
Dockerfile and data files present! ✅
Step 1.4: Read the Broken Dockerfile
# Display Dockerfile contents
cat Dockerfile
Expected output (broken example):
FROM ubuntu:latest
LABEL maintainer="devops@nautilus.com"
RUN apt-get update
RUN apt-get install -y nginx
COPY index.html /var/www/html
EXPOSE 80
CMDnginx -g 'daemon off;'
Issues to look for:
Syntax errors (missing spaces, typos)
Missing quotes or brackets
Incorrect paths
Wrong instruction format
Step 1.5: Attempt Build to See Errors
# Try building to see error messages
sudo docker build -t test-image .
Expected output (with errors):
[+] Building 2.1s (5/6)
=> [internal] load build definition from Dockerfile
=> => transferring dockerfile: 234B
=> [internal] load .dockerignore
=> [1/4] FROM docker.io/library/ubuntu:latest
=> [2/4] RUN apt-get update
=> ERROR [3/4] CMDnginx -g 'daemon off;'
------
> [3/4] CMDnginx -g 'daemon off;':
------
Dockerfile:10
--------------------
8 | EXPOSE 80
9 |
10 | >>> CMDnginx -g 'daemon off;'
--------------------
ERROR: failed to solve: process "/bin/sh -c CMDnginx -g 'daemon off;'" did not complete successfully: exit code 127
Error identified! 🔍
Phase 2: Identify All Issues
Common issues in Dockerfiles:
Issue 1: Missing space in CMD
# Wrong:
CMDnginx -g 'daemon off;'
# Right:
CMD nginx -g 'daemon off;'
Issue 2: CMD should use array format
# Wrong:
CMD nginx -g 'daemon off;'
# Right:
CMD ["nginx", "-g", "daemon off;"]
Issue 3: Missing destination in COPY
# Wrong:
COPY index.html
# Right:
COPY index.html /var/www/html/
Issue 4: Typo in instruction
# Wrong:
RRUN apt-get update
# Right:
RUN apt-get update
Issue 5: Missing quotes
# Wrong:
LABEL maintainer=devops@nautilus.com
# Right:
LABEL maintainer="devops@nautilus.com"
Step 2.1: Analyze Each Line
# Check line by line
cat -n Dockerfile
Look for:
Spelling of instructions (FROM, RUN, COPY, CMD, etc.)
Spaces after instructions
Proper quote usage
Array format for CMD/ENTRYPOINT
Complete COPY paths
Phase 3: Fix the Dockerfile
Step 3.1: Create Backup
# Always backup before editing
sudo cp Dockerfile Dockerfile.backup
Backup created! ✅
Step 3.2: Edit Dockerfile
# Edit the file
sudo vi Dockerfile
# or
sudo nano Dockerfile
Fix the issues identified:
Before (broken):
FROM ubuntu:latest
LABEL maintainer="devops@nautilus.com"
RUN apt-get update
RUN apt-get install -y nginx
COPY index.html /var/www/html
EXPOSE 80
CMDnginx -g 'daemon off;'
After (fixed):
FROM ubuntu:latest
LABEL maintainer="devops@nautilus.com"
RUN apt-get update && \
apt-get install -y nginx && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
COPY index.html /var/www/html/
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
Changes made:
Combined RUN commands (best practice)
Added cleanup (reduce image size)
Fixed COPY destination (added trailing slash)
Fixed CMD (added space, used array format)
Step 3.3: Alternative Common Fixes
If using httpd/Apache:
# Before:
CMDapachectl -D FOREGROUND
# After:
CMD ["apachectl", "-D", "FOREGROUND"]
If file path issue:
# Before:
COPY index.html /usr/local/apache2/htdocs
# After:
COPY index.html /usr/local/apache2/htdocs/
If RUN command issue:
# Before:
RRUN apt-get install nginx
# After:
RUN apt-get install nginx
Phase 4: Test the Fixed Dockerfile
Step 4.1: Build with Fixed Dockerfile
# Attempt build again
sudo docker build -t test-image .
Expected output (success):
[+] Building 45.2s (10/10) FINISHED
=> [internal] load build definition from Dockerfile
=> => transferring dockerfile: 345B
=> [internal] load .dockerignore
=> [internal] load metadata for docker.io/library/ubuntu:latest
=> [1/5] FROM docker.io/library/ubuntu:latest
=> [internal] load build context
=> => transferring context: 215B
=> [2/5] RUN apt-get update && apt-get install -y nginx
=> [3/5] COPY index.html /var/www/html/
=> [4/5] EXPOSE 80
=> [5/5] CMD ["nginx", "-g", "daemon off;"]
=> exporting to image
=> => exporting layers
=> => writing image sha256:abc123...
=> => naming to docker.io/library/test-image
Build successful! 🎉
Step 4.2: Verify Image Created
# List images
sudo docker images
Expected output:
REPOSITORY TAG IMAGE ID CREATED SIZE
test-image latest abc123def456 10 seconds ago 178MB
ubuntu latest 3b418d7b466a 2 weeks ago 77.8MB
Image created successfully! ✅
Step 4.3: Test Run Container
# Test the image works
sudo docker run -d --name test-container -p 8080:80 test-image
Expected output:
f8e7d9c6b5a4e3d2c1b0a9f8e7d6c5b4a3e2d1c0b9a8f7e6d5c4b3a2e1d0c9b8
Container started! ✅
Step 4.4: Verify Service Running
# Check if nginx responding
curl http://localhost:8080
Expected output:
<!DOCTYPE html>
<html>
<head>
<title>Welcome to Nautilus</title>
</head>
<body>
<h1>Hello from Nautilus DevOps Team!</h1>
</body>
</html>
Service working correctly! ✅
Step 4.5: Check Container Logs
# View logs to ensure no errors
sudo docker logs test-container
Should show nginx startup messages without errors ✅
Phase 5: Clean Up Test Resources
Step 5.1: Stop Test Container
# Stop the test container
sudo docker stop test-container
sudo docker rm test-container
Test container removed! ✅
Step 5.2: Remove Test Image (Optional)
# Remove test image if needed
sudo docker rmi test-image
Cleaned up! ✅
Phase 6: Final Verification
Step 6.1: Rebuild to Confirm
# Final build test
cd /opt/docker
sudo docker build -t final-image .
Should complete without errors ✅
Step 6.2: Display Fixed Dockerfile
# Show the corrected version
cat Dockerfile
Dockerfile now correct and working! ✅
Step 6.3: Document Changes
# Document what was fixed
cat > /opt/docker/FIXES.txt << 'EOF'
Dockerfile Fixes Applied:
1. Fixed CMD instruction:
- Before: CMDnginx -g 'daemon off;'
- After: CMD ["nginx", "-g", "daemon off;"]
- Issue: Missing space, wrong format
2. Combined RUN commands:
- Reduced layers
- Added cleanup
3. Fixed COPY destination:
- Added trailing slash
- Ensures proper directory copy
4. All builds now succeed
EOF
cat /opt/docker/FIXES.txt
Changes documented! ✅
TASK COMPLETE - DOCKERFILE FIXED AND BUILDING! 🚀
🔍 Understanding Common Dockerfile Issues
Syntax Error Patterns
Pattern 1: Missing Spaces
# Error: No space after instruction
FROMubuntu:latest
COPYfile.txt /app
CMDnginx
# Fixed:
FROM ubuntu:latest
COPY file.txt /app
CMD nginx
Pattern 2: Wrong Quotes
# Error: Mixing quotes
CMD ['nginx', "-g", "daemon off;"]
# Fixed:
CMD ["nginx", "-g", "daemon off;"]
Pattern 3: Missing Backslash
# Error: Multi-line without continuation
RUN apt-get update
apt-get install nginx
# Fixed:
RUN apt-get update && \
apt-get install nginx
Pattern 4: Invalid Instruction
# Error: Typo
RRUN apt-get update
COPPY file.txt /app
EXPOSE80
# Fixed:
RUN apt-get update
COPY file.txt /app
EXPOSE 80
Build Context Issues
Issue: File Not Found
# Dockerfile
COPY /absolute/path/file.txt /app/
# Error: File not found
# Fix: Use relative path
COPY file.txt /app/
Issue: Directory vs File
# Wrong: Copies directory itself
COPY /opt/app /app
# Right: Copies directory contents
COPY /opt/app/ /app/
# or
COPY /opt/app/* /app/
Debugging Process
Step-by-step debugging:
1. Read error message carefully
├─ Shows line number
├─ Indicates problem type
└─ Often suggests fix
2. Check syntax at error line
├─ Spelling correct?
├─ Space after instruction?
├─ Proper format?
3. Verify file paths
├─ Files exist in build context?
├─ Paths relative?
├─ Correct destinations?
4. Test build incrementally
├─ Comment out lines
├─ Build to find exact failure
├─ Uncomment and fix
5. Use build cache
├─ Earlier layers cached
├─ Only rebuilds changed layers
└─ Faster iteration
💡 Key Takeaways
✨ Read error messages carefully - they show line numbers ✨ Common issue: missing spaces after instructions ✨ CMD/ENTRYPOINT use array format ["cmd", "arg1", "arg2"] ✨ COPY needs destination always end with / ✨ Check spelling of instructions (FROM, RUN, COPY, etc.) ✨ Combine RUN commands with && to reduce layers ✨ Always backup before editing ✨ Test incrementally to isolate issues ✨ Use docker build output - shows exactly where it fails ✨ Paths are relative to build context
🎓 Interview Questions
Q1: What's the difference between CMD and ENTRYPOINT, and what format should each use?
Answer: Both define container startup but behave differently. CMD: Provides default command/arguments, can be overridden by docker run arguments, entire CMD replaced. Format options: Shell form: CMD nginx -g 'daemon off;' (runs in /bin/sh -c), Exec form: CMD ["nginx", "-g", "daemon off;"] (runs directly, preferred). ENTRYPOINT: Defines container executable, docker run arguments appended (not replaced), makes container behave like executable. Format options: Shell form: ENTRYPOINT nginx (PID 1 is shell), Exec form: ENTRYPOINT ["nginx"] (PID 1 is nginx, preferred). Together: dockerfile ENTRYPOINT ["nginx"] CMD ["-g", "daemon off;"] # Result: nginx -g 'daemon off;' # docker run image -V # Result: nginx -V (CMD replaced) Best practice: Always use exec form (array) for both, ensures proper signal handling, no shell overhead, PID 1 is your actual process. Common error: CMDnginx (missing space and array).
Q2: Why do Dockerfile instructions fail silently or produce cryptic errors?
Answer: Docker tries to execute instructions as shell commands. Example failure: dockerfile CMDnginx -g 'daemon off;' Docker interprets as RUN instruction, tries to execute shell command "CMDnginx", command not found → cryptic error. Another example: dockerfile COPY file.txt Missing destination → tries to copy to root with filename "file.txt", permission denied or unexpected behavior. Why cryptic: Instructions become shell commands, shell errors propagate, context lost in translation, build cache masks issues. Debugging approach: Read error from bottom up (most recent), check line number in error, verify instruction syntax, use --progress=plain for verbose output: docker build --progress=plain ., test commands manually in running container. Prevention: Use explicit array format, always specify full paths, validate Dockerfile syntax before building, use linters (hadolint, dockerlint).
Q3: How do you debug a Dockerfile that builds successfully but the resulting image doesn't work?
Answer: Build success ≠ runtime success. Debugging strategy: Step 1: Run interactively: bash docker run -it --entrypoint /bin/bash image-name # Get shell in container, test manually Step 2: Check logs: bash docker logs container-name # See what's failing at runtime Step 3: Inspect image: bash docker inspect image-name # Check CMD/ENTRYPOINT, environment variables Step 4: Test CMD/ENTRYPOINT manually: bash docker exec container sh -c "nginx -g 'daemon off;'" # Does command actually work? Step 5: Check file permissions: bash docker run image ls -la /app # Are files readable/executable? Common runtime issues: Missing dependencies (installed but library not found), wrong file permissions (chmod not run), incorrect paths (typo in destination), port not exposed properly, process exits immediately (not running in foreground). Example issue: dockerfile RUN apt-get install nginx CMD ["nginx"] # Fails! nginx daemonizes and exits Fix: CMD ["nginx", "-g", "daemon off;"]. Prevention: Test each layer, run containers during development, use multi-stage builds to separate build/runtime dependencies.
Q4: What are the best practices for combining RUN commands in a Dockerfile?
Answer: Strategic combination reduces layers and image size. Bad (many layers): dockerfile RUN apt-get update RUN apt-get install -y nginx RUN apt-get install -y curl RUN apt-get clean RUN rm -rf /var/lib/apt/lists/* Creates 5 layers, cache persists across layers (wasted space). Good (one layer): dockerfile RUN apt-get update && \ apt-get install -y \ nginx \ curl && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* Creates 1 layer, cleanup in same layer (smaller image). When to separate RUN commands: Different logical steps (apt-get vs application install), cache optimization (dependencies vs application code), slow operations (compile vs fast commands). When to combine: Package installations (all dependencies together), installation + cleanup (same layer), related configuration steps. Best practices: Use && for command chaining, use backslash continuation for readability, clean up in same RUN (apt-get clean), order by change frequency (least to most). Example optimization: dockerfile # Dependencies (rarely change) RUN apt-get update && apt-get install -y nginx # Application (changes often) COPY app.py /app/ RUN pip install -r /app/requirements.txt
Q5: How do you handle errors in multi-stage Dockerfile builds?
Answer: Multi-stage adds complexity but improves final image. Structure: dockerfile # Stage 1: Build FROM golang:1.21 AS builder WORKDIR /app COPY . . RUN go build -o myapp # Stage 2: Runtime FROM alpine:latest COPY --from=builder /app/myapp / CMD ["/myapp"] Common errors: Error 1: Build stage fails: Debug build stage separately: bash docker build --target builder -t test-build . docker run -it test-build sh # Inspect build stage Error 2: COPY --from fails: File doesn't exist in build stage, wrong path specified. Fix: verify build output location, use absolute paths. Error 3: Runtime stage missing dependencies: Built binary requires libraries not in alpine. Fix: copy libraries, use compatible base, statically compile. Debugging approach: Build each stage individually (--target), run intermediate stages, verify files copied correctly, check for missing runtime dependencies. Best practice: Name stages clearly (builder, runtime), minimize final stage size, copy only necessary artifacts, test each stage independently.
Q6: What tools and techniques help prevent Dockerfile errors before building?
Answer: Multiple validation approaches. 1. Linters: Hadolint (popular, comprehensive): bash docker run --rm -i hadolint/hadolint < Dockerfile Checks for: best practices violations, deprecated instructions, security issues, common mistakes. 2. Docker BuildKit syntax validation: bash docker buildx build --check . Validates before building. 3. IDE plugins: VSCode Docker extension (syntax highlighting, IntelliSense), vim with Dockerfile syntax. 4. CI/CD validation: yaml # GitHub Actions - name: Lint Dockerfile uses: hadolint/hadolint-action@v1 with: dockerfile: Dockerfile 5. Manual checklist: All instructions spelled correctly, spaces after instructions, CMD/ENTRYPOINT in array format, COPY has destinations, paths are relative to context. 6. Dry-run patterns: Build to specific layer: bash docker build --target stage-name . Validate without full build: bash docker buildx build --platform=local -o type=oci . Prevention > Debugging: Catch errors before building, saves time and frustration, automated in pipelines, team consistency.
🌟 Dockerfile Debugging Checklist
Pre-Build Checks:
# 1. Verify Dockerfile exists
ls -la Dockerfile
# 2. Check syntax with linter
hadolint Dockerfile
# 3. Verify files in build context
ls -la
# 4. Check for hidden characters
cat -A Dockerfile | grep '\^M' # Windows line endings
# 5. Validate YAML if using
docker-compose config
During Build:
# Build with detailed output
docker build --progress=plain .
# Build to specific stage
docker build --target builder .
# No cache (force rebuild)
docker build --no-cache .
# Build with build args for debugging
docker build --build-arg DEBUG=true .
Post-Build Testing:
# Test image
docker run --rm -it image-name sh
# Check image layers
docker history image-name
# Inspect image config
docker inspect image-name
# Test actual CMD/ENTRYPOINT
docker run --rm image-name
🔧 Common Fix Patterns
Fix 1: CMD Format
# Before:
CMDnginx -g 'daemon off;'
CMD nginx -g daemon off
CMD [nginx, -g, daemon off;]
# After:
CMD ["nginx", "-g", "daemon off;"]
Fix 2: COPY Destination
# Before:
COPY index.html
COPY index.html /var/www/html
COPY *.html /var/www/html
# After:
COPY index.html /var/www/html/
COPY index.html /var/www/html/index.html
COPY *.html /var/www/html/
Fix 3: RUN Instruction
# Before:
RRUN apt-get update
RUN apt-get update
apt-get install nginx
# After:
RUN apt-get update
RUN apt-get update && \
apt-get install nginx
Fix 4: Path Issues
# Before:
COPY /opt/app/index.html /app/ # Absolute host path
WORKDIR app # Missing slash
# After:
COPY index.html /app/ # Relative to context
WORKDIR /app # Absolute container path
🎉 Final Thoughts
You've successfully mastered Dockerfile debugging! This is essential DevOps troubleshooting:
What you accomplished:
✅ Identified Dockerfile syntax errors
✅ Fixed CMD instruction format
✅ Corrected COPY destinations
✅ Combined RUN commands (best practice)
✅ Successfully built working image
✅ Verified image functionality
Real-world impact:
Unblocked deployment: Fixed broken build pipeline
Team productivity: Removed blocker for team
Knowledge sharing: Documented common issues
Process improvement: Better validation going forward
Rapid resolution: Quick fix minimizes downtime
Key lessons learned:
Always read error messages carefully
Most issues are simple syntax errors
CMD/ENTRYPOINT need array format
COPY always needs destination
Combine RUN for efficiency
Test incrementally
Backup before editing
Document fixes for team
This is production troubleshooting! Every DevOps engineer debugs Dockerfiles regularly! 💪
🚀 What's Next?
Day 45 complete! 🎉 You've mastered Dockerfile debugging!
Skills Mastered Today:
✅ Dockerfile error identification
✅ Syntax error debugging
✅ Build failure troubleshooting
✅ Testing and verification
✅ Best practices application
Coming up: More Docker adventures - Docker security, image optimization, production deployments!
Day: 45/100
Challenge: KodeKloud Cloud DevOps
Date: December 21, 2025
Topic: Dockerfile Debugging and Fixing Build Errors
What's your Dockerfile debugging process? Share your troubleshooting tips and common errors you've encountered! 🔍




