DevOps 3 min read

[Docker] Using Utility Containers with Docker Compose

In a modern development workflow, utility containers are a powerful way to run one-off commands or background tasks without polluting your main application containers. With Docker Compose, it's even easier to define and use these utility containers as part of your development and deployment process.

🔧 What Are Utility Containers?

Utility containers are lightweight, short-lived containers used for tasks like:

  • Running database migrations

  • Seeding test data

  • Performing backups

  • Running CLIs (e.g., AWS CLI, Rails Console, etc.)

  • Running custom scripts

Unlike your app or service containers, utility containers are not long-running processes. They spin up, do their job, and vanish.

🧱 Defining Utility Containers in docker-compose.yml

Here’s how you can define a utility container in your docker-compose.yml file:

version: "3.8"

services:
  app:
    build: .
    volumes:
      - .:/app
    depends_on:
      - db
    networks:
      - app-net

  db:
    image: postgres:13
    environment:
      POSTGRES_USER: myuser
      POSTGRES_PASSWORD: secret
      POSTGRES_DB: mydb
    networks:
      - app-net

  migrate:
    build: .
    command: bundle exec rake db:migrate
    volumes:
      - .:/app
    depends_on:
      - db
    networks:
      - app-net

networks:
  app-net:
 

This defines a migrate container that shares the same environment as the main app, but only runs the migration command when called.

🚀 Running Utility Containers

You can run the migration container manually using:

docker-compose run --rm migrate

This command:

  • Starts a new container from the migrate service

  • Executes the bundle exec rake db:migrate command

  • Deletes the container after it's done (because of --rm)

⚙️ Running Custom Commands

You don’t need to define a separate service for every command. You can override the command directly when running:

docker-compose run --rm app bundle exec rails console

Or for Node.js:

docker-compose run --rm app npm run seed

🤖 Scripting Utility Commands

To simplify repeated use, create helper scripts like:

scripts/migrate.sh:

#!/bin/bash
docker-compose run --rm migrate

Make it executable:

chmod +x scripts/migrate.sh

Now, just run:

./scripts/migrate.sh

🔄 Utility Containers vs docker-compose exec

Purpose docker-compose run docker-compose exec
Temporary one-off container ✅ Yes ❌ No
Access running container shell ❌ No ✅ Yes
Suitable for CI/CD ✅ Yes ❌ No
Runs with clean environment ✅ Fresh container each time ❌ Uses stateful running container

✅ When to Use Utility Containers

Use them when:

  • You need isolated, repeatable tasks

  • You want to script command-line operations

  • You’re automating processes in CI/CD

  • You want to avoid exec-ing into live containers

🧠 Final Thoughts

Utility containers make your Docker Compose workflow cleaner, safer, and more modular. Instead of SSH-ing into containers or running complex commands manually, define a clear structure and let Docker do the heavy lifting.

They are especially useful in projects with multiple developers, staging environments, or automation pipelines, helping you keep your services clean and operations reproducible.

Related Posts