Blog
Published On: 2025-07-15 by Wiseman
How to create a docker image file

Creating a Dockerfile for your application is a fundamental step in containerization with Docker. A Dockerfile is a text file that contains a series of instructions that Docker uses to build a Docker image. This image then contains your application, its dependencies, and everything else it needs to run.

The specific instructions in your Dockerfile will depend heavily on:

  • Your application's programming language.

  • The framework you're using.

  • Its dependencies.

  • How you typically run your application.

Below, I'll walk you through the general structure and provide examples for common scenarios.



What is a Dockerfile?


A Dockerfile is a script composed of various commands (instructions) that Docker runs automatically to build a new image. Each instruction creates a new layer in the Docker image, making images lightweight and efficient.1




General Structure of a Dockerfile


Most Dockerfiles follow a similar pattern:

  1. FROM: Specify a base image (e.g., an OS, a language runtime).

  2. WORKDIR: Set the working directory inside the container.2

  3. COPY / ADD: Copy application code and necessary files into the image.

  4. RUN: Execute commands to install dependencies, compile code, etc.

  5. EXPOSE: Document the ports your application listens on.3

  6. CMD / ENTRYPOINT: Define the command to run when the container starts.



Key Dockerfile Instructions Explained


  • FROM <image>[:<tag>]:

    • This is always the first instruction. It specifies the base image on which your image will be built.

    • Examples: ubuntu:latest, node:18-alpine, python:3.9-slim, openjdk:17-jdk-slim.

    • Choose a base image that includes your application's language runtime and often a minimalist OS distribution (like Alpine or Slim versions) for smaller image sizes.

  • WORKDIR /path/to/app:

    • Sets the current working directory inside the container for subsequent RUN, CMD, ENTRYPOINT, COPY, and ADD instructions.

    • It's good practice to set this early.

  • COPY <source> <destination>:

    • Copies files or directories from your local host machine (where you're building the image) into the Docker image.

    • Example: COPY . . (copies everything from the current build context to the WORKDIR in the image).

    • Best Practice: Copy dependencies files (package.json, requirements.txt) before copying all application code, so dependency installation layer can be cached.

  • RUN <command>:

    • Executes commands during the image build process.4 This is used for installing packages, compiling code, running tests, etc. Each RUN command creates a new layer.

    • Example: RUN apt-get update && apt-get install -y my-package

    • Best Practice: Chain multiple commands in a single RUN instruction using && and \ to reduce the number of layers and image size.

  • EXPOSE <port>:

    • Informs Docker that the container listens on the specified network ports at runtime. This is primarily documentation. To actually map a port from the host to the container, you use the -p flag with docker run.

    • Example: EXPOSE 80 (for a web server), EXPOSE 3000.

  • CMD ["executable", "param1", "param2"]:

    • Provides defaults for an executing container. This is the command that will run when you start a container from your image if you don't specify another command.

    • There can only be one CMD instruction in a Dockerfile. If you provide multiple, only the last one takes effect.

    • Best Practice: Use the exec form (JSON array syntax) as shown above.

    • Example: CMD ["python", "app.py"], CMD ["npm", "start"].

  • ENTRYPOINT ["executable", "param1"] (Optional, but powerful):

    • Configures a container that will run as an executable. Unlike CMD, ENTRYPOINT arguments are always executed. CMD arguments are appended to ENTRYPOINT arguments.

    • Useful for creating images that are intended to be run as specific tools.



Examples for Common Application Types



1. Python Flask/Django Application


Assuming your app structure is:

my_flask_app/
├── app.py
├── requirements.txt
├── Dockerfile
└── .dockerignore

Dockerfile:

Dockerfile
# Use a slim Python image for smaller size
FROM python:3.9-slim-buster

# Set the working directory in the container
WORKDIR /app

# Copy only the requirements file first to leverage Docker cache
COPY requirements.txt .

# Install dependencies
RUN pip install --no-cache-dir -r requirements.txt

# Copy the rest of your application code
COPY . .

# Expose the port your Flask app listens on (default is 5000)
EXPOSE 5000

# Command to run the application when the container starts
CMD ["python", "app.py"]

.dockerignore (important for keeping image small):

__pycache__/
*.pyc
*.env
.git
.venv/
.pytest_cache/


2. Node.js Express Application


Assuming your app structure is:

my_express_app/
├── package.json
├── package-lock.json (or yarn.lock)
├── server.js
├── Dockerfile
└── .dockerignore

Dockerfile:

Dockerfile
# Use a Node.js image with Alpine for smaller size
FROM node:18-alpine

# Set the working directory
WORKDIR /app

# Copy package.json and package-lock.json (or yarn.lock) first
# to install dependencies
COPY package*.json ./

# Install dependencies
RUN npm install

# Copy the rest of your application code
COPY . .

# Expose the port your app listens on (default is often 3000)
EXPOSE 3000

# Command to run the application
CMD ["npm", "start"]

.dockerignore:

node_modules/
npm-debug.log
.git
.env


3. Java Spring Boot Application (JAR)


Assuming you have a built JAR file (e.g., my-app.jar) in your target/ directory:

Dockerfile:

Dockerfile
# Use a lightweight OpenJDK image
FROM openjdk:17-jdk-slim

# Set the working directory
WORKDIR /app

# Copy the built JAR file into the container
# Assuming your JAR is in target/my-app.jar after 'mvn package' or 'gradle build'
COPY target/my-app.jar app.jar

# Expose the port Spring Boot listens on (default is 8080)
EXPOSE 8080

# Command to run the JAR file
ENTRYPOINT ["java", "-jar", "app.jar"]

Note: For Java, you might use a multi-stage build to first compile the application and then copy only the compiled JAR into a smaller runtime image, significantly reducing the final image size.



Best Practices


  1. Use .dockerignore: Similar to .gitignore, this file specifies files and directories to exclude when building the image. This significantly reduces image size and build time.

  2. Use specific base images: Instead of python:latest, use python:3.9-slim-buster for stability and smaller size.

  3. Leverage build cache: Arrange instructions (especially COPY and RUN) so that layers that change infrequently (like dependency installation) are early in the Dockerfile. Docker caches layers, so if a layer hasn't changed, it won't be rebuilt.5



  4. Chain RUN commands: Combine multiple RUN commands using && to reduce the number of layers.

  5. Clean up: After RUN commands that install packages, clean up temporary files to keep the image small (e.g., apt-get clean, rm -rf /var/lib/apt/lists/*).

  6. Non-root user (Security): For production, it's a good practice to run your application as a non-root user inside the container for security reasons. (e.g., RUN adduser --system --group appuser && chown -R appuser:appuser /app && USER appuser).



How to Build Your Docker Image


  1. Navigate to your application's root directory in your terminal (where your Dockerfile is located).

  2. Run the build command:

    Bash


    docker build -t your-image-name:tag .
    
    • -t your-image-name:tag: This tags your image with a name and an optional tag (e.g., my-flask-app:1.0). If no tag is provided, latest is used.

    • .: This specifies the build context, which is the current directory. Docker will look for the Dockerfile here and use this directory for COPY instructions.

After the build completes successfully, you'll have a Docker image that you can then run as a container (docker run your-image-name:tag).



How to Delete Your Docker Image

To remove the image, you need to first remove the container
  1. List all containers (including stopped ones):
    bash
    docker ps -a
    
    Use code with caution.
    This command will show you a list of all containers with their ID, name, image, status, etc.
  2. Identify the container using the image: Look for the container ID c7843abd19ee or the image ID c21a3fefccb8 in the output from the previous step to locate the container associated with your image.
  3. Stop the container: If the container is running, gracefully stop it using:
    bash
    docker stop c7843abd19ee
    
    Use code with caution.
    This sends a SIGTERM signal to the container, allowing it to shut down gracefully.
  4. Remove the container: Once the container is stopped, remove it with:
    bash
    docker rm c7843abd19ee
    
    Use code with caution.
  5. Remove the image: Now you can remove the Docker image using its ID or tag:
    bash
    docker rmi c21a3fefccb8
    
    Use code with caution.
    or
    bash
    docker rmi hello-world:app
    
    Use code with caution.
     

Alternative (Force removal)
If you're absolutely sure you want to remove the container and image regardless of potential data loss or disruptions, you can force the removal in a single command using the -f flag with both docker rm and docker rmi. However, this should be used cautiously, especially in production environments. 
  1. Force remove the container:
    bash
    docker rm -f c7843abd19ee
    
    Use code with caution.
  2. Force remove the image:
    bash
    docker rmi -f c21a3fefccb8
    
    Use code with caution.
     

Using docker system prune for a more comprehensive cleanup
If you have many unused images, containers, and other Docker resources, consider using the docker system prune command, which removes all stopped containers, unused networks, dangling images, and build cache in a single operation. 

bash
docker system prune
Use code with caution.
  • By default, this command will prompt for confirmation.
  • You can add the -f flag to bypass the prompt: docker system prune -f.
  • To also prune anonymous volumes, use the --volumes flag: docker system prune --volumes or docker system prune -a -f --volumes to include unused images and bypass the prompt. 
Remember, removing resources, especially with force options, can lead to data loss if not handled carefully. 
ContainersDocker