Docker Desktop Tutorial: Run a Flask App and Fix Common Errors
Your Python app works on your laptop. A teammate downloads it, installs a different dependency version, and gets an error before the first page opens. Docker helps you package the application with its runtime and dependencies so the team can start from the same image.
In this practical, you will build a small NeoAI Tech lab API, run it with Docker Desktop, inspect its logs, and deliberately change its configuration. The goal is to understand what happened when the command worked—and where to look when it did not.
Who this is for: BCA and MCA students, Python beginners, and anyone starting DevOps. You need basic Python knowledge, a text editor, Docker Desktop running with Linux containers, and internet access for the initial build. You do not need Python installed on your laptop for the container steps. Outputs below are expected examples, not captured execution results.
1. Understand Docker using a kitchen example
Think of a Dockerfile as a written cooking procedure. An image is the prepared kit containing the application, Python runtime, and installed packages. A container is one running instance created from that kit. A registry is a place to store and distribute images.
Two containers can start from the same image, just as two kitchens can use the same kit. Each has its own process environment and writable container layer. The analogy has limits: a container is not a complete virtual machine. Linux containers on Docker Desktop run through a Linux environment, and operating-system and CPU compatibility still matter.
In DevOps, this means a CI pipeline can build an image, test it, and promote that same image to another environment. Configuration and data still need their own management. Docker alone does not provide a complete deployment process.
2. Confirm that Docker is ready
Start Docker Desktop, wait for its engine to be ready, and open PowerShell or your terminal. Run these commands one at a time:
docker version
docker infoThe first command should show both Client and Server information. If you see only client information followed by a connection error, fix the engine connection before writing application code. A working docker command does not by itself prove that the engine is running.
Create a fresh directory for this exercise and open it in your editor:
mkdir neo-flask-lab
cd neo-flask-lab3. Create a tiny application with two endpoints
Create app.py with the following contents. Save the file exactly as app.py; on Windows, make sure the editor has not added .txt.
import os
from flask import Flask, jsonify
app = Flask(__name__)
@app.get("/")
def home():
return jsonify(
message="Your first Docker app is running",
lab=os.getenv("LAB_NAME", "NeoAI Tech Docker Lab")
)
@app.get("/health")
def health():
return jsonify(status="ok"), 200
The / route returns a greeting and a configurable lab name. The /health route returns a simple HTTP 200 response when this application can answer a request. It does not check a database or other external service.
Create requirements.txt:
Flask>=3.1,<4
waitress>=3,<4Flask handles routing and responses. Waitress serves the Flask application through the WSGI interface. These dependency ranges keep the exercise readable but allow package versions to change between builds. For a shared project, test and lock exact dependencies, including transitive dependencies, and record the image digest you distribute.
This exercise uses Waitress instead of Flask's development server. A WSGI server is one part of deployment; adding it does not automatically make the entire application ready for public production use.
4. Write the Dockerfile
Create a file named Dockerfile, without an extension:
FROM python:3.12-slim
WORKDIR /app
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
RUN useradd --create-home --uid 10001 appuser
COPY --chown=appuser:appuser app.py .
USER appuser
EXPOSE 8000
CMD ["waitress-serve", "--listen=0.0.0.0:8000", "app:app"]FROM selects the Python base image. WORKDIR establishes the working directory. COPY brings selected project files into the image. RUN executes build steps, such as installing dependencies. USER makes the application run as the non-root user created earlier. CMD provides the default startup command.
The requirements file is copied before application code so Docker can often reuse the dependency installation layer when only app.py changes. The final app:app means the app object inside the app.py module. The Python version shown is a deliberate example, not a claim that it is the newest release.
EXPOSE documents port 8000. It does not publish that port to your laptop. The Waitress address 0.0.0.0 means listening on all interfaces inside the container; it is not the address you should type into your browser.
5. Keep unnecessary files out of the build
Create .dockerignore beside the Dockerfile:
.git/
.venv/
venv/
__pycache__/
*.pyc
.env
.env.*
*.logThis keeps common local files out of the build context. Check your project for other private files before building. The Dockerfile copies only the two required files, which also makes its inputs easy to review.
Your directory should now contain app.py, requirements.txt, Dockerfile, and .dockerignore. Run the build from this directory:
docker build -t neo-flask-lab:1.0 .The final dot selects the current directory as the build context. The -t option names and tags the image. The first build may take longer because Docker downloads the base image and Python packages.
6. Run the container and understand both ports
docker run -d --name neo-flask-lab -p 127.0.0.1:8080:8000 neo-flask-lab:1.0Open http://localhost:8080 in your browser. You should see JSON containing the greeting and the default lab name. Open http://localhost:8080/health to see:
{"status":"ok"}Read the mapping as three parts: 127.0.0.1 is your laptop's loopback address, 8080 is the laptop port, and 8000 is the container port. Your browser reaches laptop port 8080; Docker forwards the request to the application on container port 8000. Binding to loopback limits normal access to this laptop.
The -d option runs the container in the background. --name gives it a convenient label. The last argument identifies the image. If laptop port 8080 is already occupied, use 127.0.0.1:8081:8000 and browse to localhost:8081; the application can still listen on port 8000 inside the container.
7. Inspect before you guess
Use these commands to see whether the container is running and whether the application reported a startup problem:
docker ps
docker ps -a
docker logs neo-flask-lab
docker port neo-flask-labdocker ps shows running containers; docker ps -a also shows stopped containers. Logs can reveal import errors or startup failures. docker port shows the published mapping. Waitress does not produce a full HTTP access log by default, so no new log line after a browser refresh does not necessarily indicate a failed request.
You can also check the health endpoint from inside the container:
docker exec neo-flask-lab python -c "import urllib.request; print(urllib.request.urlopen('http://127.0.0.1:8000/health').read().decode())"If this works while the browser fails, focus on the host address, published port, and local networking. If it fails too, inspect the application and its listening port. This comparison helps separate an application problem from a port-mapping problem.
8. Change configuration without rebuilding
The lab name comes from an environment variable. Stop and remove only the container created in this tutorial, then create a replacement with a new value:
docker stop neo-flask-lab
docker rm neo-flask-lab
docker run -d --name neo-flask-lab -p 127.0.0.1:8080:8000 -e LAB_NAME="BCA DevOps Practical" neo-flask-lab:1.0Refresh the home page. The lab field should now show BCA DevOps Practical. The image is unchanged; its runtime configuration changed. This example has no stored user data. In a stateful application, plan persistent storage before removing containers.
Editing your local app.py is different: the running container uses the copy already baked into the image. For this workflow, rebuild the image and recreate the container after a code change. Restarting the old container does not copy your edited file into it.
9. Five common errors and focused fixes
Cannot connect to the Docker daemon: start Docker Desktop and check docker info again. On Windows, confirm the Linux container backend is available. If necessary, inspect docker context ls to see which engine the CLI targets.
Port is already allocated: another process or container owns the host port. Choose 8081 in the mapping and update the browser URL. Changing EXPOSE alone will not solve the collision.
The container name is already in use: run docker ps -a. If the existing neo-flask-lab is this exercise's disposable container, stop and remove it using the commands above. Otherwise choose a different name.
ModuleNotFoundError or unable to import app: check spelling, confirm app.py contains app = Flask(__name__), and read the build output for package installation errors. Rebuild after correcting the source.
Browser cannot connect even though the container is running: check docker port, use http rather than https for this local lab, and verify that Waitress listens on 0.0.0.0:8000. Use the inside-container request from the previous section to narrow the cause.
10. Turn the tutorial into portfolio evidence
Add a /course endpoint that returns a course name and semester. Rebuild with tag 1.1, run it, and record the response. Include your source files, Dockerfile, exact commands, dependency versions, and one error you diagnosed in a Git repository. Keep credentials out of the repository.
A useful README explains why the two ports differ, why an image and a container are separate objects, and when a rebuild is necessary. Capture your own screenshots of the browser response and docker ps output. These demonstrate understanding more clearly than a copied command list.
When finished, stop and remove the lab container. Optionally remove the image if you no longer need it:
docker stop neo-flask-lab
docker rm neo-flask-lab
docker image rm neo-flask-lab:1.0What should you learn next?
Use Docker Compose when you want to describe an application and its supporting services together. Add automated tests before building in CI. Explore Kubernetes when you need to learn scheduling, replicas, service discovery, and recovery across a cluster. Public deployment also requires appropriate TLS, access controls, secret management, monitoring, and dependency maintenance.
Want to adapt this lab for your classroom or a project workshop? Contact NeoAI Tech with your students' level, preferred topics, and learning objectives.

$50
Product Title
Product Details goes here with the simple product description and more information can be seen by clicking the see more button. Product Details goes here with the simple product description and more information can be seen by clicking the see more button

$50
Product Title
Product Details goes here with the simple product description and more information can be seen by clicking the see more button. Product Details goes here with the simple product description and more information can be seen by clicking the see more button.

$50
Product Title
Product Details goes here with the simple product description and more information can be seen by clicking the see more button. Product Details goes here with the simple product description and more information can be seen by clicking the see more button.


Comments