Course outline · 0% complete

0/29 lessons0%

Course overview →

Your first Dockerfile

lesson 3-1 · ~14 min · 7/29

Quiz

Warm-up from unit 2: in `docker run -d -p 8080:80 --name web nginx`, what does `nginx` refer to?

The Dockerfile

So far you ran images other people built. To containerize your app, you write a Dockerfile: a plain text file, literally named Dockerfile, that lists the steps to assemble your image.

Here is a complete one for a small Python web app:

FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["python", "server.py"]

Seven lines, and each one is a simple instruction: INSTRUCTION arguments. Docker executes them top to bottom to produce an image. Let's decode every line.

Line by line

  • FROM python:3.12-slim: every image starts from a base image. Instead of building an OS from scratch, you stand on one that already has Python 3.12. Note the pinned tag, as promised in lesson 2-1
  • WORKDIR /app: cd into /app inside the image (creating it), so later instructions run there
  • COPY requirements.txt .: copy a file from your project folder into the image's current directory
  • RUN pip install -r requirements.txt: execute a command during the build and save the result into the image
  • COPY . .: now copy the rest of your source code in
  • EXPOSE 8000: documentation that the app listens on port 8000. It does not publish the port, -p still does that at run time
  • CMD ["python", "server.py"]: the default command a container runs when started. Exactly one per image, and it runs at docker run time, not build time

The same pattern in any language

The Dockerfile above is not Python-specific, it is a template. Here is the Node.js version of the exact same idea:

FROM node:22-slim
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]

Same skeleton, different base image and package manager: pinned slim base, dependency manifest copied first, dependencies installed, then the code. Once you can read one Dockerfile you can read almost all of them, because 90% of real Dockerfiles are this seven-line shape with the language swapped. (Why the manifest is copied before the code is the star of lesson 3-3.)

Building it

docker build -t myapp:v1 .
  • -t myapp:v1: tag the resulting image with a name and version, so you can docker run myapp:v1 later
  • .: the build context, the folder whose files COPY is allowed to see. The dot means the current directory

The workflow you now own end to end:

git clone <your project>
docker build -t myapp:v1 .
docker run -d -p 8080:8000 myapp:v1

Anyone with Docker can run those three commands on any machine and get an identical running app. That is the fix for lesson 1-1's broken server.

Quiz

What is the difference between RUN and CMD in a Dockerfile?

Problem

Which Dockerfile instruction must come first and picks the base image you build on top of?