Quick Start
What you will build
A tiny Flask health service packaged in a Docker image. You will understand every Dockerfile instruction, build the image, run the container, test it and inspect failures instead of copying commands blindly.
Start from Basics
Before using Docker, understand what the application does. This example has one /health address. When it is working, that address returns a small message saying the service is healthy. It also has a requirements.txt file that records the Flask library it needs.
Create app.py:
01from flask import Flask, jsonify0203app = Flask(__name__)0405@app.get("/health")06def health():07return jsonify(status="healthy", service="maanavan-demo")0809if __name__ == "__main__":10app.run(host="0.0.0.0", port=8000)
Create requirements.txt:
01Flask==3.1.2Pinning a version improves reproducibility. In a real project, use your team’s dependency-locking and vulnerability-update process rather than leaving dependencies permanently frozen.
Core Explanation
Build once, then run the result
Follow the command and observe what Docker creates.
Docker reads the Dockerfile and prepares python-health:1.0.
$ docker build -t delivery-status:1.0 .Click execute — no real system changes are made
The created object will appear here
Architecture / Flow Diagram
Types / Components
Read the Dockerfile as seven small decisions
Create a file named exactly Dockerfile:
01FROM python:3.12-slim0203WORKDIR /app0405COPY requirements.txt .06RUN pip install --no-cache-dir -r requirements.txt0708COPY app.py .0910EXPOSE 800011CMD ["python", "app.py"]
Read it from top to bottom
| Instruction | Decision it records |
|---|---|
FROM | The starting runtime and filesystem |
WORKDIR | The default directory for later instructions and runtime |
first COPY | Add the dependency definition before frequently changing code |
RUN | Install dependencies while building the image |
second COPY | Add the application source |
EXPOSE | Document the port the application expects to use |
CMD | Set the default process when a container starts |
Copying requirements.txt before app.py helps Docker reuse the dependency layer when only application code changes. This is a practical use of the build cache.
Add a .dockerignore
01__pycache__/02*.pyc03.venv/04.git/05.env
The build context is the set of files sent to the builder. A .dockerignore keeps irrelevant, large or sensitive local files out of that context. It is not your only secret-control mechanism, but it reduces accidental inclusion.
Build and start your first container
Run these commands from the directory containing the Dockerfile:
01docker build -t python-health:1.0 .02docker run --rm --name python-health -p 8000:8000 python-health:1.0
The -p 8000:8000 part creates a path from port 8000 on your laptop to port 8000 inside the container. Think of it as connecting the laptop's front door to the application's door. Ports are explained fully in Byte 03; for now, use this mapping so the browser can reach the app.
Open http://localhost:8000/health or run:
01curl http://localhost:8000/healthThe expected response is a JSON object with status set to healthy. --rm removes the stopped container, not the image.
Simple Tanglish — build vs run
docker build app package-ஐ prepare செய்து image create பண்ணும். docker run அந்த image-லிருந்து container start பண்ணும். Code change செய்தால் first image rebuild பண்ணணும்; பிறகு new container start பண்ணணும்.
Manual Setup vs Docker Image
| Question | Manual setup | Docker image |
|---|---|---|
| Who installs the runtime? | Every learner | Image build |
| Who remembers dependencies? | Setup document | Dockerfile |
| How is a version repeated? | Repeat the steps | Start the tagged image |
Real-World Examples
Every learner receives the same kit
Imagine a Python workshop where every learner installs tools manually. Some use different Python versions, others miss a library and several have old configuration. The trainer spends the session repairing laptops.
The image is a prepared lab kit: runtime, dependency and application versions are assembled once. Each container opens a fresh instance of that kit. Learners can still have host-level differences, but the application environment is much more predictable.
- Before Docker
- The app works locally, but there is no repeatable package.
- Team action
- Write the Dockerfile and build python-health:1.0.
- After Docker
- A container returns the expected /health response.
- Before Docker
- Karthik changes app.py, but the old container still shows the previous response.
- Team action
- Rebuild the image and start a new container.
- After Docker
- The browser shows the updated response from the new image.
- Before Docker
- QA receives code but does not know the exact start command.
- Team action
- Share the tagged image and one docker run command.
- After Docker
- QA starts the same prepared application.
Imagine Pannunga
The image is like a prepared practical-lab kit. The Dockerfile is the packing list; docker build prepares the kit; docker run opens one working kit.
Use Cases
Each engineering role uses the same image for a different decision: build it, verify it, automate it or review what entered it.
“How do I record the app setup?”
Write and maintain the Dockerfile“How do I verify the packaged app?”
Run the tagged image and check /health“How do I build it consistently?”
Automate docker build in CI“What entered the image?”
Review base image, dependencies and contextCommon Mistakes and Fixes
| Symptom | Check | Likely cause |
|---|---|---|
| Build cannot find a file | docker build output and current directory | Wrong build context or .dockerignore rule |
| Container exits immediately | docker ps -a then docker logs python-health | Main process completed or crashed |
| Browser cannot connect | port mapping and application bind address | Missing -p or app bound only to 127.0.0.1 |
| Code change not visible | image creation time and tag | Image was not rebuilt |
| Dependency install fails | version, network and package logs | Invalid or unavailable package version |
Useful inspection commands:
01docker image ls02docker ps -a03docker logs python-health04docker inspect python-health
Improve it later for production
The development server in this learning example is not a production server. A production Python web service should normally use a suitable application server, run as a non-root user, expose a health endpoint, receive configuration at runtime and use a deliberately selected base-image version.
Also avoid installing editors and debugging utilities “just in case.” Smaller, purpose-built images reduce attack surface and transfer time.
Common mistakes
- Using
COPY . .before dependency installation and invalidating the build cache on every edit. - Confusing
EXPOSE 8000with publishing a host port;docker run -pperforms the publishing. - Binding the Python app to
127.0.0.1inside the container. - Copying
.env, credentials or a local virtual environment into the image. - Debugging by rebuilding randomly instead of reading build output and container logs.
Key Takeaways
- Confirm that the application works before containerising it.
- A Dockerfile records repeatable build decisions.
docker buildcreates an image.docker runstarts a container..dockerignorekeeps unnecessary files out.EXPOSEdocuments a port;-pcreates the mapping.- Rebuild the image after changing application code.
- Logs identify failures more reliably than random rebuilds.
Final Thought + Next Path
You have now moved from a Docker mental model to a working container. The important skill is not memorising every instruction; it is reading the flow: prepare a tiny working app, record its setup in a Dockerfile, build a named image, start one container and verify the visible result. In Byte 03, you will give that container three practical capabilities: a door for browser traffic, safe storage for selected data and start-time settings that can change without rebuilding the image.
FAQ + Knowledge Check
Choose an answer, inspect the explanation and explain the idea in your own words.
What happens during docker build and docker run?
Strong answer: docker build processes a Dockerfile and build context to produce a layered image. docker run creates a container from that image, adds runtime configuration such as ports and environment variables, and starts the configured process. Build-time decisions belong in the image; environment-specific configuration belongs at runtime.
Does docker run build an image? No. docker build creates the image; docker run starts a container from it.
Why use .dockerignore? To prevent irrelevant or sensitive local files from entering the build context.
Knowledge check: Which instruction defines the default startup command? CMD. What must happen after app.py changes? Rebuild the image.