Terminus
Launch PostgreSQL Using Docker Compose

Launch PostgreSQL Using Docker Compose

The short answer

To set up a PostgreSQL database using Docker Compose, you can start by creating a [.inline-code]compose.yaml[.inline-code] file with the following content:

 version: '3'

services:
  db:
    image: postgres
    environment:
      POSTGRES_USER: <username>
      POSTGRES_PASSWORD: <password>
      POSTGRES_DB: <database>
    volumes:
      - ./data:/var/lib/postgresql/data
    ports:
      - 5432:5432

Where:

  • [.inline-code]POSTGRES_USER[.inline-code] is used to create the specified user with superuser power.
  • [.inline-code]POSTGRES_PASSWORD[.inline-code] is used to define the password of the user identified by the [.inline-code]POSTGRES_USER[.inline-code] variable.
  • [.inline-code]POSTGRES_DB[.inline-code] is used to define a different name for the default database that is created when the image is first started. If it is not specified, then the value of [.inline-code]POSTGRES_USER[.inline-code] will be used.

Then run the following [.inline-code]docker-compose[.inline-code] command to start the PostgreSQL container:

 $ docker compose -f compose.yaml up -d

Where:

  • The [.inline-code]-f[.inline-code] flag is used to specify the file path of the Compose file.
  • The [.inline-code]-d[.inline-code] flag is used to launch the services in the background (i.e. detached mode).

Note that when running this command, the database data will be stored in the [.inline-code]data[.inline-code] directory relative to the [.inline-code]compose.yaml[.inline-code] file.

You can learn more about the [.inline-code]docker compose up[.inline-code] by visiting the official Docker documentation page.

[#recall-syntax-with-ai]Easily retrieve this syntax using Warp AI[#recall-syntax-with-ai]

If you’re using Warp as your terminal, you can easily retrieve this syntax using the Warp AI feature:

Entering [.inline-code]docker compose postgres[.inline-code] in the AI question input will prompt a human-readable step by step guide including code snippets.

[#persisting-data-with-volumes]Persisting database data with named volumes [#persisting-data-with-volumes]

To persist the database data in a volume instead of a bind mount, you can create a new named volume using the top-level [.inline-code]volumes[.inline-code] field:

 version: '3'

services:
  db:
    image: postgres
    environment:
      POSTGRES_USER: <username>
      POSTGRES_PASSWORD: <password>
      POSTGRES_DB: <database>
    volumes:
      - pgdata:/var/lib/postgresql/data
    ports:
      - 5432:5432

volumes:
  pgdata:

In this example, the database data will be stored in a volume named [.inline-code]pgdata[.inline-code], which can be managed using the available [.inline-code]docker volume[.inline-code] commands.

You can learn more about using volumes in Compose by checking out the official documentation page.

You can also read our other article on how to mount files in Compose.

[#initializing-with-scripts]Executing scripts and SQL files on startup [#initializing-with-scripts]

To initialize the database with a shell script or a file containing SQL instructions, for example to insert dummy data or migrate an existing database, you can place your [.inline-code]*.sh[.inline-code] and [.inline-code]*.sql[.inline-code] files within a directory on your local machine and mount it to the [.inline-code]/docker-entrypoint-initdb.d[.inline-code] directory using the [.inline-code]volumes[.inline-code] field as follows:

 version: '3'

services:
  db:
    image: postgres
    environment:
      POSTGRES_USER: <username>
      POSTGRES_PASSWORD: <password>
      POSTGRES_DB: <database>
    volumes:
      - ./data:/var/lib/postgresql/data
      - ./init-scripts:/docker-entrypoint-initdb.d
    ports:
      - 5432:5432

Once the user and the database are created, [.inline-code]postgres[.inline-code] will run any of the files found in the [.inline-code]/docker-entrypoint-initdb.d[.inline-code] directory before starting the service.

[#connecting-an-application]Connecting Postgres to an application [#connecting-an-application]

To connect a PostgreSQL database to an application you can use the [.inline-code]depends_on[.inline-code] field, which is used to express dependency between services.

 version: '3'

services:
  db:
    image: postgres
    environment:
      POSTGRES_USER: <username>
      POSTGRES_PASSWORD: <password>
      POSTGRES_DB: <database>
    volumes:
      - ./data:/var/lib/postgresql/data
    ports:
      - 5432:5432

  app:
    image: node
    depends_on:
      - db

The use of this field causes Compose to start services in dependency order, which means that, in this example, the [.inline-code]db[.inline-code] service will be started first, then the [.inline-code]app[.inline-code] service.

[#performing-a-health-check]Controlling the application's startup with a health check[#performing-a-health-check]

By default, Compose doesn't wait for a service to be ready to start the next one, only until it's running. This can cause issues if, for example, you have an application that needs to wait for the database to be up and running before being able to handle incoming connections.

To solve this issue you can use the [.inline-code]condition[.inline-code] field to define a startup condition based on the status of the service it depends on.

 version: '3'

services:
  db:
    image: postgres
    environment:
      POSTGRES_USER: <username>
      POSTGRES_PASSWORD: <password>
      POSTGRES_DB: <database>
    volumes:
      - ./data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready"]
      interval: 10s
      timeout: 5s
      retries: 5
    ports:
      - 5432:5432

  app:
    image: node
    depends_on:
      db:
        condition: service_healthy

In the above example, we are telling Compose to wait for the [.inline-code]db[.inline-code] service to be up and running before launching the [.inline-code]app[.inline-code] service by defining a startup [.inline-code]condition[.inline-code] whose value is set to [.inline-code]service_healthy[.inline-code].

The [.inline-code]service_healthy[.inline-code] represents a boolean value returned by the execution of the [.inline-code]pg_isready[.inline-code] command declared in the [.inline-code]healthcheck[.inline-code] field of the [.inline-code]db[.inline-code] service, which is a utility for checking the connection status of a PostgreSQL database server. The exit status specifies the result of the connection check.

This way, Compose will wait for the [.inline-code]pg_isready[.inline-code] utility to return [.inline-code]true[.inline-code] before launching the [.inline-code]app[.inline-code] service.

[#connecting-to-pgadmin]Connecting pgAdmin to Postgres[#connecting-to-pgadmin]

To manage your database using the pgAdmin interface, you can use the following [.inline-code]compose.yaml[.inline-code] file:

 version: '3'

services:
  db:
    image: postgres
    environment:
      POSTGRES_USER: <username>
      POSTGRES_PASSWORD: <password>
      POSTGRES_DB: <database>
    volumes:
      - ./data:/var/lib/postgresql/data
    ports:
      - 5432:5432

  pgadmin:
    image: dpage/pgadmin4
    environment:
      PGADMIN_DEFAULT_EMAIL: <email>
      PGADMIN_DEFAULT_PASSWORD: <password>
    ports:
      - 8080:80

Where:

  • [.inline-code]PGADMIN_DEFAULT_EMAIL[.inline-code] is used to create a new user account on pgAdmin.
  • [.inline-code]PGADMIN_DEFAULT_PASSWORD[.inline-code] is used as a password for the user account identified by [.inline-code]PGADMIN_DEFAULT_EMAIL[.inline-code].

You can then start both PostgreSQL and pgAdmin using the following command:

 $ docker compose -f compose.yaml up -d

And connect to the pgAdmin interface by navigating to the following address [.inline-code]127.0.0.1:8080[.inline-code] in your web browser.

[#using-an-env-file]Using .env files for storing sensitive data [#using-an-env-file]

In general, sensitive data such as usernames and passwords should live in a different place than the configuration or the code.

In the case of Compose, this data can be stored in a file named [.inline-code].env[.inline-code] located at the root of your project, next to the [.inline-code]compose.yaml[.inline-code] file.

For example, to launch a PostgreSQL container with Compose, you can declare the following environment variables into an [.inline-code].env[.inline-code] file:

 USERNAME=john
PASSWORD=hello
DATABASE=staging

And use them in the [.inline-code]compose.yaml[.inline-code] file using the following syntax:

 version: '3'

services:
  db:
    image: postgres
    environment:
      POSTGRES_USER: ${USERNAME}
      POSTGRES_PASSWORD: ${PASSWORD}
      POSTGRES_DB: ${DATABASE}
    volumes:
      - ./data:/var/lib/postgresql/data
    ports:
      - 5432:5432

Note that when running the [.inline-code]docker compose up[.inline-code] command, Compose will automatically detect the [.inline-code].env[.inline-code] file and perform the required variable substitutions before launching your service.

You can learn more about environment variables and secrets by visiting the official Docker documentation pages.