What is a Dockerfile?
A Dockerfile is a text file with instructions for building a Docker image. Each instruction creates a layer in the image.
Dockerfile Instructions
| Instruction | Purpose |
|---|---|
FROM | Base image to build upon |
RUN | Execute commands during build |
COPY | Copy files from host to image |
ADD | Like COPY, but supports URLs and tar extraction |
WORKDIR | Set working directory |
EXPOSE | Document which ports the app uses |
ENV | Set environment variables |
CMD | Default command when container starts |
ENTRYPOINT | Fixed command (CMD becomes arguments) |
VOLUME | Create mount point |
LABEL | Add metadata |
Building a Python Flask App
Project Structure
my-app/
├── Dockerfile
├── requirements.txt
├── app.py
└── .dockerignore
app.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return {'message': 'Hello from NextGen Playground!', 'status': 'running'}
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)requirements.txt
flask==3.0.0
Dockerfile
# Start from Python base image
FROM python:3.11-slim
# Set working directory
WORKDIR /app
# Copy requirements first (better caching)
COPY requirements.txt .
# Install dependencies
RUN pip install --no-cache-dir -r requirements.txt
# Copy application code
COPY app.py .
# Document the port
EXPOSE 5000
# Command to run when container starts
CMD ["python", "app.py"].dockerignore
__pycache__
*.pyc
.git
.env
node_modules
.venv
Building the Image
# Build with tag
docker image build -t my-flask-app:1.0 .
# Build with different tag
docker image build -t my-flask-app:latest .
# List images
docker image lsThe . is the build context — the directory Docker sends to the daemon.
Running Your Image
docker container run -d --name flask-app \
-p 5000:5000 \
my-flask-app:1.0
# Test it
curl http://localhost:5000Understanding Layers & Caching
Each instruction creates a layer. Docker caches layers for faster rebuilds:
FROM python:3.11-slim # Layer 1 (cached)
WORKDIR /app # Layer 2 (cached)
COPY requirements.txt . # Layer 3 (cached if file unchanged)
RUN pip install -r requirements.txt # Layer 4 (cached if requirements unchanged)
COPY app.py . # Layer 5 (rebuilt if code changes)
CMD ["python", "app.py"] # Layer 6Best practice: Put things that change least at the top, things that change most at the bottom.
CMD vs ENTRYPOINT
# CMD: default command, can be overridden
CMD ["python", "app.py"]
# docker run my-app → runs python app.py
# docker run my-app bash → runs bash instead
# ENTRYPOINT: fixed command, CMD becomes arguments
ENTRYPOINT ["python"]
CMD ["app.py"]
# docker run my-app → runs python app.py
# docker run my-app test.py → runs python test.pyMulti-Stage Builds
Reduce final image size by separating build and runtime:
# Stage 1: Build
FROM node:18 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Stage 2: Production (only runtime needed)
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]Result: Final image contains only nginx + built files (not Node.js, npm, source code).
Pushing to Docker Hub
# Login
docker login
# Tag for Docker Hub (username/image:tag)
docker image tag my-flask-app:1.0 yourusername/my-flask-app:1.0
# Push
docker image push yourusername/my-flask-app:1.0
# Others can now pull it
docker image pull yourusername/my-flask-app:1.0Saving/Loading Images (Offline)
# Save to tar file
docker image save -o my-app.tar my-flask-app:1.0
# Load from tar file (on another machine)
docker image load -i my-app.tarDockerfile Best Practices
- Use specific base image tags —
python:3.11-slimnotpython:latest - Minimize layers — Chain RUN commands with
&& - Order for caching — Dependencies before source code
- Use .dockerignore — Exclude unnecessary files
- Don't run as root — Add
USERinstruction - Use multi-stage builds — Smaller production images
- One process per container — Keep containers focused
Summary
- Dockerfiles define how to build images layer by layer
FROM→RUN→COPY→EXPOSE→CMDis the typical flow- Layer caching speeds up rebuilds — order instructions wisely
- Multi-stage builds create small production images
- Push to Docker Hub to share images with your team
- Use
.dockerignoreto exclude unnecessary files from builds
Next Steps
Next, we'll use Docker Compose to define and run multi-container applications with a single command.