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.
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.
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.
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.
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"]
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"]
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:
The most important part for me was:
depends_on:
db:
condition: service_healthy
If I only wrote:
depends_on:
- db
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:
- Start the PostgreSQL container
- Wait until PostgreSQL becomes healthy
- Start the Flask application
- 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
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
Because the application is self-contained, I did not create a docker-compose.yml.
The process only requires two steps:
- Build the image
- 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.
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.” 🐳
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
.envfile - 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
- https://qiita.com/tosane932/items/ac18b633c8c87b9807bb
- https://qiita.com/tosane932/items/f0aeed574d39c5fa5093
- https://qiita.com/tosane932/items/e19ed4a2ffe27f53faf0
- https://qiita.com/tosane932/items/c1609f17cddf842f1e7c
- https://qiita.com/tosane932/items/b9b6576c1fda3d3a76d2
Pre-Production Inspection Trilogy
- https://qiita.com/tosane932/items/7a15e942745545a37b6a
- https://qiita.com/tosane932/items/3db0e915439048624a40
- https://qiita.com/tosane932/items/b9f32a1b03b99405ad0a





Top comments (0)