Running Multiple Versions of Postgres with Docker Compose for Local Development

Rit Li ·

Say we have Project X and Project Y that require Postgres 9 and Postgres 10 respectively. These projects aren’t using Docker to manage their Postgres dependency so it is up to each developer to manage this themselves. How do we get different versions of Postgres running simultaneously on our workstation without making any modifications to these projects? One easy way is to use Docker Compose.

Why not Homebrew? With Homebrew, installing multiple versions of Postgres is easy, but running them simultaneously is cumbersome. With Docker Compose, both installing and running are easy. Note that we’re not “dockerizing” the applications themselves; instead, we’re using Docker Compose as an alternative to Homebrew to fetch and run Postgres.

Initialize and Reset the Postgres Services

We’ll use Docker Compose to manage two instances of Postgres (i.e. two “services”). The beauty of using Docker is the fact that we can start these services with a clean slate and clean the slate when we’re done.

With this docker-compose.yml file:

version: '3'
services:
  pg9:
    image: postgres:9.6.10
    ports:
      - 5961:5432
    environment:
      POSTGRES_DB: project-x

  pg10:
    image: postgres:10.5
    ports:
      - 5105:5432
    environment:
      POSTGRES_DB: project-y

We can start Postgres in a pristine state with just one simple command:

 docker-compose up -d

And clean the slate with this:

$ docker-compose down

With docker-compose down, all the records in Postgres are completely wiped out. We should only run this command when we’re done with the project. The Postgres image is by default configured to use a Docker volume so that the data persists between docker-compose start|stop|restart subcommand. docker-compose down will remove that volume, hence our Postgres data will be deleted.

Note that we have mapped port 5961 of our workstation to port 5432 (default Postgres’s port) of the container running the pg9 service. Therefore, the connection URI for pg9 should be postgres://localhost:5961/project-x. Likewise, the connection URI for pg10 should be postgres://localhost:5105/project-y.

Service Lifecycle

docker-compose up will start the process in the foreground. It stays alive until we kill it with ctrl-c. We can start it in the background with the -d flag. If we reboot our workstation, we have to start the process again. However, we don’t have to “initialize” the environment with docker-compose up. We can, instead, restart the service with docker-compose start.

Starting and Stopping the Server

After running docker-compose up, we can verify pg9 and pg10 services are running with this command:

$ docker-compose ps

The output should resemble this:

         Name                       Command              State           Ports         
---------------------------------------------------------------------------------------
postgres-docker_pg10_1   docker-entrypoint.sh postgres   Up      0.0.0.0:5105->5432/tcp
postgres-docker_pg9_1    docker-entrypoint.sh postgres   Up      0.0.0.0:5961->5432/tcp

If we want to start, stop or restart pg9, we can do so with:

$ docker-compose start|stop|restart pg9

Using psql for debugging

Given pg9 service is running, we can use this command to drop into psql:

$ docker-compose exec -u postgres pg9 bash

Once we’re at the bash shell prompt, we can use psql much like we normally would:

postgres@e2d166ef7c37:/$ psql project-x

We can also use createdb, dropdb, createuser, and other Postgres handy utilities.

This is just a taste of Docker as a power tool for development. And while Docker and its ecosystem are vast, we don’t have to navigate them all at once. For example, we don’t have to “dockerize” our apps to get the benefits. We can take it one baby step at a time.


Interested in more software development tips and insights? Visit the development section on our blog!

Now hiring developers, designers, and product managers.
Apply now: www.carbonfive.com/careers
Rit Li
Rit Li

time-traveling healer