Sunday, 06 September 2026
Advertisement Advertise Your advert could be here Reach thousands of learners and ICT professionals across Rwanda. Contact us
Advertisement Opportunity Jobs, scholarships & hackathons Fresh openings from Rwandan job boards are pulled in every hour. See openings

Your first Dockerfile

Docker and CI/CD · lesson 2 of 12

In this lesson: Write a Dockerfile and build and run an image from it.

A Dockerfile is the recipe. Each line is a step, and running docker build follows them to produce an image. It replaces the page of setup instructions that was always slightly out of date.

A working example

FROM php:8.2-fpm-alpine

# System packages the application needs
RUN apk add --no-cache git unzip libzip-dev \
 && docker-php-ext-install pdo_mysql zip

# Where everything lives inside the image
WORKDIR /app

# Dependencies first — see the next lesson for why
COPY composer.json composer.lock ./
RUN composer install --no-dev --no-scripts --optimize-autoloader

# Then the application itself
COPY . .

# Do not run as root
RUN chown -R www-data:www-data /app
USER www-data

EXPOSE 9000
CMD ["php-fpm"]
docker build -t yanjye:latest .
docker run -d --name yanjye -p 8080:9000 yanjye:latest

What each instruction does

InstructionMeaning
FROMThe image you start from. Everything below builds on it.
RUNRun a command while building. Its result becomes part of the image.
WORKDIRSet the current directory for everything after it.
COPYCopy files from your project into the image.
ENVSet an environment variable, at build time and at run time.
USERWhich user the following steps — and the container — run as.
EXPOSEDocumentation: this is the port the app listens on. It publishes nothing by itself.
CMDWhat to run when a container starts. Overridable on the command line.
ENTRYPOINTThe fixed command; CMD becomes its default arguments.

Pin your base image

FROM php:8.2-fpm-alpine     # good — a specific version
FROM php:latest             # a build that worked in March breaks in June

latest is not a version, it is a moving pointer. A build that is reproducible today and different tomorrow is not reproducible, and you will discover the change at the worst moment.

Choose a small base

BaseRough sizeNotes
ubuntu~80 MBFamiliar, full package manager.
debian-slim~30 MBA good default.
alpine~7 MBTiny. Uses musl instead of glibc, which occasionally breaks binaries.
distroless~2 MBNo shell at all. Very secure, harder to debug.
Smaller is not only about disk. Every package in the image is code an attacker might exploit and something you are responsible for patching. A 900 MB image with a full desktop toolchain has a large attack surface for no benefit — nothing in production needs a compiler.

.dockerignore

COPY . . copies everything, including things you do not want in the image at all.

.git
node_modules
vendor
.env
storage/logs
*.md
tests
Never COPY a .env into an image. Images get pushed to registries and shared. Anyone who pulls it can run docker history and read what went in. Configuration is passed at run time, not baked in.

Do not run as root

By default a container runs as root. If your application is compromised, the attacker is root inside the container — and that is the starting point for escaping to the host. Two extra lines remove the whole class of problem:

RUN adduser -D -u 1000 app
USER app

One process per container

Do not put your web server, your application and your database in one image. Each container should do one job, so that each can be restarted, scaled and updated on its own. If a container needs a process manager to keep three things alive, it should have been three containers.

Try it yourself

Write a Dockerfile for the smallest thing you have — even a static site served by nginx. Build it, run it, open it in a browser. Then run docker history <your-image> and read the layers: every line of your Dockerfile is there, with the size it added.

Create a free account to save progress

All lessons in this track

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
    Networking and ports ~25 min account needed
  7. 7
  8. 8
  9. 9
    Making the pipeline deploy ~30 min account needed
  10. 10
    Infrastructure as code ~30 min account needed
  11. 11
    Releasing without downtime ~30 min account needed
  12. 12
Advertisement Yanjye Learn a new digital skill this week ICT, programming and professional courses with graded weekly assignments. Start free