Baeldung Pro – Ops – NPI EA (cat = Baeldung on Ops)
announcement - icon

Learn through the super-clean Baeldung Pro experience:

>> Membership and Baeldung Pro.

No ads, dark-mode and 6 months free of IntelliJ Idea Ultimate to start with.

1. Overview

Docker Compose plays an important part in running multi-container Docker applications. One feature that Docker Compose often implements when building Docker images is context.

In this tutorial, we’ll discuss the meaning of context, its importance, and how it works.

2. Understanding Docker Compose Context

In Docker, context represents the directory where Docker searches for all the files needed to build an image for a service. Such files include the Dockerfile, application code, configuration files, and dependencies.

To define the context, we need to use the context field in the docker-compose.yml file to specify the context path. Docker considers the context path as its working directory for finding the necessary files. This path can be a relative path, an absolute path, or perhaps a URL to a remote Git repository:

version: "3"

services:
  app:
    build:
      context: .
    ports:
      - "8000:8000"
    volumes:
      - ./app:/app
    command: >
      sh -c "python manage.py runserver 0.0.0.0:8000"

Above, context: . instructs Docker to use the current directory, the location of the docker-compose.yml file, as the build context for the app service. Docker looks for the Dockerfile and other files in this directory to build the service’s image.

3. Importance of Context

Context defines what the Docker daemon receives during the build process. To clarify, Docker packages the entire build context and sends it to the daemon when it’s building an image:

my_django_project
├── app
│   ├── Dockerfile
│   ├── manage.py
│   └── myapp
├── docker-compose.yml
└── .git

In this example, if context: . is defined in docker-compose.yml, then Docker packages everything in the my_django_project directory. It packages unnecessary files like the .git directory. However, if we only need the files in the app/ directory, we can modify the context path in the context field:

version: "3"

services:
  app:
    build:
      context: ./app
    ports:
      - "8000:8000"
    volumes:
      - ./app:/app
    command: >
      sh -c "python manage.py runserver 0.0.0.0:8000"

In this modification, Docker excludes the unnecessary files and only includes the app directory, which makes the build process faster.

Meanwhile, Dockerfile helps to outline the steps for building an image — for instance, how to install dependencies, set up the runtime environment, and copy application files. The Docker daemon uses context as the directory to search for the Dockerfile when building an image.

If the Dockerfile exists in the build context, Docker automatically finds the file and uses it. However, if the Dockerfile doesn’t exist in the build context, we need to specify its location using the dockerfile field:

services:
  app:
    build:
      context: .
      dockerfile: ./app/Dockerfile
    ports:
      - "8000:8000"
...

Above, Docker uses the current directory as the context but looks for the Dockerfile in the app/ directory. So, the dockerfile field enables us to specify the location of the Dockerfile outside of the main context path.

4. Best Practices When Working With Context

There are a few practices we can implement to manage build context.

First, we can maintain a small context by setting the context to the smallest possible directory. This improves the speed of the build process and reduces the risk of sending unnecessary files to the Docker daemon.

Next, we can use a .dockerignore file to ignore files that don’t need to be included in the Docker image, for instance, .git, *.log files, or unnecessary large files:

# Version control directory
.git

# Logs files
*.log

# Environment files
.env
.env.local

# Python cache files and compiled bytecode
__pycache__/
*.pyc

# Docker Compose file
docker-compose.yml

Above, the .dockerignore file excludes the .git directory, log files, environment files, Python cache files, and the docker-compose.yml file from the context.

Further, we can monitor the build output. When we run docker-compose build, we need to review the output to note the files being sent to the Docker daemon.

5. Conclusion

In this article, we dove into how context is fundamental for determining which files are included in the build process for services.

Context management enables us to create Docker images that are efficient and manageable. For instance, we can use tools such as .dockerignore or keep the context small. Thus, we can streamline the build process, even for large projects.