Docker - Day2

Welcome to Day2 of our Docker blog series! Yesterday, we explored why Docker exists and how it solves problems in traditional software deployment. Today, we’ll dive into one of Docker’s core components — the Dockerfile.

What is a Dockerfile?

A Dockerfile is a simple text file that contains a set of instructions Docker uses to build an image. Think of it as a blueprint or a recipe. Just like a chef follows a recipe to cook a dish, Docker follows the Dockerfile to create a container image.

Why Use a Dockerfile?

Before Dockerfiles, creating containers was manual and error-prone. With a Dockerfile:

  • 🔁 You ensure repeatability (same build every time)

  • 📋 You version your container specs

  • 🤝 Teams collaborate easily on containerized applications

  • 🚀 Automation becomes possible via CI/CD

Basic Dockerfile Syntax

Here’s a minimal example of a Dockerfile that runs a Python Flask app:

# Base image
FROM python:3.9-slim

# Set working directory
WORKDIR /app

# Copy project files
COPY . .

# Install dependencies
RUN pip install -r requirements.txt

# Expose port
EXPOSE 5000

# Command to run the app
CMD ["python", "app.py"]

Let’s break down some commonly used instructions:

InstructionPurpose
FROMSpecifies the base image (every image starts from another image)
WORKDIRSets the working directory inside the container
COPYCopies files from your host to the image
RUNRuns a command inside the image during build time
EXPOSEDocuments the port the app runs on
CMDProvides the default command to run the container
  • ENV — Set environment variables

  • ENTRYPOINT — Similar to CMD, but for defining the main executable

  • ADD — Like COPY, but supports URL and TAR extraction

  • LABEL — Add metadata to images

Best Practices for Writing Dockerfiles

✅ Use slim base images (python:3.9-slim, alpine)
✅ Leverage .dockerignore to avoid copying unnecessary files
✅ Combine commands in a single RUN to reduce layers
✅ Pin versions for deterministic builds
✅ Use multi-stage builds for production (more on this later)

Hands-On: Build and Run Your First Dockerfile

Let’s build an image using the Dockerfile above.
Step 1: Create a Flask app
📝 app.py

from flask import Flask
app = Flask(_name_)

@app.route('/')
def hello():
    return "Hello, Docker World!"

if _name_ == '_main_':
    app.run(host='0.0.0.0', port=5000)

Step 2: Create requirements.txt

flask

Step 3: Paste the earlier example into a file named Dockerfile.

# Base image
FROM python:3.9-slim

# Set working directory
WORKDIR /app

# Copy files
COPY . .

# Install dependencies
RUN pip install -r requirements.txt

# Expose the port
EXPOSE 5000

# Start the app
CMD ["python", "app.py"]

Step 4: Build Docker image

docker build -t flask-app . # Build image from Dockerfile

docker images # List local images

Step 5 : Run Docker Container

docker run -d -p 5000:5000 flask-app # Run Docker container using flask-app image

docker ps # List all running Containers

Output :
Now, visit http://localhost:5000 in your browser. You should see: Hello, Docker World!

Port Binding

When your application runs inside a Docker container, it’s isolated from your host machine — including its network. That means if your app runs on port 5000 inside the container, your host machine (like your laptop) can’t access it unless you explicitly bind a port.

Port binding is the process of mapping a port from your host machine to a port inside the container. This allows traffic to flow between your local environment and the containerized application.

Syntax:

docker run -p <host_port>:<container_port> image_name

For example:

docker run -p 8080:5000 flask-app

This means:

  • The container is running a Flask app on port 5000

  • You can access it on your host at port 8080

  • Docker bridges the request from localhost:8080 to container:5000

  • <container_port> is the port your app listens to inside the container.

  • <host_port> is the port you use to access the app from outside.

  • You can bind multiple containers with different ports, like -p 8081:5000, -p 8082:5000, etc.

Conclusion

The Dockerfile is your first step to mastering Docker images. It's declarative, repeatable, and enables powerful automation. In tomorrow's post, we’ll explore how containers behave at runtime, how to manage them, and how Docker Compose helps with multi-container setups.

1
Subscribe to my newsletter

Read articles from Harika Devulapally directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Harika Devulapally
Harika Devulapally

DevOps Engineer with expertise in AWS, Docker, Kubernetes, Terraform, and Ansible. Focused on automation, performance, and security