How to run Postgres in Docker and persist the data in a volume

@rkazak helped me figure out some problems I was having with persisting data from a Postgres Docker container. I wanted to save the notes, so I’m posting them here in the forum. :slight_smile:

Scenario: I uninstalled Postgres on my laptop and have been running it in Docker containers instead. Everything has been working fine, except that I’ve been losing the data when the container disappears. This post explains how to persist it in a Docker volume.

This page assumes that you know at least the very basics of Docker already.

Create a Docker volume

The following command creates a docker volume where your data will be stored. You can change the name of the volume, but be sure to also change it in the later commands.

$ docker volume create my_app_data

Then start a Docker container with a command like this. You can change the settings if you want, but this will work fine.

$ docker run --rm \
    -p 5432:5432 \
    -v my_app_data:/var/lib/postgresql/data \
    -e POSTGRES_USER=postgres \
    -e POSTGRES_PASSWORD=postgres \
    --name my-postgres-container postgres

This table explains what the flags mean:

Flag Meaning
--rm removes the container after it shuts down
-p binds the port in the Docker container to a port on your computer
-v tells Docker to persist your volume at the given location on your computer
-e sets an environment variable
--name sets the name of the Docker container

Finding the Postgres command line

To enter the Docker container, type this:

$ docker container exec -it my-postgres-container bash

Change to the postgres user (or whatever username you gave it).

# su -u postgres

Then launch psql.

$ psql

The next command will list the databases.

# \l

You can now connect your application to Postgres with the username and password that you set with the docker container command. When you stop the container (destroying it, because of the --rm flag), the data will still be there when you start a new temporary Docker container.

Additional tip: you can inspect the Docker volume with this command:

$ docker volume inspect my_app_data

If anyone is interested in Docker and Kubernetes, this course looks good, and I’m going to watch the videos soon. (Don’t pay more than $10-12 on Udemy. The courses go on sale regularly.)

I have a shell script that copies some default files into my projects. I added a docker-compose.yml file to it that is something like the example below.

version: '3'

# Be sure to change the db_data name throughout this file, if necessary.
volumes:
    db_data:

services:
    database:
        image: postgres
        # Create the necessary environment variables.
        # env_file:
        #     - .env/development/database
        ports:
            - 5432:5432
        environment:
            - POSTGRES_USER=postgres
            - POSTGRES_PASSWORD=postgres
            # POSTGRES_DB=myapp_development
        volumes:
            - db_data:/var/lib/postgresql/data

Instead of typing the long Docker command every time I want to start a database, I can now type this:

$ docker-compose up

Then to stop it and remove the containers (but not the named volumes that store the data):

$ docker-compose down

If I’ve made any mistakes in my explanation, or if there is a better way to do it, let me know. :slight_smile:

My main goal is to run Postgres without installing Postgres, and have the development data persisted between reboots, without clogging up my hard drive with a lot of old Docker containers.

Thanks again to @rkazak for the tips.

I use docker-compose in a similar way, but I like to create a dockerfile in a directory postgres so I can put init.sql in it to init whatever databases I want and also because I like alpine distributions (except for in the case of python where I prefer debian buster slim because pip doesn’t build for alpine so all python modules have to be compiled).

FROM postgres:11-alpine

COPY ./init.sql /docker-entrypoint-initdb.d/init.sql

init.sql:

CREATE DATABASE mydb;
-- also maybe set up extensions

Then

docker build -t registry.gitlab.com/myrepo/postgres .

Then I use the same docker-compose method but use registry.gitlab.com/myrepo/postgres for the image

version: "3"

services:
  postgres:
    image: registry.gitlab.com/myrepo/postgres:latest
1 Like

Thanks for that tip. I use Gitlab for my main site and didn’t know that they host Docker images.

1 Like