Complete "From Prototype to Production: The Deployment Mindset" first to unlock this lesson.Go to previous lesson

Introduction

When you build an AI-generated web app on your laptop, it runs because Python is installed, dependencies are in the right place, and environment variables are set just so. Move that app to a server, a teammate's machine, or a cloud platform, and suddenly nothing works. Docker solves this by packaging your application together with everything it needs into a single portable unit called a container.

In this lesson, you will write a Dockerfile for a typical Python web app, build it into an image, and use Docker Compose to run the app alongside any services it depends on (like a database or Redis). The focus is practical: by the end, you should be able to containerize a small AI-generated web app with confidence.

Writing Your First Dockerfile

A Dockerfile is a text file with instructions Docker reads to assemble an image. Think of it as a recipe: each line adds a layer. For an AI-generated web app, the most common setup is a FastAPI or Flask app served by Uvicorn.

Here is a minimal Dockerfile for a Python web app:

dockerfile
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

Let us unpack each line. `FROM python:3.11-slim` starts from an official lightweight Python image. `WORKDIR /app` sets the working directory inside the container so subsequent commands run there. `COPY requirements.txt .` copies just the dependency file first, which lets Docker cache the `pip install` step and skip it when only your code changes. `COPY . .` then copies your source code. `EXPOSE 8000` documents which port the app listens on. `CMD` defines the command that runs when the container starts.

A common mistake is to copy everything before installing dependencies. That forces Docker to rebuild the heavy `pip install` layer every time you tweak a single Python file. Order matters for speed.

sơ đồ các layer của Docker image khi build Dockerfile ở trên, từ base image python:3.11-slim đến application code

Building and Running the Image

Once the Dockerfile exists, two commands turn it into a running container. First, build the image and tag it with a name:

bash
docker build -t my-ai-app:1.0 .

The `.` tells Docker where to find the Dockerfile (current directory). The `-t` flag assigns a name and version tag, which makes the image easy to reference later. Then run it:

bash
docker run -p 8000:8000 my-ai-app:1.0

The `-p 8000:8000` flag maps port 8000 on your host to port 8000 inside the container. Without it, the app runs but you cannot reach it from your browser. Visit `http://localhost:8000` and you should see your app responding.

To verify the image was built correctly, list all images:

bash
docker images

To see running containers, use `docker ps`. Add `-a` to include stopped ones.

Using Docker Compose for Multi-Service Apps

Most AI web apps do not run alone. They typically need a database (PostgreSQL), a cache (Redis), or both. Managing each with separate `docker run` commands gets messy fast. Docker Compose lets you declare all services in one YAML file and start them together.

Create a file named `docker-compose.yml`:

yaml
services:
web:
build: .
ports:
- "8000:8000"
environment:
- DATABASE_URL=postgresql://user:pass@db:5432/mydb
depends_on:
- db
db:
image: postgres:16
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=pass
- POSTGRES_DB=mydb
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:

Notice how the `web` service references `db` by hostname. Docker Compose creates a private network where services reach each other by service name. The `depends_on` directive tells Docker to start the database before the web app. The named volume `pgdata` keeps database data safe even if the container is removed.

To bring everything up:

bash
docker compose up --build

Add `-d` to run in the background. To stop and remove the containers:

bash
docker compose down

Common Mistakes and How to Avoid Them

Forgetting the `.dockerignore` file is a frequent oversight. Create one alongside your Dockerfile:

bash
echo -e "__pycache__\n.git\n.env\n*.pyc" > .dockerignore

Without it, your image carries unnecessary files, slows down builds, and risks leaking secrets like `.env` files containing API keys.

Another pitfall is hardcoding secrets inside the image. Never put API keys or database passwords directly in the Dockerfile. Use environment variables or mount secret files at runtime instead.

Finally, running containers as root by default is a security risk for production. Add a non-root user to your Dockerfile when you are ready to deploy seriously.

Summary

A Dockerfile describes how to package your app into an image. Docker Compose orchestrates multiple containers together through a single YAML file. For an AI-generated web app, the typical workflow is: write a Dockerfile that copies dependencies first, build an image with `docker build`, run it with `docker run -p`, and extend to Compose when you add services like databases. Mastering these four commands and one YAML structure gets you 90 percent of the way to a containerized app ready for any cloud platform.

Lesson practice

Lesson Checkpoint

Review what you learned and get feedback on your work.

0/1
Answered · 0/1