Operations 11 min read

How to Build Enterprise‑Ready Docker Images in 5 Minutes

This guide walks you through the four essential principles for production Docker images, demonstrates a complete multi‑stage Dockerfile for a Flask app, shows how to tag, build, verify, and deploy with docker‑compose, and warns against unsafe shortcuts like docker commit.

Xiao Liu Lab
Xiao Liu Lab
Xiao Liu Lab
How to Build Enterprise‑Ready Docker Images in 5 Minutes

Production Image Core Principles

Reproducible : Anyone can recreate the exact same image from the configuration, eliminating "it works on my machine" issues.

Auditable : Every step is recorded, enabling security audits without extra effort.

Securely Controlled : Run as a non‑root user, manage ports, permissions, and users explicitly.

Small Size : Remove unnecessary dependencies to keep the image lightweight and fast to transfer.

Hands‑on: Build a Production‑grade Flask Image

Step 1 – Prepare project files

Create a working directory and add the source files: mkdir flask-prod-app && cd flask-prod-app app.py (minimal Flask app)

from flask import Flask
app = Flask(__name__)

@app.route("/")
def index():
    return "This is a production‑grade Docker image!"

requirements.txt (pin exact versions)

flask==2.3.3          # web framework
gunicorn==21.2.0     # production WSGI server

.dockerignore (exclude unnecessary files)

# version control
.git
.gitignore

# local development artifacts
__pycache__/
*.pyc
.venv

# sensitive files
.env
*.log

Step 2 – Multi‑stage Dockerfile

The builder stage installs dependencies; the final stage contains only the runtime environment.

# ===== Builder stage =====
FROM docker.xuanyuan.run/python:3.10-slim AS builder

# Optional: fast Chinese pip mirror
RUN mkdir -p /root/.pip && \
    echo "[global]
index-url = https://pypi.tuna.tsinghua.edu.cn/simple" > /root/.pip/pip.conf

WORKDIR /build
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt -t ./vendor && \
    rm -rf /root/.cache/pip

# ===== Final stage =====
FROM docker.xuanyuan.run/python:3.10-slim

# Set timezone and install curl (optional)
RUN apt update && apt install -y --no-install-recommends curl && \
    rm -rf /var/lib/apt/lists/* && \
    ln -snf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
    echo Asia/Shanghai > /etc/timezone && \
    useradd -m -u 1001 appuser

USER appuser
WORKDIR /app
COPY --from=builder /build/vendor ./vendor
ENV PYTHONPATH=/app/vendor
COPY --chown=appuser:appuser app.py .
ENV WORKERS=2
EXPOSE 5000
CMD ["sh", "-c", "gunicorn --bind 0.0.0.0:5000 --workers ${WORKERS} --graceful-timeout 30 app:app"]

Step 3 – Build and verify the image

Build with an explicit tag (avoid latest): docker build -t flask-prod-app:1.0-prod . Run locally to test:

docker run -d -p 5000:5000 --name flask-test flask-prod-app:1.0-prod

Check the response: curl http://localhost:5000 Verify the container runs as appuser: docker exec -it flask-test whoami Clean up:

docker stop flask-test && docker rm flask-test

Step 4 – Deploy with Docker Compose

Compose file enforces the non‑root user, resource limits, and health checks.

version: '3.8'
services:
  flask-app:
    image: flask-prod-app:1.0-prod
    user: 1001
    ports:
      - "5000:5000"
    restart: always
    mem_limit: 512m
    cpus: 0.5
    healthcheck:
      test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:5000/')"]
      interval: 30s
      timeout: 10s
      retries: 3
    networks:
      - app-net
networks:
  app-net:
    driver: bridge

Deploy with docker-compose -f docker-compose.prod.yml up -d.

Temporary Scenarios (Never Use in Production)

docker commit : Quick debugging only; lacks reproducibility and audit trail.

docker save/load : Suitable for offline migration; export the image to a tar file, transfer, then load on the target host.

Ops Veteran Red‑Line Checklist

Never run the application as root.

Do not promote images created with docker commit to production.

Avoid exposing ports directly to the internet; use a load balancer or ingress.

Never use the latest tag; always version your images explicitly.

Key Takeaways

Production images should be built with a multi‑stage Dockerfile, run as a non‑root user, and use explicit versioned tags.

Temporary tricks like docker commit or rootfs imports are for debugging only and must not reach production.

Following these practices ensures images pass security audits, stay lightweight, and run reliably in production environments.

DockerdevopsSecurityFlaskProductionImage BuildingMulti‑stage
Xiao Liu Lab
Written by

Xiao Liu Lab

An operations lab passionate about server tinkering 🔬 Sharing automation scripts, high-availability architecture, alert optimization, and incident reviews. Using technology to reduce overtime and experience to avoid major pitfalls. Follow me for easier, more reliable operations!

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.