Back to Engineering Notes
DockerEngineering Note

9. Write a Docker File

Create a custom Docker image using a Dockerfile that:

🎯 Objective

Create a custom Docker image using a Dockerfile that:

Uses Ubuntu 24.04 as the base image
Installs Apache2
Changes Apache's listening port from 80 to 6400
Exposes port 6400
Runs Apache in the foreground
Builds and tests the custom image

The final container should respond on:

plain text
http://localhost:6400

🧠 Concept

A Dockerfile is a text file containing instructions Docker uses to build an image.

The flow is:

plain text
Dockerfile
    ↓
docker build
    ↓
Docker Image
    ↓
docker run
    ↓
Container

Instead of manually changing a running container, a Dockerfile makes the setup:

Repeatable
Version-controlled
Automated
Easier to rebuild

🔍 Step 1: Check Running Containers

Check currently running containers:

bash
docker ps

This helps confirm the Docker environment before starting.


📂 Step 2: Go to the Docker Directory

Navigate to:

bash
cd /opt/docker/

Check the directory contents:

bash
ls -lah

📝 Step 3: Create the Dockerfile

Create the Dockerfile:

bash
sudo touch Dockerfile

Edit it:

bash
sudo vi Dockerfile

Add:

docker
FROM ubuntu:24.04

RUN apt-get update && \
    apt-get install -y apache2 && \
    sed -i 's/Listen 80/Listen 6400/' /etc/apache2/ports.conf && \
    rm -rf /var/lib/apt/lists/*

EXPOSE 6400

CMD ["apache2ctl", "-D", "FOREGROUND"]

🧩 Dockerfile Breakdown

FROM

docker
FROM ubuntu:24.04

Defines the base image.

In this case:

plain text
Base Image → Ubuntu 24.04

Every instruction that follows builds on top of this image.


RUN

docker
RUN apt-get update && \
    apt-get install -y apache2 && \
    sed -i 's/Listen 80/Listen 6400/' /etc/apache2/ports.conf && \
    rm -rf /var/lib/apt/lists/*

This performs several operations during the image build.

First:

bash
apt-get update

updates the package repository metadata.

Then:

bash
apt-get install -y apache2

installs Apache2.

Then:

bash
sed -i 's/Listen 80/Listen 6400/' /etc/apache2/ports.conf

changes Apache's listening port:

plain text
80
 ↓
6400

Finally:

bash
rm -rf /var/lib/apt/lists/*

removes cached package metadata to reduce the final image size.


🌐 Step 4: Expose Port 6400

The Dockerfile contains:

docker
EXPOSE 6400

This documents that the containerized application listens on port:

plain text
6400

Important:

> EXPOSE does not publish the port to the Docker host by itself.

Port publishing happens with:

bash
-p

when the container is started.


▶️ Step 5: Run Apache in Foreground

The Dockerfile ends with:

docker
CMD ["apache2ctl", "-D", "FOREGROUND"]

Docker containers normally keep running only while their main process is active.

Running Apache in foreground mode ensures:

plain text
Apache
   ↓
Main Container Process
   ↓
Container remains running

If Apache ran only as a background daemon, the container could exit after the main process finished.


🔍 Step 6: Verify the Dockerfile

Check the contents:

bash
cat /opt/docker/Dockerfile

Expected:

docker
FROM ubuntu:24.04

RUN apt-get update && \
    apt-get install -y apache2 && \
    sed -i 's/Listen 80/Listen 6400/' /etc/apache2/ports.conf && \
    rm -rf /var/lib/apt/lists/*

EXPOSE 6400

CMD ["apache2ctl", "-D", "FOREGROUND"]

🏗️ Step 7: Build the Docker Image

Make sure you are inside:

bash
cd /opt/docker

Build the image:

bash
sudo docker build -t custom-apache .

Command Breakdown

plain text
docker build

Builds an image from a Dockerfile.

plain text
-t custom-apache

Tags the new image as:

plain text
custom-apache

The final:

plain text
.

means:

> Use the current directory as the build context.

Docker looks for:

plain text
./Dockerfile

by default.


🔍 Step 8: Verify the Image

Check the available images:

bash
sudo docker image ls

You should see:

plain text
REPOSITORY      TAG       IMAGE ID       CREATED          SIZE
custom-apache   latest    ...            ...              ...

Because no explicit tag was provided, Docker uses:

plain text
latest

So the image is effectively:

plain text
custom-apache:latest

🚀 Step 9: Run the Container

Create a container from the image:

bash
sudo docker run -d \
  --name apache-test \
  -p 6400:6400 \
  custom-apache

Command Breakdown

plain text
-d

Runs the container in detached mode.

plain text
--name apache-test

Names the container:

plain text
apache-test
plain text
-p 6400:6400

Maps:

plain text
Host Port 6400
      ↓
Container Port 6400

Finally:

plain text
custom-apache

is the image used to create the container.


🌐 Port Mapping

The port flow is:

plain text
Browser / curl
      │
      │ localhost:6400
      ▼
Docker Host
Port 6400
      │
      │ -p 6400:6400
      ▼
Container
Port 6400
      │
      ▼
Apache

This works because:

docker
Listen 6400

inside Apache matches:

bash
-p 6400:6400

in Docker.


🔍 Step 10: Verify the Container

Check running containers:

bash
sudo docker ps

You should see apache-test.

Example:

plain text
CONTAINER ID   IMAGE           PORTS                    NAMES
abc123         custom-apache   0.0.0.0:6400->6400/tcp   apache-test

The important part is:

plain text
0.0.0.0:6400->6400/tcp

This confirms the host-to-container port mapping.


🧪 Step 11: Test Apache

Test the container:

bash
curl http://localhost:6400

If everything is working correctly, Apache should return its default HTML page.

The request flow is:

plain text
curl localhost:6400
      ↓
Host Port 6400
      ↓
Container Port 6400
      ↓
Apache
      ↓
HTML Response ✅

🗑️ Step 12: Remove the Test Container

After verification, remove the container:

bash
sudo docker rm -f apache-test

f

Forces Docker to:

plain text
Stop container
      ↓
Remove container

in one command.

Without -f, you would normally do:

bash
sudo docker stop apache-test
sudo docker rm apache-test

🧠 Dockerfile vs Manual Container Changes

Using manual commands:

plain text
Run Ubuntu Container
        ↓
Install Apache
        ↓
Change Port
        ↓
Configure Startup

requires repeating those steps every time.

With a Dockerfile:

plain text
Dockerfile
    ↓
docker build
    ↓
Reusable Image
    ↓
docker run

The infrastructure becomes repeatable.


⚠️ Important Notes

Dockerfile instructions are normally written in uppercase for readability.
FROM defines the base image.
RUN executes commands while building the image.
EXPOSE documents the application's container port.
CMD defines the default command when the container starts.
Apache must run in foreground mode for the container to remain active.
EXPOSE 6400 does not automatically publish the port.
p 6400:6400 publishes the container port to the Docker host.
Removing /var/lib/apt/lists/* helps reduce unnecessary image size.
Dockerfile-based builds are more reproducible than manually configuring containers.

⚙️ Complete Workflow

Check Docker:

bash
docker ps

Navigate:

bash
cd /opt/docker/

Inspect files:

bash
ls -lah

Create Dockerfile:

bash
sudo touch Dockerfile
sudo vi Dockerfile

Dockerfile:

docker
FROM ubuntu:24.04

RUN apt-get update && \
    apt-get install -y apache2 && \
    sed -i 's/Listen 80/Listen 6400/' /etc/apache2/ports.conf && \
    rm -rf /var/lib/apt/lists/*

EXPOSE 6400

CMD ["apache2ctl", "-D", "FOREGROUND"]

Verify:

bash
cat /opt/docker/Dockerfile

Build:

bash
cd /opt/docker
sudo docker build -t custom-apache .

Run:

bash
sudo docker run -d \
  --name apache-test \
  -p 6400:6400 \
  custom-apache

Verify:

bash
sudo docker ps

Test:

bash
curl http://localhost:6400

Clean up:

bash
sudo docker rm -f apache-test

🧪 Validation Checklist

[ ] /opt/docker directory checked
[ ] Dockerfile created
[ ] Ubuntu 24.04 base image configured
[ ] Apache2 installation added
[ ] Apache port changed from 80 to 6400
[ ] Port 6400 exposed
[ ] Apache configured to run in foreground
[ ] Docker image built as custom-apache
[ ] Container created as apache-test
[ ] Port 6400:6400 published
[ ] Container running
[ ] curl http://localhost:6400 succeeds
[ ] Test container removed

📌 Summary

The Dockerfile is:

docker
FROM ubuntu:24.04

RUN apt-get update && \
    apt-get install -y apache2 && \
    sed -i 's/Listen 80/Listen 6400/' /etc/apache2/ports.conf && \
    rm -rf /var/lib/apt/lists/*

EXPOSE 6400

CMD ["apache2ctl", "-D", "FOREGROUND"]

Build it:

bash
sudo docker build -t custom-apache .

Run it:

bash
sudo docker run -d \
  --name apache-test \
  -p 6400:6400 \
  custom-apache

Test:

bash
curl http://localhost:6400

The complete flow is:

plain text
Dockerfile
    │
    │ docker build
    ▼
custom-apache Image
    │
    │ docker run
    ▼
apache-test Container
    │
    │ Apache listens on 6400
    ▼
Container Port 6400
    │
    │ -p 6400:6400
    ▼
Host Port 6400
    │
    ▼
curl localhost:6400 ✅

The key takeaway is:

> A Dockerfile defines a repeatable image build process. In this task, Ubuntu 24.04 is used to build a custom Apache image that listens on port 6400 and runs Apache as the container's foreground process.