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.