Dockerize Postgres: How to install Postgres with Docker
Tags: docker, postgres, tips, beginners
Published: 2022-05-01 19:25:59

Hi. I'm about to take a [CubeJS tutorial](https://react-dashboard.cube.dev) to create a nice dashboard and it seems that I need a Postgres DB.

So it's obvious that I have to use docker as usual and I want to share how to install Postgres in Docker because I wish someone had instructed me how to do it and save some time.

I will show you 2 ways to do it: the very simple way (single line command), and the most common and accepted way (using `docker-compose.yml` file).

One thing you have to know is that the container will be installed in the path you currently are, so I suggest you to create a directory for your project and create the container there.

## 1. Install it with the command line
Use the command below to install the postgres "image". You should customize it.

```sh
❯ docker run -p 127.0.0.1:5442:5432/tcp  -e POSTGRES_PASSWORD=mysuperpassword --name postgres_cube -d postgres:12
```

Once you ran that command, your terminal will show you something like the code below. This is the container ID which means it's up and running.
```
❯ 90a63c0aa9b597d63f644ecd178e549f161d12fa84d7a16a47545aaa92087124
```

To verify the container is up and running just run this command and you should see the postgres_cube is Up
```sh
❯ docker ps -a
CONTAINER ID   IMAGE         COMMAND                  CREATED         STATUS                     PORTS                    NAMES
17a8bca60f6d   postgres:12   "docker-entrypoint.s…"   3 minutes ago   Up 3 minutes               0.0.0.0:5442->5432/tcp   postgres_cube
```

### Command explanation
- The `docker run` command first creates a writeable container layer over the specified image, and then starts it. [Read more](https://docs.docker.com/engine/reference/commandline/run/#extended-description)

- The `-p 127.0.0.1:5442:5432/tcp` part specifies the `port` option to map the host (the real machine) port to the container port. Put attention on the 5442:5432. In this case I'm specifying that I want to use my machine 5442 port to connect with the container 5432 port (which is the postgres service port living in the container). I'll use the 5442 to connect my DB Client (DBeaver in my case). [Read more about published ports](https://docs.docker.com/config/containers/container-networking/#published-ports)

- The `-e POSTGRES_PASSWORD=mysuperpassword` part specifies the `environment` [variables](https://docs.docker.com/engine/reference/commandline/run/#options) that the image can detect. You should read the **postgres** [image docs](https://hub.docker.com/_/postgres) to see a complete list but the most common are:

1. - `POSTGRES_DB`
2. - `POSTGRES_USER`
3. - `POSTGRES_PASSWORD`

For postgres the `POSTGRES_PASSWORD` is required.

- In this case I'm specifying the required `POSTGRES_PASSWORD` to set a password. The user by default is "postgres" and the default database is "postgres" and other 2 template databases (as usual).

- `--name postgres_cube` specifies a custom name for the container. `postgres_cube` in my case. If you don't pass it, docker will use a random name but it's a good idea to give it a name to identify your container when using just a few of them

- The `-d` is an [option](https://docs.docker.com/engine/reference/commandline/run/#options) that allows us to detach our terminal session, which means that we don't need to keep a terminal open while the container is running.

- And finally, the `postgres:12` is the image (postgres) name and the tag (12). This is useful to install a specific version. If the tag is not specified, you will install the `latest` version.


## 2. Use a docker-compose file

First step is to create a `docker-compose.yml` in your project folder.

After that, copy, paste and save the content below:

```yml
version: "3"

networks:
  net-cube-tutorial:

volumes:
  db-cube-tutorial:

services:
  postgres:
    image: postgres:12
    hostname: postgres
    restart: unless-stopped
    container_name: postgres_cube
    environment:
      POSTGRES_DB: cube_tutorial
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: mysuperpassword
    ports:
      - 5442:5432
    volumes:
      - db-cube-tutorial:/var/lib/postgresql/data
    networks:
      - net-cube-tutorial

```

Finally run the command below. Docker will look for the `docker-compose.yml` file at the current path automatically, so you don't need to worry about.
```sh
❯ docker-compose up  -d
```

Your terminal will output something like the messages bellow, which means everything is fine.
```sh
Creating network "cubejs_net-cube-tutorial" with the default driver
Creating postgres_cube ... done
```

To verify the container is up and running just run this command and you should see the postgres_cube is Up
```sh
❯ docker ps -a
CONTAINER ID   IMAGE         COMMAND                  CREATED         STATUS                     PORTS                    NAMES
17a8bca60f6d   postgres:12   "docker-entrypoint.s…"   3 minutes ago   Up 3 minutes               0.0.0.0:5442->5432/tcp   postgres_cube
```

### File structure explanation
- What does the docker-compose.yml? Compose is a tool for defining and running multi-container Docker applications. With Compose, you use a YAML file to configure your application’s services. Then, with a single command, you create and start all the services from your configuration. [Read more about it](https://docs.docker.com/compose/)
- `version: "3"` specifies we will use the syntax version 3. [Read more about compose versions](https://docs.docker.com/compose/compose-file/compose-versioning/)
- `networks` lets you to define container internal networks. This is perfect when you need to connect or join multiple services in the same container. [Read more about networking](https://docs.docker.com/compose/networking/)
- `volumes` is the virtual storage for the container if we wan't to keep the data. It's useful to set a name for it because it will let us to reference the services to use it. [Read more about volumes](https://docs.docker.com/storage/volumes/)
- `services` is the list of the services will run on the container. Here we can specify a set of images and its configuration. In this case we only need the postgres service connected to the named container network, using the named storage and map the network port to our host port to be able to connect the DB with an external program outside the container.
1. `image` is the image name, in this case we need `postgres`. Also we can specify a tag, which is `12`. If you don't specify it, it will use the "latest" version.
2. `hostname` lets you assign a name to the service as host in the network. It's useful if we want to allow services to communicate between them. We can use the host name to reference the services at network level.
3. `restart` specifies the restart policy. `unless-stopped` always restart the container if it stops, Similar to `always`, except that when the container is stopped (manually or otherwise), it is not restarted even after Docker daemon restarts.
4. `container_name` allow to set a name for the container so we can easily find it. If you don't give it a name, a randomly generated name will be used.
5. `environment` is the section where we are able to specify [environment variables](https://docs.docker.com/compose/environment-variables/) supported by the image. For postgres there is a list you can explore in the [docs](https://hub.docker.com/_/postgres) but the most common are
1. - `POSTGRES_DB`
2. - `POSTGRES_USER`
3. - `POSTGRES_PASSWORD`

I recommend you to avoid exposing the variable values in the `docker-compose.yml`. Instead, you should specify them in a .env file and use it in the `docker-compose.yml` in this way

**.env file: **
```.env
DB_USER=mypostgresuser
DB_PASSWORD=mysuperpassword
```

**docker-compose.yml: **
```yml
   POSTGRES_USER: ${DB_USER}
   POSTGRES_PASSWORD: ${DB_PASSWORD}
```
- `ports` lets you map your actual machine (the host) to the container network ports. In this case I'm using the port 5442 of my machine to access to the container port 5432 to be able to connect DBeaver to the database directly.
- `volumes` here we map the container folder called "data" `(/var/lib/postgresql/data)` to be accesible from the root of our volume named `db-cube-tutorial` 
- `networks` allow us to connect our service to the specified container network.



## Bonus: Connect a DB client (DBeaver) to our DB

I'll use thee DBeaver program to easily use my DB, but you can use whatever you want.

1. Create a new connection with your own settings:

![New DBeaver Connection Setup](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l3mnza01fjx288mne2r1.png)

2. Test the connection
![Connection Test Result](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ppkubmsqdj3m251c4b8k.png)


 3. Enjoy
![DBeaver Connected to the DB](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0ggy6nkvmstryt3wvth5.png)