DEV Community

Cover image for 🐳 A Truck Driver Dockerized Two Flask Apps After 105 Hours of Learning
tosane932
tosane932

Posted on

🐳 A Truck Driver Dockerized Two Flask Apps After 105 Hours of Learning

Introduction 🐳

Hello from Japan! 🇯🇵

I am a professional truck driver with seven years of experience in logistics, teaching myself Python while working toward a career transition into web engineering.

Without using traditional textbooks, I developed two Flask applications through conversations with AI:

After 105 hours of self-study, I Dockerized both systems so they could be shared and reproduced without depending on my own computer environment.

This article records what I learned as of June 30, 2026.


Docker overview


1. Why I Dockerized My Applications

In my previous article, I found and fixed nine “loading mistakes” in my Flask code.

I removed assumption-driven coding and improved the application so it could protect itself against:

  • Invalid input
  • Missing configuration
  • Unused files
  • Obsolete processing
  • Unsafe production settings

However, one major problem remained until the end:

The applications only worked on my computer.

A different Python version, a different PostgreSQL connection configuration, or a missing dependency could immediately cause the familiar problem:

It worked on my computer.

This had the same structure as unsafe assumption-based driving.

Assuming “it will probably work in this environment too” is dangerous.


Environment differences

2. What Is Docker? 🐳

At first, I did not clearly understand what Docker was.

Even when I tried to think about it from a logistics perspective, it still felt abstract.

Then I connected it to two examples from my own interests, and the concept finally made sense.

2-1. A Mini 4WD Beginner Set 🏎️

A Mini 4WD beginner set contains everything in one box:

  • Chassis
  • Motor
  • Gears
  • Tires

No matter who buys it, the same combination of parts arrives together.

If batteries and a small racing circuit were also included, it would be very similar to Docker.

Everything required to reproduce the same environment is packaged together.

2-2. Frozen Food in a Container 🍱

Frozen food includes the ingredients, seasoning, and container in one package.

You only need to heat it in a microwave.

No matter who prepares it, the result is designed to be almost the same.

Docker works in a similar way.

It packages:

  • Python
  • Flask
  • PostgreSQL
  • Required libraries
  • Application files
  • Runtime settings

into a container.

With one command, the same environment can be created on another machine.

Docker transforms:

A system that only works on my computer

into:

A product that can be shipped and reproduced anywhere.


Docker setup

3. The Dockerization Process Was Surprisingly Smooth

I expected trouble.

I had never used Docker before, so I thought:

I will definitely get stuck somewhere.

However, the actual process was much smoother than expected.

Afterward, I reviewed why it had gone so well.

Reason 1: My Previous Safety Improvements Still Worked

In my previous code review, I added a startup check for required environment variables using the Fail-Fast principle.

That same mechanism continued working inside the Docker container.

If an important configuration value was missing, the application stopped immediately during startup.

Because that protection already existed, I did not have to guess why the container failed later.

Reason 2: I Had Already Unloaded Unnecessary Cargo

Before Dockerizing the applications, I had already removed:

  • Old Excel processing
  • Unused files
  • Obsolete modules
  • Duplicate libraries
  • Unnecessary dependencies

The amount of cargo placed inside the container was already small and organized.

Cleaning the project before containerization made Dockerization easier.

Reason 3: I Had Developed the Habit of Removing Assumptions

Instead of thinking:

This setting will probably work.

I checked each part individually:

  • Is this environment variable available?
  • Is this dependency actually required?
  • Is the database ready?
  • Is this port exposed correctly?
  • Does this command work from a clean environment?

The defensive-driving mindset I use in logistics became the way I approached Docker.

The large obstacle I expected never appeared.

The small improvements I had made beforehand were already protecting me.


Docker workflow

4. The Dockerfile and docker-compose.yml I Actually Wrote

Saying that Dockerization was “surprisingly smooth” is not convincing without showing the actual configuration.

Here is the code I wrote.

4-1. Dockerfile for sales_data_app

# 1. Use Python 3.12 as the base environment.
# The slim image reduces unnecessary size and resource usage.
FROM python:3.12-slim

# 2. Set the working directory inside the container.
WORKDIR /app

# 3. Copy the dependency list first.
COPY requirements.txt .

# 4. Install Python dependencies without storing the pip cache.
RUN pip install --no-cache-dir -r requirements.txt

# 5. Copy the entire application into the container.
COPY . .

# 6. Expose port 5000 so Flask can accept external connections.
EXPOSE 5000

# 7. Start the Flask application when the container launches.
CMD ["python", "app.py"]
Enter fullscreen mode Exit fullscreen mode

4-2. Dockerfile for Puoppo

FROM python:3.11-slim

WORKDIR /app

COPY requirements.txt .

RUN pip install --no-cache-dir -r requirements.txt

COPY . .

EXPOSE 5000

CMD ["python", "app.py"]
Enter fullscreen mode Exit fullscreen mode

As you can see, the only major difference is the Python version in the base image.

The rest of the structure was reused almost exactly.

This taught me an important lesson:

Once you create a working pattern, the second implementation becomes much faster because you can reuse the knowledge.

Comparing these two Dockerfiles makes that improvement in reproducibility easy to see.


4-3. Database Configuration: docker-compose.yml for sales_data_app

Because sales_data_app uses PostgreSQL, the Dockerfile alone was not enough.

I needed a separate database container and a way to connect it to the Flask application.

services:
  web:
    build: .
    ports:
      - "5000:5000"
    env_file:
      - .env
    environment:
      - DATABASE_URL=postgresql://postgres:postgres@db:5432/sales_db
    volumes:
      - ./:/app
    depends_on:
      db:
        condition: service_healthy

  db:
    image: postgres:16
    environment:
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=postgres
      - POSTGRES_DB=sales_db
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 5

volumes:
  postgres_data:
Enter fullscreen mode Exit fullscreen mode

The most important part for me was:

depends_on:
  db:
    condition: service_healthy
Enter fullscreen mode Exit fullscreen mode

If I only wrote:

depends_on:
  - db
Enter fullscreen mode Exit fullscreen mode

the web container could try to connect as soon as the database container started launching.

However, “the database container has started” does not necessarily mean “PostgreSQL is ready to accept connections.”

The web application could attempt a connection before PostgreSQL finished initializing.

By using a healthcheck with pg_isready, the web container waits until the database is ready.

This guarantees the correct order:

  1. Start the PostgreSQL container
  2. Wait until PostgreSQL becomes healthy
  3. Start the Flask application
  4. Connect to the database

This is similar to the Fail-Fast environment-variable check from my previous article.

I did not want assumption-driven behavior to remain in the container startup order.

The application should not assume:

The database is probably ready.

It should confirm:

The database is definitely ready.

That difference helped eliminate environment-related instability.


4-4. Why the Two Applications Use Different Commands

Both projects were Dockerized, but they use different startup commands.

The reason is the database architecture.

sales_data_app

sales_data_app requires multiple containers because it connects to PostgreSQL.

docker compose up --build
Enter fullscreen mode Exit fullscreen mode

This single command starts:

  • The Flask web container
  • The PostgreSQL database container

Docker Compose handles:

  • Container startup order
  • Networking
  • Environment variables
  • Volume configuration
  • Service dependencies

I did not need to configure each connection manually.

Puoppo

Puoppo uses SQLite and does not require a separate database container.

docker build -t puoppo-app .

docker run -p 5000:5000 --env-file .env puoppo-app
Enter fullscreen mode Exit fullscreen mode

Because the application is self-contained, I did not create a docker-compose.yml.

The process only requires two steps:

  1. Build the image
  2. Run the container

The same word, “Dockerization,” can describe different configurations.

When the architecture changes, the required commands also change.

The difference between one container and multiple connected containers appears directly in the execution process.


5. I Dockerized the Second Application Using the Same Pattern

Dockerizing sales_data_app took approximately three hours because it was my first attempt.

After that, I Dockerized Puoppo using the same pattern.

That process took approximately one hour.

The README files contain instructions for trying both applications.

🥪 sales_data_app on GitHub

🕊️ Puoppo on GitHub

Once I had created the first working pattern, I could reuse the knowledge for the second application.

That reduction from three hours to one hour showed me that I had gained reproducible knowledge, not just completed a one-time task.


6. What Changed After Dockerization

The problem:

It only works on my computer

was gone.

With the documented commands, another person can create the same environment.

Docker reduces problems caused by:

  • Different Python versions
  • Missing libraries
  • Dependency-version differences
  • Incorrect PostgreSQL configuration
  • Missing setup steps
  • Differences between operating systems

The project changed from:

“It runs, so it is fine.” ☝️

to:

“It runs the same way in other environments.” 🐳


Docker result

7. What Comes Next

Docker removed many environment differences.

However, the application is still only accessible to people who understand Docker.

Even with clear README instructions, users still need to:

  • Clone the repository
  • Create a .env file
  • Build the image
  • Start the containers
  • Open the local application

That means the application is reproducible, but not yet accessible to everyone.

The next challenge is to create a state where even someone without technical knowledge can use it simply by opening a URL.

The next steps are:

  • Production deployment
  • Public access through a browser
  • Improved deployment automation
  • CI/CD in the future

Docker made the product shippable.

Deployment will make it reachable.


sales_data_app Maintenance Series

Pre-Production Inspection Trilogy

Post-Deployment Maintenance Series

🍞 sales_data_app on GitHub

Top comments (0)