Build Virtual Machines Easily with PuPHPet – Part 2

By Matthew Setter

In part one of this series, we considered the problem of how to create and manage development environments in a way which was simple, repeatable, and (even) efficient. It's a problem which I'm confident we've all run in to from time to time. The solution was a relatively new tool called PuPHPet.

If you missed part one, PuPHPet creates a Vagrant + Puppet configuration based on five key areas:

  1. Deploy Target (memory, ip address, port forwarding)
  2. Server Basics
  3. PHP (modules, libraries, config settings)
  4. Database (PostgreSQL, MySQL)
  5. Webserver (Apache 2, Nginx)

We looked at how to configure most of the options and how to use the generated configuration, with some basic vagrant commands. But that's as far as we went.

So in this second part of the series, we're going further. Specifically, we're going to be looking at the two core files used: common.yaml, and Vagrantfile. We'll be making some changes to them, then provisioning the virtual machines to reflect the configuration changes.

This series won't make you a master of either Puppet or Vagrant, but by the end of it, you'll have sufficient knowledge to make changes to the configuration generated by PuPHPet, so you can adapt your virtual machine, as your needs change.

NOTE: please be aware that there have been some important changes in Puppet and Vagrant, which affect PuPHPet. Please pay particular attention to the two notices at the top of the PuPHPet site – especially if you've been experiencing any difficulties following part one of this series. If the notices are gone, the problems are, too.

Provisioning Changes

For our purposes today, there is one key command which you need to know, vagrant provision. Quoting the manual:

This command is a great way to quickly test any provisioners, and is especially useful for incremental development of shell scripts, Chef cookbooks, or Puppet modules. You can just make simple modifications to the provisioning scripts on your machine, run a vagrant provision, and check for the desired results. Rinse and repeat.

We'll be using it to roll out our environment changes.

Altering Shared Folder Configuration

One of the things that you'll likely want to do first is to adjust the configuration of the shared folders. Assuming the original configuration from the first part in the series, here's what the relevant section of Vagrantfile looks like.

config.vm.synced_folder "/Users/matthewsetter/Documents/workspace/my-great-app", "/var/www/my-great-app", id: "vagrant-root", :nfs => false

You see that I've set up a shared folder in my workspace directory with the same name under /var/www/ in the virtual machine. Recently I was setting up a new Vagrant VM for a client's project. I found that my Zend Framework 2 application couldn't write to the cache directory. Looking at the directory structure of the project, in the virtual machine, all of the directories were owned by the user vagrant; but the webserver user (and group) was www-data. So it wasn't able to read. I saw two options:

  1. Change the permissions to 777
  2. Change the owner and group

To me the second was the more intelligent choice. But how to do that? As it turns out, it is quite simple. At the end of the configuration above, we need to specify the owner and group as www-data. I've done this in the updated configuration below.

    config.vm.synced_folder "/Users/matthewsetter/Documents/workspace/my-great-app", "/var/www/my-great-app", id: "vagrant-root", :nfs => false, owner: "www-data", group: "www-data"

To roll out the change, update the file Vagrantfile in your Vagrant configuration, save the file, then from the command line, run the command: vagrant provision. All being well, you'll see output similar to the first time you called vagrant up, but much shorter, as only a few changes are being made – not the entire VM being provisioned. When it's done, ssh in to the VM with vagrant ssh, then have a look at the permissions on the project directory. They should look like the output below:

    $ ls -lahrt /var/www/my-great-app/
    total 3.1M
    drwxrwxrwx 1 www-data www-data  102 Oct 15 05:00 data/
    -rw-rw-rw- 1 www-data www-data 856K Oct 15 05:00 composer.phar
    -rw-rw-rw- 1 www-data www-data  607 Oct 15 05:00
    -rw-rw-rw- 1 www-data www-data  259 Oct 15 05:00 .gitmodules
    -rw-rw-rw- 1 www-data www-data  338 Oct 15 05:00 .gitignore
    -rw-rw-rw- 1 www-data www-data 1.8K Oct 15 05:00 init_autoloader.php
    drwxrwxrwx 1 www-data www-data  340 Oct 15 05:00 db/
-rw-rw-rw- 1 www-data www-data 2.2M Oct 15 05:00 zftool.phar

Altering PHP’s Configuration

So far, so good. Let's look at the PHP configuration and make some changes. Below is the original configuration generated by PuPHPet in part one. You'll find it in vagrant/puppet/hieradata/common.yaml. It shows the PHP version is 5.5, composer's enabled, the php modules, ini settings and the default timezone.

    version: '55'
    composer: '1'
            - cli
            - intl
            - cgi
            - curl
            - mcrypt
            - memcache
            - memcached
            - pspell
            - tidy
            - sqlite
        pear: {  }
            - pecl_http
        display_errors: On
        error_reporting: 'E_ALL & ~E_STRICT'
        apc.enabled: '1'
    timezone: Europe/Berlin

Let's make some changes. Let's change:

  • the timezone to ’Europe/London’
  • the session save_path
  • some other runtime options:

Specifically, update from ini: down with the following:

        display_errors: On
        error_reporting: 'E_ALL & ~E_STRICT'
        apc.enabled: '1'
        session.save_path: /tmp
        allow_url_fopen: '1'
        allow_url_include: '1'
        error_log: syslog
        file_uploads: '1'
    timezone: Europe/London

You'll likely recognize the ini directives from the PHP Manual so I won't rehash them here. But you can see that they're as easy to configure here as they are in the PHP ini file. Actually, let's go out on a limb and make a decent change.

After doing so, here's the new PHP settings:

  • session.save_path => /tmp => /tmp
  • allow_url_fopen => On => On
  • allow_url_include => On => On
  • file_uploads => On => On
  • error_log => syslog => syslog
  • Default timezone => Europe/London
  • date.timezone => Europe/London => Europe/London

Note: If you'd like to pick a different timezone, here's the list supported by PHP.

Now I'm sure you'll agree that being able to change a PHP configuration, just by changing a text file, is powerful, very powerful. Imagine the amount of work you've likely done in the past to achieve this.

Altering XDebug Configuration

If you've been using PHP for any amount of time, you've likely been using Derek Rethan's XDebug package. In part one, we took the basic setup offered in PuPHPet. Let's make some modest changes to it and again provision the virtual machine to enable them. Below is the original configuration in common.yaml.

    install: '1'
        xdebug.default_enable: '1'
        xdebug.remote_autostart: '0'
        xdebug.remote_connect_back: '1'
        xdebug.remote_enable: '1'
        xdebug.remote_handler: dbgp
        xdebug.remote_port: '9000'

Add in the following options to it, remembering to respect the indentation, otherwise you'll have problems.

    xdebug.auto_trace = 1
    xdebug.idekey = "SITEPOINTPHP"
    xdebug.profiler_enable = 1
    xdebug.profiler_output_dir = "/tmp/xdebug.profiler"

There's no specific reason for these configuration options over any other. They've been randomly picked just to play with XDebug. So feel free to choose other options if you wish.

As before, call vagrant provision, wait a bit for the provisioning process to complete. Then we can check that the process' been successful by again looking at the running PHP configuration with:

php -i | grep -i xdebug
  • xdebug.auto_trace => On => On
  • xdebug.idekey => SITEPOINTPHP => SITEPOINTPHP
  • xdebug.max_nesting_level => 100 => 100
  • xdebug.profiler_enable => On => On
  • xdebug.profiler_output_dir => /tmp/xdebug.profiler => /tmp/xdebug.profiler

Altering MySQL’s Configuration

Ok, one last one. Let's make changes to MySQL and have a bit of fun. We’ll change the user account name and password, install and enable phpmyadmin. We’ll finish by making the user's privileges a bit more restrictive. Not a bad list of tasks. No?

Think for a moment just how long that'd take if you did it all by hand. No, seriously. maybe you already have provisioning scripts for this; maybe you do it all by hand; but either way it takes time.

Note: I'll be honest, I'm not a MySQL admin; so there's tools I won't be aware of. If you are, please mention some in the comments.

As before, below is the originally generated configuration so you know what it looks like.

    root_password: password
    phpmyadmin: 0
                - ALL
            name: testdb
            host: localhost
            user: testdb_couser
            password: testdb_password
            sql_file: ''

Below is the updated version, showing what it looks like with the changes I mentioned above. You see that the username and password have changed, I've set 1 next to phpMyAdmin to enable & install it.

    root_password: password
    phpmyadmin: 1
                - INSERT
                - SELECT
                - 'SHOW DATABASES'
                - UPDATE
                - USAGE
                - INDEX
                - DELETE
                - CREATE
                - ALTER
            name: testdb
            host: localhost
            user: testdb_username
            password: testdb_P@$sw0rd
            sql_file: ''

Let's provision the virtual machine again and see what's changed. After running vagrant provision, I'm able to login with the new username and password, and phpMyAdmin is available at


So, what do you think? Do PuPHPet, Vagrant, and Puppet make managing a development environment really simple? So many things which may have been done manually, or with hand written bash scripts don't need to be any longer.

As you followed along, did your provisioning run as smoothly as mine did? Did you encounter any issues that you need a hand with? If so, feel free to email me directly.

Otherwise, I hope that you've been bitten by the bug that is Vagrant, Puppet, and PuPHPet. I also hope you've seen just how easy it is to adjust your environments, on the fly, to match your changing needs, through nothing more than 2 version controlled text files and a handful of commands.

Please share your thoughts in the comments.

  • Peter Nijssen

    PuPHPet was a nice tool for me to start seeing the strength of Vagrant and Puppet. However, I soon came to a point I wanted to do more. After googling and trying a lot, I learned how to write the Puppet manifests myself. Currently, I am busy implementing every thing through the profile/role design of puppet.

    • Matthew Setter

      @peter_nijssen:disqus thanks for the feedback. I admit that this series is specifically targetted at those nearer the start of their Vagrant/Puppet journey, such as myself. But thanks for the pointer on Puppet profiles and roles.

      • Peter Nijssen

        Well, I am now only working a week now with puppet and I have rewritten everything 6 times, but now I came to a point that I am more then satisfied. I absolutely love puppet and if you have some spare time; dig in! It’s really fun and interesting. :)

  • muratpurc

    Thanks for the article. Could you please review the section about adding xdebug options, the example code there seems incomplete.

    • Matthew Setter

      @muratpurc:disqus I shall do. thanks for pointing that out.

  • Clauberson Pacheco

    Hello, I enjoyed this post, vagrant met recently and am amazed by the agility, and would like a your help, I am also new to zendframework2 and was wondering how can I redirect to the public all my projects run on ZendFramework folder. thank you

Get the latest in PHP, once a week, for free.