Example docker-compose setup for postgres, adminer and mailcatcher

Learn how to create a docker-compose.yml file for one of the most common web stacks. Postgres + Adminer + Mailcatcher

The stack

In this example I will show you how to setup docker containers using docker-compose to work in this commonly used web stack.

Typically, a web application needs a database whether thats MySQL/Postgres or a NoSQL database and usually you need an interface to check explore your databases and also a service to catch mails. Setting all these in your local machine can be a headache. Thankfully with the help of docker we can setup a standalone stack for each of our projects and start/stop it on demand.

This is also pretty handy when working on multiple projects and you may need to switch between them every now and then.

Set up a database service

First things first lets create our docker-compose.yml in the root of our project and lets add the database service we are going to use in our project. In this case we would like to use postgres.

version: "3"

services:
  db:
    image: postgres:14.3-alpine
    ports:
      - 5432:5432
    environment:
      - POSTGRES_HOST=localhost
      - POSTGRES_PORT=5432
      - POSTGRES_USER=root
      - POSTGRES_DB=myproject
      - POSTGRES_PASSWORD=root

Our database is now setup however there is one issue. Our database data is not persistent. That means all the data in our database will be gone once we stop the running container.

To solve this we need to tell docker to use a volume to persist the container's /var/lib/postgresql/data path which contains all of the database data.

version: '3'

services:
  db:
    image: postgres:14.3-alpine
    ports:
      - 5432:5432
    volumes:
      - db-datavolume:/var/lib/postgresql/data
    environment:
      - POSTGRES_HOST=localhost
      - POSTGRES_PORT=5432
      - POSTGRES_USER=root
      - POSTGRES_DB=myproject
      - POSTGRES_PASSWORD=root

volumes:
  db-datavolume:

Please note the : after db-datavolume: This is not a typo!

Add Adminer service

There are many options out there for database explorers. I personally like to use Adminer because it's very simple and fast. Let's add the Adminer service to our docker-compose.yml

services:
  #---------
  adminer:
    image: adminer:latest
    ports:
      - 8080:8080

That will make adminer available on port 8080. You can login to your database using the environment settings we passed to db service.

IMPORTANT: One thing to keep in mind here is that these services work under docker's networking and not on your local machine networking. Sometimes localhost may not be available in your app is running outside docker. However, docker services can interact with each other by using the service name as the address to that service.

For example, since both adminer and db services are on the same docker network, we can use HOST=db when trying to connect to our database from adminer. So to connect to the db service using adminer we can use the following settings.

HOST=db
PORT=5432
DATABASE=myproject
USERNAME=root
PASSWORD=root

Add the MailCatcher service

Finally, let's add the MailCatcher service to our docker-compose.yml. MailCatcher will catch all the mails sent through smtp://127.0.0.1:1025. This makes it very easy to work with and debug your email templates if your application sends any.

services:
  #---------
  mailcatcher:
    image: schickling/mailcatcher:latest
    ports:
      - 1080:1080
      - 1025:1025

This configuration will make the mailcatcher interface available on port 1080 and we can use port 1025 in our application's SMTP settings so that every mail send will be caught by the mailcatcher service.

As explained before, if smtp://127.0.0.1:1025 doesn't work for you try using the service name as the host like so smtp://mailcatcher:1025

Summary

We have now have a nice virtual environment to work with for our project! We can now start this stack by running docker-compose up -d.

Here is the final result

version: "3"

services:
  db:
    image: postgres:14.3-alpine
    ports:
      - 5432:5432
    volumes:
      - db-datavolume:/var/lib/postgresql/data
    environment:
      - POSTGRES_HOST=localhost
      - POSTGRES_PORT=5432
      - POSTGRES_USER=root
      - POSTGRES_DB=myproject
      - POSTGRES_PASSWORD=root

  adminer:
    image: adminer:latest
    ports:
      - 8080:8080

  mailcatcher:
    image: schickling/mailcatcher:latest
    ports:
      - 1080:1080
      - 1025:1025

volumes:
  db-datavolume:

References

More posts in DevOps