Deployment should be automated. Deployment should be easy. Deployment should be flexible.

I represent a simple deployment strategy which allows to

  • avoid storing passwords in version control
  • use site-specific deployment configuration
  • be understood easily

As an example, I assume a web application written in PHP. The goal is to not store the MySQL access credentials inside the application.

We’ll first make the application flexible enough to read local configuration. Jenkins will serve as an example on how to roll out the actual deployment.

Consider the following application

<?php
require_once('conf.inc.php'); # define the $CFG array

$mysqli = new mysqli($CFG['mysql_host'], $CFG['mysql_user'], $CFG['mysql_pass'], $CFG['mysql_db']);

echo "Hi!</br>";
echo $mysqli->host_info."</br>";

$mysqli->close();
?>

In order to run this, we’ll need obviously a conf.inc.php which holds the MySQL credentials. However, we don’t actually want to store the MySQL password there.

<?php
/*
This application's config file. Put everything in the CFG array. Note that 
everything can be overwritten locally by providing a "conf.local.inc.php" file 
in this file's directory (see end of this script).
*/
$CFG = array();

$CFG['mysql_host'] = 'www.example.com';
$CFG['mysql_user'] = 'mysqluser';
$CFG['mysql_pass'] = 'placeholder';
$CFG['mysql_db'] = 'dbname';

/* Check if deployment-specific configuration is available if load if possible. */
$local_conf_file = dirname(__FILE__)."/conf.local.inc.php";
if (file_exists($local_conf_file)) {
    include_once($local_conf_file);
}
?>

The simple trick is to have yet another config file which can overwrite the default values. That other file is never going to be put in version control (read: put it in your .gitconfig). But it’ll be lying around.

<?php
$CFG['mysql_pass'] = 'the_real_password';
?>

Note that this approach is not only good to avoid storing sensible information but also for development setups. One can easily use different database servers, credentials, paths and so on during development without ever breaking the master configuration.

All that’s left for us now is an automated deployment. As an example, let’s use Jenkins. Without going into the details, the idea is to create a Jenkins project which

  • checks out the repo
  • generates the local config file on the fly
  • deploys everything to the target machine

You can tell Jenkins to clone the repo into a local subfolder. Assuming the checkout goes into ./mylocaljenkinsclone, the appropriate Jenkins build script might look like this:

#!/bin/bash

# Write the local config file to the cloned repo.
cat > mylocaljenkinsclone/conf.local.inc.php <<'EOF'
$CFG['mysql_host'] = 'the_production_mysql_host';
$CFG['mysql_pass'] = 'the_production_mysql_password';
EOF

# Deploy.
rsync -av --delete ./mylocaljenkinsclone/ deploy@webserver.com:/var/www/html

That’s it! A click of a button in Jenkins and the new site is up and running!

The production password is now stored in Jenkins. You got to store it somewhere. The less components can access it, the better. Building the application is the very last step before deployment so that’s actually the only sane location where the password should get introduced.

To recap:

  • Build applications with (“local”) configuration mechanisms.
  • Don’t store passwords inside the application (repository).
  • Add credentials during deployment.