Using PostgreSQL

Before using the PostgreSQL backend, you must set up a PostgreSQL server. Note that users installing PuppetDB via the module will already have PostgreSQL configured properly and these steps should not be necessary.

Please note, if you are not using the module, and you are running PostgreSQL on a different server from your PuppetDB node, you must configure an SSL connection between PuppetDB and PostgreSQL to secure your database connections. Otherwise your PuppetDB communication with Postgres will be going over a network in plaintext.

If you are not using the module, you will need to configure a PostgreSQL server, version 11 or newer, to include a user and an empty database for PuppetDB, and the server must accept incoming connections to that database as that user. PostgreSQL connections and authentication are discussed here, and setting up users and databases is discussed in the Getting Started section of the PostgreSQL manual.

Completely configuring PostgreSQL is beyond the scope of this guide, but a example setup is described below. First, you can create a user and database as follows. Then, to have a secure installation you must create a read-only user to configure the [read-database] config section. This limits the postgresql permissions of PuppetDB queries and prevents them from writing, updating, or deleting any data. If you do this, the normal [database] user should be granted the read user's "role" so that it will be able to properly coordinate partition clean up (it needs to be able to terminate read user queries that might be blocking the attempt).

sudo -u postgres sh
createuser -DRSP puppetdb
createuser -DRSP puppetdb_read
# For coordinating partition cleanup
psql puppetdb -c 'grant puppetdb_read to puppetdb'

createdb -E UTF8 -O postgres puppetdb
psql puppetdb -c 'revoke create on schema public from public'
psql puppetdb -c 'grant create on schema public to puppetdb'
psql puppetdb -c 'alter default privileges for user puppetdb in schema public grant select on tables to puppetdb_read'
psql puppetdb -c 'alter default privileges for user puppetdb in schema public grant usage on sequences to puppetdb_read'
psql puppetdb -c 'alter default privileges for user puppetdb in schema public grant execute on functions to puppetdb_read'

If you already have PuppetDB installed and running and are adding a read-only user, you will need to grant the same privileges as above to existing objects.

psql puppetdb -c 'grant select on all tables in schema public to puppetdb_read'
psql puppetdb -c 'grant usage on all sequences in schema public to puppetdb_read'
psql puppetdb -c 'grant execute on all functions in schema public to puppetdb_read'

Particularly if you plan to run more than one PuppetDB instance connecting to the same database, we recommend you also add and use a "migrator" user

Install the pg_trgm extension. PuppetDB makes use of this extension to improve the performance of queries that use regular expression filters (e.g. certname ~ "abc\"). This may require installing the postgresql-contrib (or equivalent) package, depending on your distribution:

$ sudo -u postgres sh
$ psql puppetdb -c 'create extension pg_trgm'
$ exit

Next, you will most likely need to modify the pg_hba.conf file to allow for MD5 authentication from at least localhost. To locate the file you can either issue a locate pg_hba.conf command (if your distribution supports it) or consult your distribution's documentation for the PostgreSQL confdir.

The following example pg_hba.conf file allows MD5 authentication from localhost for both IPv4 and IPv6 connections:

local   all        all                  md5
host    all        all  md5
host    all        all    ::1/128       md5

Restart PostgreSQL and ensure you can log in by running:

$ sudo service postgresql restart
$ psql -h localhost puppetdb puppetdb

To configure PuppetDB to use this database, put the following in the [database] section:

subname = //<HOST>:<PORT>/<DATABASE>
username = <USERNAME>
password = <PASSWORD>

Replace <HOST> with the DB server's hostname. Replace <PORT> with the port on which PostgreSQL is listening. Replace <DATABASE> with the name of the database you've created for use with PuppetDB.

Using SSL With PostgreSQL

It's possible to use SSL to protect connections to the database. There are several extra steps and considerations when doing so; see the PostgreSQL SSL setup page for complete details.

You have two options for setting up an SSL connection to Postgres, we recommend using certificates to setup and authenticate an SSL connection between your Postgres and PuppetDB. It is also possible to use password authentication, but this has many limitations. Firstly, your PostgreSQL database username and password will be stored in plaintext in your PuppetDB configuration files. Additionally, neither Postgres nor PuppetDB will be verifying the identity of the other server, meaning that anyone who can read that plaintext password will be able to create database connections to Postgres.

Coordinating database migrations

If you plan to run more than one PuppetDB instance connected to the same database, you must ensure that two instances do not attempt to upgrade the database simultaneously. Further, you should also ensure that a PuppetDB server never tries to use a database whose migration level (data format version) differs from the one it expects.

PuppetDB will refuse to start if it detects an unexpected migration level, which covers many cases, but won't help, for example, if a new version of PuppetDB is started while older versions are still running.

One direct solution, using upgrades as an example, is to just make sure to stop all of your PuppetDB instances, then run one instance of the newer version to perform any necessary upgrade via

puppetdb upgrade -c .../normal-config.ini

Once that's finished, relaunch all of your instances using the newer version of PuppetDB.

PuppetDB can also be configured to attempt to automatically guard you against these risks. To do so, first make sure all but one of your PuppetDB instances are configured with [database] migrate option set to false in the config file.

This will prevent PuppetDB from attempting to upgrade the database at startup (it will just quit on a mismatch instead). You will of course need to set it to true (the default) in the config file of the instance you want to perform your migrations (either at startup or via the upgrade subcommand shown above).

Setting migrate to false helps prevent unexpected migrations, but it doesn't prevent a migration from starting while other (soon to be invalid/out-of-date) PuppetDB instances continue to access the database. That's true even though newer PuppetDB versions have a check to prevent them from creating new connections to an unrecognized database version because PuppetDB can continue to use any connections that are already open (and either active or waiting in the connection pool).

To help prevent all acccess to an unexpected database version, you can provide PuppetDB with a separate, suitably configured PostgreSQL user (role), for migrations. That role must have the ability to grant and revoke connection privileges to/from the normal PuppetDB database user, and it must also be allowed to terminate the normal user's existing connections. One way to arrange that is to do sometthing like this after creating the puppetdb and puppetdb_read users as described above:

sudo -u postgres sh
createuser -DRSP puppetdb_migrator
psql puppetdb -c 'revoke connect on database puppetdb from public'
psql puppetdb -c 'grant connect on database puppetdb to puppetdb_migrator with grant option'

psql puppetdb -c 'set role puppetdb_migrator; grant connect on database puppetdb to puppetdb'
psql puppetdb -c 'set role puppetdb_migrator; grant connect on database puppetdb to puppetdb_read'

# Allow the migrator to terminate puppetdb connections.  It must also be
# allowed to terminate puppetdb_read connections, and can, given the
# recommended config (above) because the puppetdb role was granted the
# puppetdb_read role.
psql puppetdb -c 'grant puppetdb to puppetdb_migrator'

Then specify puppetdb_migrator as the migrator-username and set the migrator-password as described below.

See the migration coordination documentation for a more detailed explanation of the process.