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:
Instruction | Purpose |
FROM | Specifies the base image (every image starts from another image) |
WORKDIR | Sets the working directory inside the container |
COPY | Copies files from your host to the image |
RUN | Runs a command inside the image during build time |
EXPOSE | Documents the port the app runs on |
CMD | Provides the default command to run the container |
ENV
— Set environment variablesENTRYPOINT
— Similar toCMD
, but for defining the main executableADD
— LikeCOPY
, but supports URL and TAR extractionLABEL
— 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
tocontainer: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.
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