GitHub Actions ship with PostgreSQL already installed in the hosted virtual environments, which removes most of the work normally involved in standing up a database for CI. The workflows below were first written to test pg_timetable and are now kept as a template for PostgreSQL-related actions; they target windows-latest, ubuntu-latest and macos-latest, though any of the available environments can be substituted.

Each workflow performs the same three jobs: start PostgreSQL and confirm it is accepting connections, create a user named scheduler, and create a database named timetable. Real pipelines would add steps for importing test data, checking out and building the code, running tests, collecting coverage and releasing.

Environment YAML Label Included Software
Ubuntu 20.04 ubuntu-20.04 ubuntu-20.04
Ubuntu 18.04 ubuntu-latest or ubuntu-18.04 ubuntu-18.04
Ubuntu 16.04 ubuntu-16.04 ubuntu-16.04
macOS 11.0 macos-11.0 macOS-11.0
macOS 10.15 macos-latest or macos-10.15 macOS-10.15
Windows Server 2019 windows-latest or windows-2019 windows-2019
Windows Server 2016 windows-2016 windows-2016

Ubuntu

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

  setup-postgresql-ubuntu:

    if: true # false to skip job during debug

    name: Setup PostgreSQL on Ubuntu

    runs-on: ubuntu-latest

    steps:

    - name: Start PostgreSQL on Ubuntu

      run: |

        sudo systemctl start postgresql.service

        pg_isready

    - name: Create scheduler user

      run: |

        sudo -u postgres psql --command='CREATE USER scheduler PASSWORD 'somestrong'' --command='du'

    - name: Create timetable database

      run: |

        sudo -u postgres createdb --owner=scheduler timetable

        PGPASSWORD=somestrong psql --username=scheduler --host=localhost --list timetable

On Ubuntu the server is started with systemctl and its readiness is verified with pg_isready. User creation goes through the psql client in non-interactive mode: one statement issues CREATE USER, and a following du lists the users so the result can be inspected. A mnemonic helps here — dt describes tables, du describes users.

The timetable database is created with createdb. Because sudo -u postgres switches to the system postgres account, no connection credentials are needed: that system user may connect locally without restrictions. As before, psql lists the databases afterwards for control.

macOS

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

  setup-postgresql-macos:

    if: true # false to skip job during debug

    name: Setup PostgreSQL on MacOS

    runs-on: macos-latest

    steps:

    - name: Start PostgreSQL on MacOS

      run: |

        brew services start postgresql

        echo 'Check PostgreSQL service is running'

        i=10

        COMMAND='pg_isready'

        while [ $i -gt 0 ]; do

            echo 'Check PostgreSQL service status'

            eval $COMMAND && break

            ((i--))

            if [ $i == 0 ]; then

                echo 'PostgreSQL service not ready, all attempts exhausted'

                exit 1

            fi

            echo 'PostgreSQL service not ready, wait 10 more sec, attempts left: $i'

            sleep 10

        done

    # Homebrew creates an account with the same name as the installing user, but no password

    - name: Create scheduler user

      run: |

        psql --command='CREATE USER scheduler PASSWORD 'somestrong'' --command='du' postgres

    - name: Create timetable database

      run: |

        createdb --owner=scheduler timetable

        PGPASSWORD=somestrong psql --username=scheduler --host=localhost --list timetable

The macOS variant differs only slightly. brew services starts the server, and the readiness check is more elaborate than on Ubuntu. The sudo -u postgres prefix can be dropped entirely, since the current user already holds all required rights in that environment.

Windows

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

  setup-postgresql-windows:

    if: true # false to skip job during debug

    name: Setup PostgreSQL on Windows

    runs-on: windows-latest

    steps:

    - name: Start PostgreSQL on Windows

      run: |

        $pgService = Get-Service -Name postgresql*

        Set-Service -InputObject $pgService -Status running -StartupType automatic

        Start-Process -FilePath '$env:PGBINpg_isready' -Wait -PassThru

    - name: Create scheduler user on Windows

      run: |

        & $env:PGBINpsql --command='CREATE USER scheduler PASSWORD 'somestrong'' --command='du'

    - name: Create timetable database

      run: |

        & $env:PGBINcreatedb --owner=scheduler timetable

        $env:PGPASSWORD = 'somestrong'

        & $env:PGBINpsql --username=scheduler --host=localhost --list timetable

Windows takes a different route. The environment defaults to PowerShell (pwsh), and services are managed by Windows itself rather than by systemctl or Homebrew. The scripts otherwise stay readable.

The detail worth calling out is the ampersand (&), PowerShell's call operator, placed in front of certain commands. It executes a command, script or function, which matters because psql and createdb are not on the PATH and must be reached through their full paths. The PGBIN environment variable supplies that location; concatenating it with a command name yields an ordinary string, and the call operator is what turns that string back into an executable command.

pwsh is available across all GitHub Actions environments, including Ubuntu and macOS.

Other approaches

PostgreSQL can also be brought up inside GitHub Actions by other means — for example Docker-based actions or actions that download binaries — but those are not covered here.