xx execs commands for you in your Docker Compose projects

This is a short blog post about a short Perl script that I wrote to execute commands in running Docker Compose containers.

Coincidentally, the city of Amsterdam celebrated its 750th anniversary today.
Flag of Amsterdam, with the last white cross replaced by an underscore

Coincidentally, the city of Amsterdam celebrated its 750th anniversary today.

I usually work on problems that are anything but straightforward – such as fighting disinformation – which typically require solutions that are anything but straightforward. Every so often though, you encounter a tiny problem that only needs a tiny fix.

Executing commands in a running Docker container is one such tiny problem. It’s not hard. If you use Docker Compose you can just type docker compose exec, give it a service name and a command, and you’re done. If that feels like too much typing, you can make an alias or use your shell history.

That’s still too much typing for me. It doesn’t help that I have to deal with multiple microservice macaroni architectures and often have to switch between projects that use different service names. In one project the main service might be called backend, in another app, and in a third it might be something like newsspeak. I got tired of typing the right service names all the time, so I wrote a small utility called xx.

xx tries to find the container you’re most likely to want to run commands in. This is usually a container that is built using from source, i.e. defined with a build: in the Compose file rather than pulled as an image. xx will pass whatever command you have provided on to that container.

For example, let’s take a look at the following Compose file.

services:
million-dollar-app:
build: .
depends_on:
- database
volumes:
- .:/app
database:
image: mysql:latest

This file contains two services: million-dollar-app, which we are developing, and a database. If million-dollar-app is a Node.js app, normally you would run something like this:

docker compose exec million-dollar-app npm i

With xx, you simply run:

xx npm i

Often I just want a shell. By default, if you don’t provide any arguments to xx, it will try to run bash. So to open a shell in the million-dollar-app container all you have to do is run:

xx

But what if your Compose file has more than one service that you build yourself?

services:
frontend:
build:
context: frontend
volumes:
- ./frontend:/app
backend:
build:
context: backend
depends_on:
- database
volumes:
- ./backend:/app
database:
image: mysql:latest

In this case xx will prompt you to choose the container that should execute the command:

$ xx composer install
Where should 'composer install' be executed?
1. frontend
2. backend
Enter the number of your choice:

If you’d like to try it out yourself, you can! I have published the entire 200-line script as a Gist. There’s no installer. Download it, make it executable and put it somewhere in your PATH like /usr/local/bin, and you’re good to go.