Speed up local docker-compose setup

Mark Lee
Raidboss.io blog
Published in
3 min readApr 19, 2020

Today I will make a brief overview of a simple technique to make your local development using docker-compose a bit simpler and faster.

To remind, as a base for our back-end development we are using Golang and docker-compose template from my previous article. One of the crucial ideas for this setup is to separate source code from the Docker image to reuse cache and start up the environment as fast as possible during the development stage.

Most probably you are using a grasp of docker-compose services to develop even a single microservice application. The most frequent case I have ever seen is the database companion service. It’s a local database needed to run the main application, perform testing. Other examples are database UI tool, mail catcher (e.g. Mailhog), cache storage like Memcache. In other words, all dependencies you have as separate servers/services on production are suitable to be included in your local docker-compose.

Let’s take one example:

This is a quite clean and simple setup to work with.

Now you have two options on how to run it: use a docker-compose up or docker-compose up -d command to run the environment.

First, let’s see what is the development workflow for the daemonized ( -d) case. If we run all services from the compose file in this way, what we need to do to apply changes in our source code? Right, we can run docker-compose up -d again. And it will not work because Docker will reuse the running containers as the underlying image is the same (sources are outside the image). So we need either to do docker-compose down or to use --force-recreate flag. Both options lead to stopping all services within that compose file and then you start up all of them again*. I believe you repeating the cycle of changing your code and running a new version quite often during regular development. And during each iteration, you forced to stop and start containers which are definitely not changing (e.g. database).

With docker-compose up the approach, you are falling into the same issue. When you boring the handing service, all of them are stopped. But what I’m personally like in this hanging in terminal style — it’s always easy to see the output of your app.

And here I came to the conclusion — well I want to have a mix of those approaches. For companion services, it’s smart to use -d and do a start-up once. While for the main application I want to use docker-compose up to be able to see the logs, quickly stop and run it again.

The solution is straightforward. We need to have two docker-compose files. One for daemonized services and another, for on-demand.

So we end up with something like:

docker-compose -f docker-compose-services.yml up -d
docker-compose -f docker-compose-app.yml up

For the simplicity, wrap both commands in a Makefile task:

app_up:
docker-compose -f docker-compose-services.yml up -d
docker-compose -f docker-compose-app.yml up

Finally, our development flow looks like:

  • make app_up (services are running in the background and app hanging in your terminal)
  • making changes to your source code
  • terminate command in the terminal and make app_up again

Now a fresh version of code is running without extra time for Docker routines.

Here is a sample repo demonstrating all the steps together.

--

--