Building a Docker Image from Scratch on Ubuntu 24.04

Docker Images from Scratch on Ubuntu 24.04

Learn how to build a Docker image from scratch on Ubuntu 24.04 with step-by-step instructions and optimization tips for security, performance, and minimalism.

Introduction

In today’s fast-paced world of software development, containerization has become an essential tool. Docker is at the forefront of this revolution, providing a way to package applications and their dependencies into containers that can run seamlessly on any platform. Building a Docker image from scratch allows developers to have full control over their environment, ensuring that only the necessary components are included. In this blog post, we will dive deep into the process of building a Docker image from scratch on Ubuntu 24.04.

This step-by-step guide will ensure that you understand how to create a minimal image with only the required libraries, which is a great way to optimize performance and reduce potential security vulnerabilities. Along the way, we’ll incorporate CLI examples, charts, and key insights to help make the process clearer and more intuitive.

What is Docker?

Before jumping into the technicalities of creating a Docker image from scratch, let’s quickly review what Docker is and how it fits into the development ecosystem. Docker is a platform used to build, run, and manage containers. These containers are isolated environments that allow software to run consistently across different environments. With Docker, developers can ensure that their application behaves the same regardless of where it runs—whether on a local machine, on a cloud server, or in a production environment.

Docker images serve as the blueprint for containers. They are static snapshots of an application’s environment, which are used to create containers. Docker images can be built on top of existing images, or they can be built entirely from scratch.

Why Build a Docker Image from Scratch?

Building a Docker image from scratch may seem daunting at first, but it has several benefits:

  • Minimalist Approach: By starting from scratch, you have full control over what gets included in your image. You can strip away unnecessary components, reducing the size of the final image and improving performance.
  • Security: Minimal Docker images only include the bare essentials. This minimizes the surface area for potential attacks, as there are fewer vulnerabilities present.
  • Customization: Starting from scratch allows you to customize the environment to your specific needs, ensuring your app runs exactly as intended.
  • Performance: With fewer dependencies, the final Docker image is lighter and faster to build and deploy.

Prerequisites

Before we start building our Docker image, ensure that you have the following:

  • Ubuntu 24.04: Docker will be installed and configured on this version of Ubuntu.
  • Docker: Docker needs to be installed on your system. If it’s not installed yet, follow these steps to get started:
sudo apt update && sudo apt install docker.io -y
sudo systemctl enable --now docker

After installing Docker, restart your system or log out and back in to apply the group changes.

sudo usermod -aG docker $USER
  • Basic knowledge of the command line: We will be using the command line for most of the Docker operations.

Step 1: Creating a Dockerfile

A Dockerfile is a simple text file that contains a series of instructions on how to build a Docker image. These instructions define the base image (or if you’re starting from scratch, the environment), the necessary dependencies, and how the application is built and run. Let’s break down the steps for creating a Docker image from scratch.

Create the Project Directory

First, create a directory to house your Dockerfile and any necessary files:

mkdir my-docker-project && cd my-docker-project

Create the Dockerfile

In the project directory, create a file named Dockerfile:

touch Dockerfile

Now, open this file in your preferred text editor and define the image:

vim Dockerfile

Starting From Scratch

When you start from scratch, the base image is defined as FROM scratch. This means that the image will be empty, and you’ll need to manually define all the components required for your application. Here’s an example of a simple Dockerfile:

# Start from scratch
FROM scratch

# Copy necessary files into the image
COPY ./hello /hello

# Define the command to run
CMD ["/hello"]

In this example:

  • FROM scratch signifies that we are starting with an empty image.
  • COPY ./hello /hello copies the hello binary from the host into the image at /hello.
  • CMD [“/hello”] sets the default command to run when the container is started, in this case, the hello binary.

Let’s assume that the hello binary is already compiled for your system. For more complex projects, you will need to include steps for compiling the necessary binaries.

Include Dependencies

If your project requires specific libraries, you’ll need to install them manually. Here’s an example of how you could build a basic Go binary from scratch, including its dependencies:

# Use a minimal Ubuntu image to build the Go binary
FROM ubuntu:24.04 AS builder

# Install Go compiler and dependencies
RUN apt-get update && apt-get install -y golang-go

# Copy source code
COPY ./hello.go /go/src/hello.go

# Build the binary
WORKDIR /go/src
RUN go build -o hello hello.go

# Start from scratch for the final image
FROM scratch

# Copy the binary from the builder stage
COPY --from=builder /go/src/hello /hello

# Set the command to run
CMD ["/hello"]

In this Dockerfile:

  • The first stage (builder) uses an Ubuntu base image to install Go and build the Go binary.
  • The second stage (FROM scratch) copies only the compiled binary to the final image, reducing the size significantly by leaving out unnecessary build dependencies.

Step 2: Building the Docker Image

Once you’ve defined your Dockerfile, it’s time to build the Docker image. In the terminal, navigate to your project directory and run the following command:

docker build -t my-docker-image .

This will execute the instructions in your Dockerfile and build the image.

Build Output

The build process will output each step of the Dockerfile. You should see output similar to this:

Sending build context to Docker daemon 3.072kB
Step 1/4 : FROM scratch
--->
Step 2/4 : COPY ./hello /hello
---> 7a5b6c3f567e
Step 3/4 : CMD ["/hello"]
---> Running in 02b0eaffcd67
---> 7a5b6c3f567e
Successfully built 7a5b6c3f567e
Successfully tagged my-docker-image:latest

Step 3: Running the Docker Image

After successfully building your Docker image, you can run it in a container:

docker run --rm my-docker-image

This will execute the hello binary within the container.


Step 4: Optimizing the Image

While the image we built from scratch is already quite minimal, there are further optimizations that you can make:

  • Use Multi-Stage Builds: As shown above, multi-stage builds allow you to use larger images for building and then copy only the necessary files to the final image. This dramatically reduces the final image size.
  • Minimize Dependencies: Ensure that your image only contains the essential libraries needed to run your application. Remove unnecessary tools and utilities.
  • Use Alpine: For many applications, using a base image like alpine (which is a small, security-oriented distribution) can reduce the image size significantly.

Example with Alpine

FROM alpine:latest

# Install necessary packages
RUN apk add --no-cache libc6-compat

# Copy your compiled binary
COPY ./hello /hello

# Run the binary
CMD ["/hello"]

Conclusion

Building a Docker image from scratch on Ubuntu 24.04 offers developers unparalleled control over their container environment. By stripping away unnecessary components and only including what’s essential, you can ensure that your Docker image is lean, secure, and efficient. Whether you’re optimizing for performance or security, building from scratch is an excellent approach to ensure your containerized applications are running as efficiently as possible.

Did you find this article useful? Your feedback is invaluable to us! Please feel free to share this post!


Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *