Writing and Deploying a Chef PostgreSQL Cookbook

Hands-On Lab


Photo of Keith Thompson

Keith Thompson

DevOps Training Architect II in Content





The core unit of Chef configuration is the cookbook. In this activity, the student will write a cookbook to install and configure PostgreSQL 9.6. After writing the cookbook it will be stored on the Chef Server and run on a managed node. This process encompasses the primary workflow used with Chef and is a great start to learning Chef.

What are Hands-On Labs?

Hands-On Labs are scenario-based learning environments where learners can practice without consequences. Don't compromise a system or waste money on expensive downloads. Practice real-world skills without the real-world risk, no assembly required.

Writing and Deploying a Chef PostgreSQL Cookbook

The Scenario

As the first step of our organization's migration to Chef-managed infrastructure, we've decided to write the configuration for our PostgreSQL database server first. For the time being, we're only concerned with installing PostgreSQL, initializing the database, and enabling PostgreSQL to start on boot. We have a workstation server set up to for development purposes, and we've already registered a node with our Chef Server. After we've written our cookbook, we need to push it to the Chef Server, and run it on the node.

On the workstation, we already have a chef-repo where we can begin working on the cookbook, and connect to the Chef Server. In case we run into something we're not familiar with, as far as the resources required to write this cookbook, we ought to have the Chef documentation open handy as we go.

Logging in

On the lab overview page, we'll see three EC2 instances: a Chef server ( we'll call it chef), a workstation (we'll call it worker) and a node (we'll call it node). The shell prompts in this lab guide will reflect which one we're running commands in at the moment.

Create a postgres Cookbook, and Add It to the database-1 run-list

We should create a postgresql cookbook that will hold onto the PostgreSQL configuration policy. After the cookbook is created, we need to push it to the Chef Server and add it to the database-1 node's run-list.

Remember that the cookbook needs to do the following:

  • Install PostgreSQL Server 9.6
  • Initialize the database
  • Start the PostgreSQL service

To install PostgreSQL 9.6 we'll likely want to to use the official PostgreSQL YUM repository.

Generating the Cookbook

After logging into node, we can get into the provided Chef repository right away, with cd chef-repo. Once we're in there, we can start building our cookbook:

cloud_user@node]$ chef generate cookbook cookbooks/postgres

Now let's create a spot to store the PostgreSQL install file, then get into it:

cloud_user@node]$ mkdir -p cookbooks/postgres/files/default
cloud_user@node]$ cd cookbooks/postgres/files/default

And now we can pull the installer down so that we have a local copy:

cloud_user@node]$ curl -O https://download.postgresql.org/pub/repos/yum/9.6/redhat/rhel-7-x86_64/pgdg-centos96-9.6-3.noarch.rpm

Writing Our Recipe

Once we get back into our repo's base (cd ~/chef-repo) we'll start on the recipe. Edit the file ./cookbooks/postgres/recipes/default.rb with whichever text editor you like. When we're done, the file should read like this (after our additions, and once we've removed the comments that were sitting in there already):

cookbook_file "/opt/pgdg-centos96-9.6-3.noarch.rpm" do
  source "pgdg-centos96-9.6-3.noarch.rpm"

package "pgdg-centos96-9.6-3.noarch.rpm" do
  source "/opt/pgdg-centos96-9.6-3.noarch.rpm"

package "postgresql96-server"
package "postgresql96-contrib"

execute "initdb" do
  command "/usr/pgsql-9.6/bin/postgresql96-setup initdb"
  not_if 'test "$(ls -A /var/lib/pgsql/9.6/data/)"'
#  not_if { Dir.exists?("/var/lib/pgsql/9.6/data/") && !Dir.empty?('/var/lib/pgsql/9.6/data/') }

service "postgresql-9.6" do
  action [:enable, :start]

Note the two not_if statements in there. We only need one, depending on whether or not we want a Bash or a Ruby approach to checking for PostGreSQL's existence. We can comment out whichever one we don't want.

Loading Our Cookbook

If we run this:

cloud_user@node]$ knife upload cookbooks/postgres

and then this:

cloud_user@node]$ knife node list

we'll see our database-1 node running. Now, we can add our recipe to the run-list, and then load the cookbook:

cloud_user@node]$ knife node run_list add database-1 'recipe[postgres]'
cloud_user@node]$ knife ssh 'name:database-1' 'sudo chef-client'

Are We Done?

Well, we're not quite finished yet. If we load our cookbook again, we don't want the database initializing. Remember, we put a not_if statement in the recipe so that it would only happen once, ever. So let's run this again and check:

cloud_user@node]$ knife ssh 'name:database-1' 'sudo chef-client'

It should run a lot faster this time. If we read the command's output, we'll see that execute[initdb] was skipped. We'll also notice that the postgresql-9.6 service was started when we loaded the cookbook.


We had three tasks to accomplish with this recipe; get PostgreSQL Server 9.6 installed and running, make it start on boot, and initialize the database. We did it all. Congratulations!