Writing Your First Dockerfile
So far you have used pre-built images from Docker Hub. Now it is time to build your own. A Dockerfile is a plain text file that tells Docker how to build your custom image. You write the recipe; Docker follows it.
What a Dockerfile Is
A Dockerfile is a set of instructions, one per line. Docker reads each instruction from top to bottom and executes them in order. Each instruction creates a new layer in the image. Together, those layers form your complete application image.
Think of a Dockerfile as a step-by-step guide you leave for a new chef in your kitchen. "Start with a clean kitchen. Install the oven. Put in the ingredients. Set the temperature. When the door opens, serve the cake." Docker follows your guide and produces the exact same result every time.
A Simple Python App to Dockerize
Create a folder called my-app. Inside it, create two files.
File 1: app.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def home():
return 'Hello from my Docker container!'
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
File 2: requirements.txt
flask==3.0.0
Now create the Dockerfile (no file extension) inside the same folder:
# Start from an official Python image FROM python:3.11-slim # Set the working directory inside the container WORKDIR /app # Copy requirements file first (for caching) COPY requirements.txt . # Install the dependencies RUN pip install -r requirements.txt # Copy the rest of the application code COPY . . # Tell Docker which port the app listens on EXPOSE 5000 # The command to run when the container starts CMD ["python", "app.py"]
Every Dockerfile Instruction Explained
FROM — The Starting Point
FROM python:3.11-slim python:3.11-slim ┌────────────┬──────────┐ │ python │ 3.11 │ -slim │ (image) │ (tag) │ (variant) └────────────┴──────────┘ "slim" = minimal Ubuntu base, smaller image size Alternatives: python:3.11, python:3.11-alpine
Every Dockerfile must start with FROM. It defines the base image you build on top of. Docker Hub has official images for Python, Node.js, Java, Go, and hundreds more.
WORKDIR — Your App's Home Directory
WORKDIR /app
This creates the /app directory inside the container and sets it as the current directory for all following commands. Without WORKDIR, files scatter all over the container's root directory.
COPY — Moving Files Into the Image
COPY requirements.txt .
↑ ↑
file on your destination
machine inside container
(. means current WORKDIR)
The first COPY brings only requirements.txt. This is intentional — it is a caching optimization covered in Topic 10.
RUN — Executing Commands During Build
RUN pip install -r requirements.txt
RUN executes a shell command during the image build. The result becomes part of the image layer. Use RUN to install software, create directories, or set permissions.
EXPOSE — Documenting the Port
EXPOSE 5000
EXPOSE is documentation — it tells anyone reading the Dockerfile that the container listens on port 5000. It does not actually open the port. You open the port when you run the container with -p.
CMD — The Startup Command
CMD ["python", "app.py"]
CMD defines the default command to run when the container starts. Write it as a JSON array (this is called exec format). The first element is the executable; the rest are arguments.
Your Complete Folder Structure
my-app/ ├── app.py ├── requirements.txt └── Dockerfile
You are ready to build. In the next topic, you learn how to turn this Dockerfile into a working image using docker build.
Common Dockerfile Instructions Reference
FROM → Base image to start from WORKDIR → Set working directory inside container COPY → Copy files from host to container ADD → Like COPY but also handles URLs and .tar files RUN → Run a command during build ENV → Set environment variables ARG → Build-time variables (not in final image) EXPOSE → Document which port the app uses CMD → Default startup command (can be overridden) ENTRYPOINT → Fixed startup command (harder to override) VOLUME → Declare a mount point for persistent data USER → Run as a specific user (not root)
