I’ve recently been doing a fair bit of investigation into docker. My latest post was investigating the idea of setting up a sidercar pattern just using docker compose. In this post, I’m exploring the concept of launching an interactive shell using docker compose.
Why is this so hard?
Docker, and by extension, docker compose, are not really designed to run interactive tasks: they fare best left with ephemeral background tasks. However, in my particular use case, I needed to run a console app which accepted a keyboard input.
The system will let you create the console app as far as actually running, but once you issue a docker compose up, you’ll get something like the following error:
Unhandled exception. System.InvalidOperationException: Cannot read keys when either application does not have a console or when console input has been redirected. Try Console.Read.
How to run Interactively
In fact, there’s a few issues here, the first (as pointed out by this post) is that you need to tell docker compose to pass through the interactive mode to the container:
my-app:
build: .\\My.App
stdin\_open: true
tty: true
depends\_on:
- "some-api"
stdin_open and tty translate to passing -it to the container. However, that doesn’t solve the problem entirely; if you issue a docker compose up you’ll still see the same issue.
The reason here is that there are (or can be) multiple containers, and so there’s no sensible way of knowing how to deal with that. In fact the solution is to simply tell docker compose which one to run; for example:
docker compose build --no-cache
docker compose run my-app
In the example above, that will also run the api some-api because we’ve declared a dependency. Note that build is optional; although while developing and debugging, I find it useful to execute both.