Cooking Perl with Chef Solo

David Golden


configuration management

(e.g. chef, puppet, cfengine, ...)

unknown state

target state

new machine

deployed app

infrastructure as code




(no manual steps, checklist, etc.)

one tool to deploy the whole stack

(DB, caching, messaging, etc.)

chef ❤ ruby

(written in ruby; various ruby app stacks)

chef ❤ python

(virtualenv; pip; django apps)

chef ?? perl

(system perl and CPAN)

do you need:

  • a different perl?
  • versioned dependency sets?

we have tools for that

  • perlbrew
  • local::lib
  • carton
  • (others?)

demo task: deploy simple Plack app

  • app.psgi
  • lib/ZZZ/Hello/
  • Makefile.PL
  • carton.lock


use ZZZ::Hello::World;
my $app = sub { ZZZ::Hello::World->run_psgi(@_) };


package ZZZ::Hello::World;
our $VERSION = "1.0";

use Plack::Request;

sub run_psgi {
  my $self = shift;
  my $req = Plack::Request->new(shift);
  my $res = $req->new_response(200);
  $res->body("Hello World (@{[scalar localtime]})");
  return $res->finalize;



use inc::Module::Install;
name 'ZZZ-Hello-World';
version '1.0';

requires 'Plack';
requires 'Starman';


carton.lock has versioned prereqs

(carton will install and use them at runtime)

$ carton install
$ carton exec -Ilib -- starman -D -p 80 app.psgi

step 1

teach chef about perlbrew and friends

quick chef glossary (1/2)

  • cookbook
  • recipe
  • resource
  • provider

quick chef glossary (2/2)

  • node
  • attributes

solution: a perlbrew cookbook

  • bootstrap perlbrew
  • perlbrew_perl — install a perl
  • perlbrew_lib — configure a local::lib
  • perlbrew_run — shell commands with specific perl

next: create a recipe for hello-world


ruby DSL ahead

recipe for hello-world (1/3)

# get perl and carton ready

include_recipe 'perlbrew'

perl_ver = node['hello-world']['perl_version']
perl_carton = "#{perl_ver}@carton"

perlbrew_perl perl_ver
perlbrew_lib perl_carton

perlbrew_run "cpanm Carton" do
  perlbrew perl_carton

recipe for hello-world (2/3)

# deploy the app from git

package 'git'

git "/opt/hello-world" do
  repository "git://"
  reference node['hello-world']['deploy_tag']

recipe for hello-world (3/3)

# install deps and fire it up

perlbrew_run "carton install" do
  cwd "/opt/hello-world"
  perlbrew perl_carton

perlbrew_run "carton exec -Ilib -- starman -D -p 80 app.psgi" do
  cwd "/opt/hello-world"
  perlbrew perl_carton

step 2

deploy to a (virtual) machine

chef vs chef solo

Chef vs chef solo

rsync/ssh is manual

(plus different data for each node)

(needs automation tools)

pocketknife (ruby)

littlechef (python)

I didn't like them

solution: pantry (perl)

  • pantry init
  • pantry create node
  • pantry edit node
  • pantry sync node

deploying hello-world (1/2)

$ mkdir my-pantry
$ cd my-pantry
$ git init
$ pantry init

(copy in perlbrew and hello-world cookbooks)

$ git add .
$ git commit -m "added cookbooks"
$ pantry create node
$ pantry edit node (node config file)

   "name" : "",
   "run_list" : ["recipe[hello-world]"],
   "hello-world": {
      "perl_version": "perl-5.14.2",
      "deploy_tag":   "v1.0"
} (advanced)

   "name" : "",
   "run_list" : ["recipe[perlbrew]", "recipe[hello-world]"],
   "perlbrew" : {
      "perls" : [ "perl-5.14.2", "perl-5.12.3", "perl-5.10.1" ],
      "install_options" : "-D usethreads"
   "hello-world": {
      "perl_version": "perl-5.14.2",
      "deploy_tag":   "v1.0"

deploying hello-world (2/2)

$ git add .
$ git commit -m "configured"
$ git tag release-1.0 -m "deploy hello-world 1.0"

(note: versioned configuration!)

$ pantry sync node

(get a cup of coffee while you wait)

it works!

Hello World

chef <3 perl (now)

still missing some things

  • better plack automation (e.g. restart server on update)
  • better carton automation (e.g. reinstalling dependencies when carton.lock changes)
  • better app deployment (e.g. parameterized git checkout)
  • more pantry features (e.g. avoid editing raw JSON)
  • ...

chef ❤ perl (soon)