PostgreSQL (Database service)

PostgreSQL is a high-performance, standards-compliant relational SQL database.

See the PostgreSQL documentation for more information.

Use a framework 

If you use one of the following frameworks, follow its guide:

For more implementation ideas, consult a template.

Supported versions 

Grid Dedicated Gen 3 Dedicated Gen 2
  • 14
  • 13
  • 12
  • 11
  • 10
  • 14
  • 13
  • 12
  • 11
  • 10
  • 11*

* No High-Availability on Dedicated Gen 2.

Deprecated versions 

The following versions are deprecated. They’re available, but they aren’t receiving security updates from upstream and aren’t guaranteed to work. They’ll be removed at some point in the future, so you should migrate to one of the supported versions.

Grid Dedicated Gen 3 Dedicated Gen 2
  • 9.6
  • 9.5
  • 9.4
  • 9.3
  • 9.6*
  • 9.5
  • 9.4
  • 9.3


The format exposed in the $PLATFORM_RELATIONSHIPS environment variable:

    "username": "main",
    "scheme": "pgsql",
    "service": "postgresql12",
    "fragment": null,
    "ip": "",
    "hostname": "",
    "port": 5432,
    "cluster": "rjify4yjcwxaa-master-7rqtwti",
    "host": "postgresql.internal",
    "rel": "postgresql",
    "path": "main",
    "query": {
        "is_master": true
    "password": "main",
    "type": "postgresql:12",
    "public": false,
    "host_mapped": false

Usage example 

1. Configure the service 

To define the service, use the postgresql type:

    type: postgresql:<VERSION>
    disk: 256

Note that if you later change the name, it’s treated as an entirely new service. This removes all data from your service. Always backup your data before changing the service.

2. Add the relationship 

To define the relationship, use the postgresql endpoint :

You can define <SERVICE_NAME> and <RELATIONSHIP_NAME> as you like, but it’s best if they’re distinct.

For PHP, enable the extension for the service:
        - pdo_pgsql

Example Configuration 

Service definition

    type: postgresql:14
    disk: 256

App configuration
    postgresdatabase: "dbpostgres:postgresql"

# If using PHP
        - pdo_pgsql

Use in app 

Then use the service in your app with a configuration file like the following:

package examples

import (
	_ ""
	psh ""
	libpq ""

func UsageExamplePostgreSQL() string {

	// Create a NewRuntimeConfig object to ease reading the environment variables.
	// You can alternatively use os.Getenv() yourself.
	config, err := psh.NewRuntimeConfig()

	// The 'database' relationship is generally the name of the primary SQL database of an application.
	// It could be anything, though, as in the case here where it's called "postgresql".
	credentials, err := config.Credentials("postgresql")

	// Retrieve the formatted credentials.
	formatted, err := libpq.FormattedCredentials(credentials)

	// Connect.
	db, err := sql.Open("postgres", formatted)

	defer db.Close()

	// Creating a table.
	sqlCreate := \x60
city VARCHAR(30) NOT NULL);\x60

	_, err = db.Exec(sqlCreate)

	// Insert data.
	sqlInsert := \x60
INSERT INTO PeopleGo(name, city) VALUES
('Neil Armstrong', 'Moon'),
('Buzz Aldrin', 'Glen Ridge'),
('Sally Ride', 'La Jolla');\x60

	_, err = db.Exec(sqlInsert)

	table := \x60<table>

	var id int
	var name string
	var city string

	// Read it back.
	rows, err := db.Query("SELECT * FROM PeopleGo")
	if err != nil {
	} else {
		for rows.Next() {
			err = rows.Scan(&id, &name, &city)
			table += fmt.Sprintf("<tr><td>%s</td><td>%s</td><tr>\n", name, city)
		table += "</tbody>\n</table>\n"

	_, err = db.Exec("DROP TABLE PeopleGo;")

	return table
package sh.platform.languages.sample;

import sh.platform.config.Config;
import sh.platform.config.MySQL;
import sh.platform.config.PostgreSQL;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.function.Supplier;

public class PostgreSQLSample implements Supplier<String> {

    public String get() {
        StringBuilder logger = new StringBuilder();

        // Create a new config object to ease reading the environment variables.
        // You can alternatively use getenv() yourself.
        Config config = new Config();

        // The 'database' relationship is generally the name of primary SQL database of an application.
        // It could be anything, though, as in the case here here where it's called "postgresql".
        PostgreSQL database = config.getCredential("postgresql", PostgreSQL::new);
        DataSource dataSource = database.get();

        // Connect to the database
        try (Connection connection = dataSource.getConnection()) {

            // Creating a table.
            String sql = "CREATE TABLE JAVA_FRAMEWORKS (" +
                    " id SERIAL PRIMARY KEY," +
                    "name VARCHAR(30) NOT NULL)";

            final Statement statement = connection.createStatement();

            // Insert data.
            sql = "INSERT INTO JAVA_FRAMEWORKS (name) VALUES" +
                    "('Spring')," +
                    "('Jakarta EE')," +
                    "('Eclipse JNoSQL')";


            // Show table.
            sql = "SELECT * FROM JAVA_FRAMEWORKS";
            final ResultSet resultSet = statement.executeQuery(sql);
            while ( {
                int id = resultSet.getInt("id");
                String name = resultSet.getString("name");
                logger.append(String.format("<li>id <code>%d</code> has a name of <code>%s</code>", id, name));
            statement.execute("DROP TABLE JAVA_FRAMEWORKS");
            return logger.toString();
        } catch (SQLException exp) {
            throw new RuntimeException("An error when execute PostgreSQL", exp);
const pg = require("pg");
const config = require("platformsh-config").config();

exports.usageExample = async function () {
    const credentials = config.credentials("postgresql");

    const client = new pg.Client({
        port: credentials.port,
        user: credentials.username,
        password: credentials.password,
        database: credentials.path,


    // Creating a table.
    await client.query(
            id SERIAL PRIMARY KEY,
            name VARCHAR(30) NOT NULL,
            city VARCHAR(30) NOT NULL

    // Insert data.
    await client.query(
        `INSERT INTO People (name, city)
            ('Neil Armstrong', 'Moon'),
            ('Buzz Aldrin', 'Glen Ridge'),
            ('Sally Ride', 'La Jolla');`

    // Show table.
    const result = await client.query("SELECT * FROM People");

    // Drop table.
    await client.query("DROP TABLE People");

    const outputRows = result.rows
        .map(({ name, city }) => `<tr><td>${name}</td><td>${city}</td></tr>\n`)

    return `


use Platformsh\ConfigReader\Config;

// Create a new config object to ease reading the environment variables.
// You can alternatively use getenv() yourself.
$config = new Config();

// The 'database' relationship is generally the name of primary SQL database of an application.
// It could be anything, though, as in the case here here where it's called "postgresql".
$credentials = $config->credentials('postgresql');

try {
    // Connect to the database using PDO.  If using some other abstraction layer you would
    // inject the values from $database into whatever your abstraction layer asks for.
    $dsn = sprintf('pgsql:host=%s;port=%d;dbname=%s', $credentials['host'], $credentials['port'], $credentials['path']);
    $conn = new \PDO($dsn, $credentials['username'], $credentials['password'], [
        // Always use Exception error mode with PDO, as it's more reliable.

    $conn->query("DROP TABLE IF EXISTS People");

    // Creating a table.
    $sql = "CREATE TABLE IF NOT EXISTS People (
      name VARCHAR(30) NOT NULL,
      city VARCHAR(30) NOT NULL

    // Insert data.
    $sql = "INSERT INTO People (name, city) VALUES
        ('Neil Armstrong', 'Moon'),
        ('Buzz Aldrin', 'Glen Ridge'),
        ('Sally Ride', 'La Jolla');";

    // Show table.
    $sql = "SELECT * FROM People";
    $result = $conn->query($sql);

    if ($result) {
        print <<<TABLE
        foreach ($result as $record) {
            printf("<tr><td>%s</td><td>%s</td></tr>\n", $record->name, $record->city);
        print "</tbody>\n</table>\n";

    // Drop table.
    $sql = "DROP TABLE People";

} catch (\Exception $e) {
    print $e->getMessage();
import psycopg2
from platformshconfig import Config

def usage_example():
    # Create a new Config object to ease reading the environment variables.
    # You can alternatively use os.environ yourself.
    config = Config()

    # The 'database' relationship is generally the name of primary SQL database of an application.
    # That's not required, but much of our default automation code assumes it.' \
    database = config.credentials('postgresql')

        # Connect to the database.
        conn_params = {
            'host': database['host'],
            'port': database['port'],
            'dbname': database['path'],
            'user': database['username'],
            'password': database['password']

        conn = psycopg2.connect(**conn_params)

        # Open a cursor to perform database operations.
        cur = conn.cursor()

        cur.execute("DROP TABLE IF EXISTS People")

        # Creating a table.
        sql = '''
                CREATE TABLE IF NOT EXISTS People (
                id SERIAL PRIMARY KEY,
                name VARCHAR(30) NOT NULL,
                city VARCHAR(30) NOT NULL


        # Insert data.
        sql = '''
                INSERT INTO People (name, city) VALUES
                ('Neil Armstrong', 'Moon'),
                ('Buzz Aldrin', 'Glen Ridge'),
                ('Sally Ride', 'La Jolla');


        # Show table.
        sql = '''SELECT * FROM People'''
        result = cur.fetchall()

        table = '''<table>

        if result:
            for record in result:
                table += '''<tr><td>{0}</td><td>{1}</td><tr>\n'''.format(record[1], record[2])
            table += '''</tbody>\n</table>\n'''

        # Drop table
        sql = "DROP TABLE People"

        # Close communication with the database

        return table

    except Exception as e:
        return e

Access the service directly 

Access the service using the Platform CLI by running platform sql.

You can also access it from your app container via SSH. From your relationship data, you need: username, host, and port. Then run the following command:

psql -U <USERNAME> -h <HOST> -p <PORT>

Using the values from the example, that would be:

psql -U main -h postgresql.internal -p 5432

Exporting data 

The easiest way to download all data in a PostgreSQL instance is with the Platform CLI. If you have a single SQL database, the following command exports all data using the pg_dump command to a local file:

platform db:dump

If you have multiple SQL databases it prompts you which one to export. You can also specify one by relationship name explicitly:

platform db:dump --relationship database

By default the file is uncompressed. If you want to compress it, use the --gzip (-z) option:

platform db:dump --gzip

You can use the --stdout option to pipe the result to another command. For example, if you want to create a bzip2-compressed file, you can run:

platform db:dump --stdout | bzip2 > dump.sql.bz2

Importing data 

Make sure that the imported file contains objects with cleared ownership and IF EXISTS clauses. For example, you can create a DB dump with following parameters:

pg_dump --no-owner --clean --if-exists

The easiest way to load data into a database is to pipe an SQL dump through the platform sql command, like so:

platform sql < my_database_backup.sql

That runs the database backup against the SQL database on That works for any SQL file, so the usual caveats about importing an SQL dump apply (for example, it’s best to run against an empty database). As with exporting, you can also specify a specific environment to use and a specific database relationship to use, if there are multiple.

platform sql --relationship database -e <BRANCH_NAME> < my_database_backup.sql

Multiple databases 

If you are using version 10, 11, 12, 13, or later of this service, it’s possible to define multiple databases as well as multiple users with different permissions. To do so requires defining multiple endpoints. Under the configuration key of your service there are two additional keys:

  • databases: This is a YAML array listing the databases that should be created. If not specified, a single database named main is created.
  • endpoints: This is a nested YAML object defining different credentials. Each endpoint may have access to one or more schemas (databases), and may have different levels of permission for each. The valid permission levels are:
    • ro: Using this endpoint only SELECT queries are allowed.
    • rw: Using this endpoint SELECT queries as well as INSERT/UPDATE/DELETE queries are allowed.
    • admin: Using this endpoint all queries are allowed, including DDL queries (CREATE TABLE, DROP TABLE, etc.).

Consider the following illustrative example:

    type: postgresql:13
    disk: 2048
            - main
            - legacy
                    main: admin
                    legacy: admin
                default_database: main
                    main: ro
                default_database: legacy
                    legacy: rw

This example creates a single PostgreSQL service named dbpostgres. The server has two databases, main and legacy with three endpoints created.

  • admin: has full access to both databases.
  • reporter: has SELECT query access to the main database, but no access to legacy.
  • importer: has SELECT/INSERT/UPDATE/DELETE access (but not DDL access) to the legacy database. It doesn’t have access to main.

If a given endpoint has access to multiple databases you should also specify which is listed by default in the relationships array. If one isn’t specified, the path property of the relationship is null. While that may be acceptable for an application that knows the name of the database it’s connecting to, automated tools like the CLI can’t access the database on that relationship. For that reason, defining the default_database property is always recommended.

Once these endpoints are defined, you need to expose them to your application as a relationship. Continuing with the above example, your relationships in might look like:

    database: "dbpostgres:admin"
    reports: "dbpostgres:reporter"
    imports: "dbpostgres:importer"

Each database is accessible to your application through the database, reports, and imports relationships. They’ll be available in the PLATFORM_RELATIONSHIPS environment variable and all have the same structure documented above, but with different credentials. You can use those to connect to the appropriate database with the specified restrictions using whatever the SQL access tools are for your language and application.

A service configuration without the configuration block defined is equivalent to the following default values:

        - main
          default_database: main
            main: admin

If you do not define database but endpoints are defined, then the single database main is created with the following assumed configuration:

        - main
    endpoints: <your configuration>

Alternatively, if you define multiple databases but no endpoints, a single user main is created with admin access to each of your databases, equivalent to the configuration below:

        - firstdb
        - seconddb
        - thirddb
            firstdb: admin
            seconddb: admin
            thirddb: admin

Service timezone 

To change the timezone for the current session, run SET TIME ZONE <timezone>;.

Extensions supports a number of PostgreSQL extensions. To enable them, list them under the configuration.extensions key in your services.yaml file, like so:

    type: postgresql:12
    disk: 1025
            - pg_trgm
            - hstore

In this case, you have pg_trgm installed, providing functions to determine the similarity of text based on trigram matching, and hstore providing a key-value store.

Available extensions 

The following is the extensive list of supported extensions. Note that you can’t currently add custom extensions not listed here.

  • address_standardizer - Used to parse an address into constituent elements. Generally used to support geocoding address normalization step.
  • address_standardizer_data_us - For standardizing addresses based on US dataset example
  • adminpack - administrative functions for PostgreSQL
  • autoinc - functions for auto-incrementing fields
  • bloom - bloom access method - signature file based index (requires 9.6 or higher)
  • btree_gin - support for indexing common data types in GIN
  • btree_gist - support for indexing common data types in GiST
  • chkpass - data type for auto-encrypted passwords
  • citext - data type for case-insensitive character strings
  • cube - data type for multidimensional cubes
  • dblink - connect to other PostgreSQL databases from within a database
  • dict_int - text search dictionary template for integers
  • dict_xsyn - text search dictionary template for extended synonym processing
  • earthdistance - calculate great-circle distances on the surface of the Earth
  • file_fdw - foreign-data wrapper for flat file access
  • fuzzystrmatch - determine similarities and distance between strings
  • hstore - data type for storing sets of (key, value) pairs
  • insert_username - functions for tracking who changed a table
  • intagg - integer aggregator and enumerator (obsolete)
  • intarray - functions, operators, and index support for 1-D arrays of integers
  • isn - data types for international product numbering standards
  • lo - Large Object maintenance
  • ltree - data type for hierarchical tree-like structures
  • moddatetime - functions for tracking last modification time
  • pageinspect - inspect the contents of database pages at a low level
  • pg_buffercache - examine the shared buffer cache
  • pg_freespacemap - examine the free space map (FSM)
  • pg_prewarm - prewarm relation data (requires 9.6 or higher)
  • pg_stat_statements - track execution statistics of all SQL statements executed
  • pg_trgm - text similarity measurement and index searching based on trigrams
  • pg_visibility - examine the visibility map (VM) and page-level visibility info (requires 9.6 or higher)
  • pgcrypto - cryptographic functions
  • pgrouting - pgRouting Extension (requires 9.6 or higher)
  • pgrowlocks - show row-level locking information
  • pgstattuple - show tuple-level statistics
  • plpgsql - PL/pgSQL procedural language
  • postgis - PostGIS geometry, geography, and raster spatial types and functions
  • postgis_sfcgal - PostGIS SFCGAL functions
  • postgis_tiger_geocoder - PostGIS tiger geocoder and reverse geocoder
  • postgis_topology - PostGIS topology spatial types and functions
  • postgres_fdw - foreign-data wrapper for remote PostgreSQL servers
  • refint - functions for implementing referential integrity (obsolete)
  • seg - data type for representing line segments or floating-point intervals
  • sslinfo - information about SSL certificates
  • tablefunc - functions that manipulate whole tables, including crosstab
  • tcn - Triggered change notifications
  • timetravel - functions for implementing time travel
  • tsearch2 - compatibility package for pre-8.3 text search functions (obsolete, only available for 9.6 and 9.3)
  • tsm_system_rows - TABLESAMPLE method which accepts number of rows as a limit (requires 9.6 or higher)
  • tsm_system_time - TABLESAMPLE method which accepts time in milliseconds as a limit (requires 9.6 or higher)
  • unaccent - text search dictionary that removes accents
  • uuid-ossp - generate universally unique identifiers (UUIDs)
  • xml2 - XPath querying and XSLT


Could not find driver 

If you see this error: Fatal error: Uncaught exception 'PDOException' with message 'could not find driver', this means you are missing the pdo_pgsql PHP extension. You need to enable it in your (see above).


PostgreSQL 10 and later include an upgrade utility that can convert databases from previous versions to version 10 or later. If you upgrade your service from a previous version of PostgreSQL to version 10 or above (by modifying the services.yaml file), it upgrades automatically.

The utility can’t upgrade PostgreSQL 9 versions, so upgrades from PostgreSQL 9.3 to 9.6 aren’t supported. Upgrade straight to version 11 instead.

Downgrading isn’t supported. If you want, for whatever reason, to downgrade you should dump to SQL, remove the service, recreate the service, and import your dump.

Upgrade to PostgreSQL 12 with the postgis extension 

You can’t upgrade to PostgreSQL 12 with the postgis extension enabled. It involves a change to a major version that results in a failed deployment that requires support intervention to fix. Upgrading from 12 to a higher version is possible.

If you need to upgrade to version 12, follow the same steps recommended for downgrading:

  1. Dump the database.
  2. Remove the service.
  3. Create a new service with PostgreSQL 12.
  4. Import the dump to that service.