1. Introduction

Validating the Docker Compose YAML file ensures that configurations are correct, prevents runtime errors, and saves time during development and deployment.

In this tutorial, we’ll explore how to validate a Docker Compose file. First, we’ll review the YAML format. Then, we’ll understand how Docker Compose employs it. Finally, we’ll explore manual and automated validation techniques that help maintain error-free configurations.

2. Understanding a YAML File

YAML (YAML Ain’t Markup Language) is a human-readable data serialization standard often used for configuration files. Unlike other markup languages such as XML or JSON, YAML aims to be easy to read and write. Its syntax is sensitive to indentation and formatting.

YAML files use indentation to indicate structure, similar to Python. However, tab characters aren’t allowed; we should only use spaces to ensure portability across different systems. At its core, YAML uses key-value pairs to represent data. A colon followed by a space separates keys and values.

Also, YAML supports various data types, including strings, integers, floats, booleans, null, dates, and times. The # symbol denotes comments. The parser ignores anything following this symbol on a line.

Finally, scalars are the simplest form of data in YAML, representing individual values such as strings, numbers, or booleans. We can write them in plain style (without quotes), single quotes, or double quotes.

3. Docker Compose and YAML File

A Docker Compose YAML file is a configuration file used to define and manage multi-container Docker applications. A typical compose.yaml file includes several sections that outline various aspects of the application’s architecture.

The services section defines the containers that make up the application. Each service corresponds to a container, and we can configure it with parameters such as images, ports, volumes, and environment variables. Examples of services include apps, web servers, and databases.

For a better understanding, let’s study an example available on the Official Docker GitHub:

services:
   redis: 
     image: redislabs/redismod
     ports:
       - '6379:6379' 
   web:
     build: .
     ports:
       - "8000:8000"
     volumes:
       - .:/code
     depends_on:
       - redis

This file sets up a web service to run with a Redis database. The Redis service is constructed using a ready-made image from Docker Hub, whereas the web service is created from a Dockerfile in the current directory (.). Additionally, the web service requires an active Redis service to operate correctly.

4. Validation Techniques

Let’s explore some techniques to verify the correctness of our compose file.

4.1. Manual Techniques

Docker Compose provides a built-in command to check the syntax and validity of a YAML file. The docker-compose config command parses the file and prints the configuration, highlighting any errors or issues.

To illustrate, let’s test the same YAML file as above:

$ docker compose config
name: baeldung
services:
  redis:
    image: redislabs/redismod
    networks:
      default: null
    ports:
      - mode: ingress
        target: 6379
        published: "6379"
        protocol: tcp
  web:
    build:
      context: /home/user/baeldung
      dockerfile: Dockerfile
    depends_on:
      redis:
        condition: service_started
        required: true
    networks:
      default: null
    ports:
      - mode: ingress
        target: 8000
        published: "8000"
        protocol: tcp
    volumes:
      - type: bind
        source: /home/user/baeldung
        target: /code
        bind:
          create_host_path: true
networks:
  default:
    name: baeldung_default

This output shows the effective configuration after Docker Compose has processed the YAML file. We observed that the configuration now includes additional specifications, such as communication protocols and network settings. These settings are essential for the services to function correctly. However, since we didn’t specify them, Docker uses the default values.

This command also replaces environment variables and resolves any variables defined in the file, an example being directory paths displayed in full where previously they were just a dot.

In this example, the command looked for the compose.yaml file in the current directory. However, we can designate the file with the -f flag (as in the following example) or by setting the COMPOSE_FILE environment variable to reference the file.

Now, let’s introduce an error into the file to analyze the output of the config command. To do this, we’ll delete the line indicating the image that Redis will use (image: redislabs/redismod):

$ docker compose -f compose.yaml config
service "redis" has neither an image nor a build context specified: invalid compose project

Docker Compose requires each service to specify either an image to pull from a Docker registry or a build context to build the image from a Dockerfile. In this case, the error indicates that the Redis service is defined but doesn’t have an image or a build context specified precisely because we’ve removed this specification.

As the output of the command can be long, a brief message is more interesting to receive, such as OK or an error. In this case, we can use the –quiet flag together with printf:

$ docker compose config --quiet && printf "OK\n" || printf "ERROR\n"
OK

$ docker compose config --quiet && printf "OK\n" || printf "ERROR\n"
service "redis" has neither an image nor a build context specified: invalid compose project
ERROR

4.2. Automated Techniques

We can validate our file using automation techniques. The first of these is the use of linting tools in the development environment. Installing a YAML linting extension provides real-time validation and highlights errors as we edit the compose file.

Several Integrated Development Environments and text editors, such as Visual Studio Code (VS Code), support YAML linting through extensions. To install it in VS Code, we must search for Red Hat’s YAML extension from the VS Code marketplace (ID: redhat.vscode-yaml). Once installed, when we access any YAML file, the extension will automatically highlight syntax errors and provide suggested fixes.

The second automation technique involves integrating YAML validation into the Continuous Integration/Continuous Deployment (CI/CD) pipeline. This ensures that the system automatically checks for errors in any changes to the YAML file. Many CI/CD platforms, such as Jenkins, GitHub Actions, and GitLab CI, support executing Docker Compose commands.

For example, let’s produce a workflow to validate a Docker Compose YAML file. So, let’s create a .github/workflows/validate-docker-compose.yaml file with the following content:

name: Validate Docker Compose

on: [push, pull_request]

jobs:
  validate:
    runs-on: ubuntu-latest

    steps:
    - name: Checkout repository
      uses: actions/checkout@v2

    - name: Set up Docker
      uses: docker/setup-buildx-action@v1

    - name: Validate docker-compose.yaml
      run: docker-compose config

This workflow triggers whenever we send code or make a pull request. It also fetches the repository code to access the compose.yaml file. Docker is configured in the runner environment to enable the execution of Docker commands. Finally, it runs docker-compose config to validate the compose.yaml file.

5. Conclusion

In this tutorial, we learned how to validate a compose.yaml file. The simplest way is to use the docker compose config command, which renders the file and reports any errors. We can also integrate this command into CI/CD to automate the check. Finally, we can use lint tools within the IDE to highlight problems in real-time.