Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 29

Managing Services with Compose Profiles

Recommended resources

Hello! Welcome to the next lesson in our Docker series.

In our last session, we focused on managing application stability and performance by configuring CPU and memory limits for your services. Now that you know how to constrain individual services, let's explore how to manage which groups of services run in the first place.

Today's learning outcome is to use Compose profiles to selectively enable and disable groups of services. This is a powerful feature that helps you maintain a single, clean docker-compose.yml file while adapting it for different environments like development, testing, and production. It's a core concept for practicing Infrastructure as Code and keeping your configurations DRY (Don't Repeat Yourself).

1. What Are Docker Compose Profiles?

Imagine you have services that you only need for specific tasks:

  • A database admin tool like phpmyadmin or pgadmin for development.
  • An email-catching service like mailhog for local testing.
  • A separate service that runs your integration test suite.
  • A monitoring stack (prometheus, grafana) that you might not want running all the time.

In the past, you might have managed this by commenting out services in your docker-compose.yml, using multiple override files, or maintaining separate files altogether (docker-compose.dev.yml, docker-compose.test.yml). These approaches can lead to configuration drift and become difficult to manage.

Compose profiles solve this by letting you assign one or more "profile" names to a service. A service with a profile will only start if that profile is explicitly activated.

  • Services without a profiles key are considered part of a default group; they are enabled when you run docker compose up without any profile flags.
  • Services with a profiles key are disabled by default and only run when their profile is active.

Let's look at a visual example.

Docker Compose Profiles for Multi-Environment Service Management
This image shows a `docker-compose.yml` file with two services, `llm` and `llm-gpu`. The `llm` service is assigned to the `dev` and `test` profiles, while the `llm-gpu` service is in the `prod` profile. The commands below demonstrate how to start the services for each environment by specifying the appropriate profile.

This image neatly illustrates the core idea: one file, multiple configurations, activated on demand. This is perfect for a situation where you might use a standard CPU-based service for development and testing, but a more powerful GPU-based service in production.


2. Defining and Activating Profiles

The syntax is straightforward. You add a profiles key to any service, followed by a list of profile names.

services:
  web:
    image: nginx
    # No profiles key, so this is an "always-on" service.

  db:
    image: postgres
    profiles: ["prod"] # Only runs when the 'prod' profile is active.

  tests:
    image: my-app-tests
    profiles: ["test"] # Only runs when the 'test' profile is active.

You can activate profiles using the --profile flag on the command line.

# Start only the 'web' service (default)
docker compose up -d

# Start 'web' and 'db' services
docker compose --profile prod up -d

# Start 'web' and 'tests' services
docker compose --profile test up -d

Notice that the service without a profile (web) is included when you activate other profiles.

Stop Struggling with Docker Compose – Use These 10 Tricks Instead!

To see a quick demonstration of this in action, the following video clip from VirtualizationHowto offers a clear, concise walkthrough.

Watch from 11:00 to 12:46. The presenter shows how to add the profiles key to services in the YAML file and uses the docker compose --profile <name> up -d command to start specific service groups.


3. A Practical Use Case: Managing Dev, Prod, and Test Environments

The real power of profiles comes from managing entire application environments. As a DevOps Engineer, this is a pattern you'll find incredibly useful.

Let's walk through an example that sets up distinct prod, dev, and test environments using profiles.

Leveraging Docker Compose profiles

The article 'Leveraging Docker Compose profiles' provides an excellent, step-by-step guide on refactoring a single docker-compose.yml to handle different environments. It starts with a problem and progressively applies profiles as the solution.

Please read the entire article. Focus on: The challenges the author faces (e.g., different database hostnames, separating test data). How profiles: ["prod"], profiles: ["dev"], and profiles: ["test"] are added to services to isolate them. The commands used to launch each environment (docker compose --profile prod up, etc.). How this single-file approach simplifies configuration management.

This article clearly demonstrates how you can define entirely different services (like db for production and db-dev for development) and use profiles to select the correct one at runtime.

Test your understanding!

You have a docker-compose.yml with the services webapp, database, cache, and monitoring. Your goal is to:

  1. Have a core group of services (webapp, database, cache) that run for normal operation.
  2. Have the monitoring service run only on-demand, alongside the core services, but not by default.

How would you assign profiles to these services to achieve this? What commands would you run?

Show answer

A clean way to do this is to assign all services to explicit profiles.

services:
  webapp:
    profiles: ["core"]
    # ...
  database:
    profiles: ["core"]
    # ...
  cache:
    profiles: ["core"]
    # ...
  monitoring:
    profiles: ["monitor"]
    # ...

Commands:

  • To run the main application stack: docker compose --profile core up -d
  • To run the main stack and the monitoring service: docker compose --profile core --profile monitor up -d

This approach makes your service groups explicit and avoids potential confusion with the "always-on" behavior of services without profiles.


4. Advanced Concepts and Best Practices

For professional use, especially in automated CI/CD pipelines, there are a couple of advanced features you should know. The following video from Nick Janetakis, a well-regarded expert in the Docker community, covers these perfectly.

Docker Compose v2 and Profiles Are the Best Thing Ever

This video, 'Docker Compose v2 and Profiles Are the Best Thing Ever', covers two critical features that make profiles invaluable in a professional setting.

Please watch the following segments: Defining Profiles (05:23 - 06:39): A quick review of how profiles are added to services. Using COMPOSE_PROFILES (07:01 - 10:13): This is a key best practice. The video explains how to activate profiles using an environment variable instead of a command-line flag, which is ideal for CI/CD and declarative setups. It also shows a practical dev vs. prod scenario. depends_on Behavior (10:13 - 13:07): This is the most important part. It demonstrates how depends_on now works seamlessly even if the dependency is in an inactive profile. This is a massive improvement over older Docker Compose behavior and a common source of confusion.

Let's summarize the two key takeaways from the video:

  1. Use the COMPOSE_PROFILES Environment Variable: Instead of --profile flags, you can define an environment variable, typically in your .env file:

    # In .env file
    COMPOSE_PROFILES=core,monitor
    

    Now, a simple docker compose up -d will activate the core and monitor profiles. This is the preferred method for CI/CD pipelines and production deployments because it keeps the configuration self-contained and the command simple.

  2. depends_on Works Intelligently: In modern Docker Compose, if service-A has depends_on: service-B, but service-B is not in an active profile, Compose simply ignores the dependency. This is a huge benefit. For example, your webapp can depend_on a database service for local development, but in production (where you use a managed cloud database), you can simply run with a profile that excludes the database service, and your application will start without errors.


Conclusion

You've now learned how to use Docker Compose profiles to create flexible, manageable, and environment-aware application stacks. This technique is fundamental to professional DevOps workflows.

Key Takeaways:

  • Profiles allow you to label services to be enabled or disabled in groups.
  • They are a superior alternative to multiple Compose files or commented-out services for managing different environments (dev, test, prod).
  • Activate profiles with the docker compose --profile <name> flag or, preferably, by setting the COMPOSE_PROFILES environment variable.
  • Modern Docker Compose handles depends_on intelligently, ignoring dependencies on services in inactive profiles, which simplifies using external services (like managed databases) in production.

In our next lesson, we will tackle another critical aspect of multi-service applications: implementing a strategy for managing secrets in Docker Compose. We'll look at how to handle sensitive data like API keys and passwords securely, comparing different methods like environment files and Docker's built-in secrets management.