<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
 
 <title>Alex Coomans</title>
 <link href="http://alexcoomans.com/blog/atom.xml" rel="self" />
 <link href="http://alexcoomans.com/blog/" />
 <updated>2012-12-04T21:05:41-06:00</updated>
 <id>http://tom.preston-werner.com/</id>
 <author>
   <name>Alex Coomans</name>
   <email>alex@alexcoomans.com</email>
 </author>

 
 <entry>
   <title>Building a Custom 15TB RAID Storage Server</title>
   <link href="http://alexcoomans.com/blog/building-a-custom-raid-storage-server"/>
   <updated>2012-12-05T00:00:00-06:00</updated>
   <id>http://alexcoomans.com/blog/building-a-custom-raid-storage-server</id>
   <content type="html">&lt;p&gt;&lt;img src=&quot;/blog/images/2012-12-05/header.jpg&quot; alt=&quot;&quot; width=&quot;940&quot; height=&quot;380&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I recently built a custom 15TB RAID storage server to store all of my media files, and now that is has been running reliably for a few months, I figured I'd cover how I built it.&lt;/p&gt;

&lt;!--more--&gt;


&lt;h2&gt;The Hardware&lt;/h2&gt;

&lt;p&gt;Before this, I stored all of my data across about 5 external hard drives that I've acquired and filled over the years, and I really wanted everything to be in one place. I'd decided on using ZFS so I started there. Thanks to a &lt;a href=&quot;https://mocko.org.uk/b/2012/06/17/how-i-store-my-1s-and-0s-zfs-bargain-hp-microserver-joy/&quot; rel=&quot;nofollow&quot;&gt;post&lt;/a&gt; I saw on HackerNews I knew some basic requirements, and I also looked into FreeNAS. After a fair amount of research, I figured that the following would be my best bet:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&quot;http://www.amazon.com/gp/product/B005GAJHUS/ref=as_li_ss_tl?ie=UTF8&amp;camp=1789&amp;creative=390957&amp;creativeASIN=B005GAJHUS&amp;linkCode=as2&amp;tag=alexcoomans-20&quot;&gt;Intel Server Board S1200KP&lt;/a&gt; because it supported ECC memory in a pretty small package, also has dual gigabit ethernet&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&quot;http://www.amazon.com/gp/product/B005LTU54Q/ref=as_li_ss_tl?ie=UTF8&amp;camp=1789&amp;creative=390957&amp;creativeASIN=B005LTU54Q&amp;linkCode=as2&amp;tag=alexcoomans-20&quot;&gt;Intel Celeron G530 2.4GHz&lt;/a&gt; since I needed it to be low power (also really inexpensive)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&quot;http://www.amazon.com/gp/product/B002T3JN0Y/ref=as_li_ss_tl?ie=UTF8&amp;camp=1789&amp;creative=390957&amp;creativeASIN=B002T3JN0Y&amp;linkCode=as2&amp;tag=alexcoomans-20&quot;&gt;Kingston ECC 2x4GB Memory&lt;/a&gt; because ECC was recommended in the &lt;a href=&quot;http://news.ycombinator.com/item?id=4122937&quot; rel=&quot;nofollow&quot;&gt;YC comments&lt;/a&gt; about the article above. This was one of the harder (relatively) parts to find since the board needed ECC Unbuffered memory&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&quot;http://www.amazon.com/gp/product/B005RUBFAC/ref=as_li_ss_tl?ie=UTF8&amp;camp=1789&amp;creative=390957&amp;creativeASIN=B005RUBFAC&amp;linkCode=as2&amp;tag=alexcoomans-20&quot;&gt;Corsair 60 GB SSD&lt;/a&gt; which was originally planned to the ARC cache for ZFS, but I later scrapped that after the FreeNAS install didn't work (more later on). It ended up being the main OS drive.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;5 x &lt;a href=&quot;http://www.amazon.com/gp/product/B005T3GRLY/ref=as_li_ss_tl?ie=UTF8&amp;camp=1789&amp;creative=390957&amp;creativeASIN=B005T3GRLY&amp;linkCode=as2&amp;tag=alexcoomans-20&quot;&gt;Seagate Barracuda 3TB SATA&lt;/a&gt; drives for the main storage&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&quot;http://www.newegg.com/Product/Product.aspx?Item=N82E16816115111&quot;&gt;HighPoint Rocket 2720SGL HBA&lt;/a&gt; because I needed more SATA ports (the &lt;a href=&quot;http://www.amazon.com/gp/product/B000FBYS2U/ref=as_li_ss_tl?ie=UTF8&amp;camp=1789&amp;creative=390957&amp;creativeASIN=B000FBYS2U&amp;linkCode=as2&amp;tag=alexcoomans-20&quot;&gt;cables&lt;/a&gt; I used)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;An &lt;a href=&quot;http://www.newegg.com/Product/Product.aspx?Item=N82E16817338053&quot;&gt;Athena Power 300W Mini-ITX&lt;/a&gt; power supply&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;&lt;a href=&quot;/blog/images/2012-12-05/mobo_full.jpg&quot; class=&quot;fancybox&quot;&gt;&lt;img src=&quot;/blog/images/2012-12-05/mobo.jpg&quot; alt=&quot;&quot; width=&quot;940&quot; height=&quot;160&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;The Case&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;http://instagram.com/p/OK1cXDQNbi/&quot; rel=&quot;nofollow&quot;&gt;&lt;img src=&quot;http://distilleryimage2.s3.amazonaws.com/7ffd9546e35311e1ba4022000a1e8932_7.jpg&quot; width=&quot;300&quot; height=&quot;300&quot; class=&quot;br5 align-right&quot;/&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I've always wanted to build a custom computer case (and while this isn't a full case), this project gave me the perfect opportunity.&lt;/p&gt;

&lt;p&gt;I started off by looking for places that would do custom CNC work, and hopefully allowed me to avoid a CAD software (I'd already tried a design and failed). I found &lt;a href=&quot;http://www.emachineshop.com/&quot; rel=&quot;nofollow&quot;&gt;eMachineShop&lt;/a&gt; and they had a Windows app to help design what you wanted. I booted up my VM and after about a month of designing, printing, and test fitting using the parts I already had, I finally placed my order. The only downside was the price - it ended up costing me a little over $500 for two sets of all the pieces I needed. After about two weeks of waiting I received a UPS tracking number another week later I had them in my hands.&lt;/p&gt;

&lt;p&gt;At the same time I placed an order for all of the standoffs &amp;amp; screws I needed for the case from &lt;a href=&quot;http://digikey.com&quot; rel=&quot;nofollow&quot;&gt;DigiKey&lt;/a&gt;. Overall the standoffs have worked great, the only time I've had problems is when a few snapped at the top when I had to ship it (I have yet to order the replacements though), but overall it is pretty strong.&lt;/p&gt;

&lt;div class=&quot;clear&quot;&gt;&lt;/div&gt;




&lt;div class=&quot;imgrow&quot;&gt;
    &lt;a href=&quot;/blog/images/2012-12-05/build1_full.jpg&quot; class=&quot;fancybox&quot; rel=&quot;img1&quot;&gt;&lt;img src=&quot;/blog/images/2012-12-05/build1.jpg&quot; /&gt;&lt;/a&gt;
    &lt;a href=&quot;/blog/images/2012-12-05/build2_full.jpg&quot; class=&quot;fancybox&quot; rel=&quot;img1&quot;&gt;&lt;img src=&quot;/blog/images/2012-12-05/build2.jpg&quot; /&gt;&lt;/a&gt;
    &lt;a href=&quot;/blog/images/2012-12-05/build3_full.jpg&quot; class=&quot;fancybox&quot; rel=&quot;img1&quot;&gt;&lt;img src=&quot;/blog/images/2012-12-05/build3.jpg&quot; /&gt;&lt;/a&gt;
&lt;/div&gt;


&lt;h2&gt;The Software&lt;/h2&gt;

&lt;p&gt;I'd originally planned to use FreeNAS to power everything, but when I tried to install FreeNAS, there was a bug related to my HBA card, so it wouldn't recognize my five 3TB drives. After a bunch of tries suggested by other people using the card with previous versions, I decided to switch to Ubuntu. This ended up working out perfectly for me since I'm much more familiar with Debian-based systems than I am with FreeBSD ones.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://instagr.am/p/OQ2Y5rwNQt/&quot; rel=&quot;nofollow&quot;&gt;&lt;img src=&quot;http://distilleryimage9.s3.amazonaws.com/679b2d10e52911e1ba8122000a1e88a8_7.jpg&quot; width=&quot;300&quot; height=&quot;300&quot; class=&quot;br5 align-left&quot;&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I started off with a server install of 12.04LTS on the SSD. It instantly recognized my storage drives, and I started working on setting up ZFS. The &lt;a href=&quot;http://zfsonlinux.org/&quot;&gt;ZFS PPA&lt;/a&gt; was really easy to get installed and afterwords I set up the ZFS pool. The following was all I needed to get started with one large storage tank setup in a ZRAID configuration:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;sudo zpool create tank raidz /dev/disk/by-id/ata-ST3000DM001-serial ...
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;sudo zfs create tank/store
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This ZRAID left me with about 10.72TB:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;sudo zfs list
NAME         USED  AVAIL  REFER  MOUNTPOINT
tank        3.47T  7.25T  46.3K  /tank
tank/store  3.47T  7.25T  3.47T  /tank/store
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;




&lt;div class=&quot;clear&quot;&gt;&lt;/div&gt;


&lt;h4&gt;Sharing&lt;/h4&gt;

&lt;p&gt;To get sharing started, all I needed to do was install Netatalk and Avahi, and &lt;a href=&quot;http://kremalicious.com/ubuntu-as-mac-file-server-and-time-machine-volume/&quot; rel=&quot;nofollow&quot;&gt;this article&lt;/a&gt; helped me configure everything. In my &lt;code&gt;/etc/netatalk/afpd.conf&lt;/code&gt; config file I've got:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;- -tcp -noddp -uamlist uams_guest.so,uams_dhx.so,uams_dhx2_passwd.so -nosavepassword -nozeroconf -advertise_ssh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;And then to actually share that, I added the following into my &lt;code&gt;/etc/netatalk/AppleVolumes.default&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;:DEFAULT: options:upriv,usedots
/tank/store Dashr veto:/temp/ options:ro
/tank/store Dash allow:alex options:usedots,upriv
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Which gave me two shares - one for my own use (that excludes a temp folder I have some disorganized files in), and then a read-only share without authentication for anyone else to use.&lt;/p&gt;

&lt;h4&gt;Remote Access&lt;/h4&gt;

&lt;p&gt;So recently I decided I wanted to be able to access the server remotely (I've got nginx running in front of some other apps I run), and because I'm stuck in an apartment complex that has it's own router, and I have a router for my unit, I was in a bind. Then I realized that I could reverse SSH tunnel from my server to an EC2 micro server I've got running. To set that up, I created a cron script on my server that runs every five minutes, mainly too reconnect if something crashes (credit to &lt;a href=&quot;http://toic.org/blog/2009/reverse-ssh-port-forwarding/&quot; rel-&quot;nofollow&quot;&gt;this article&lt;/a&gt;):&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;&lt;span class=&quot;c&quot;&gt;#!/bin/sh&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;COMMAND&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;ssh -N -f -R 8080:localhost:80 ec2-user@&amp;quot;&lt;/span&gt;
pgrep -f -x &lt;span class=&quot;s2&quot;&gt;&amp;quot;$COMMAND&amp;quot;&lt;/span&gt; &amp;gt; /dev/null 2&amp;gt;&amp;amp;1 &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$COMMAND&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;And then on my EC2 server in my nginx configuration file, I added to following:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;nginx&quot;&gt;&lt;span class=&quot;k&quot;&gt;upstream&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;dash&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;127.0.0.1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8080&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;proxy_cache_path&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/var/www/cache&lt;/span&gt;  &lt;span class=&quot;s&quot;&gt;levels=1:2&lt;/span&gt;    &lt;span class=&quot;s&quot;&gt;keys_zone=STATIC:10m&lt;/span&gt;
                                 &lt;span class=&quot;s&quot;&gt;inactive=24h&lt;/span&gt;  &lt;span class=&quot;s&quot;&gt;max_size=1g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;server_name&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;remote.example.com&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;auth_basic&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;Restricted&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;auth_basic_user_file&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;dash.htpasswd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;proxy_set_header&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Host&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;proxy_set_header&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;X-Real-IP&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$remote_addr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;proxy_set_header&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;X-Forwarded-For&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;proxy_set_header&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;X-Forwarded-Proto&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$scheme&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;proxy_pass&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;http://dash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;^.+.(jpg|jpeg|gif|png|css|js)&lt;/span&gt;$ &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;auth_basic&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;Restricted&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;auth_basic_user_file&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;dash.htpasswd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;proxy_set_header&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Host&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;proxy_set_header&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;X-Real-IP&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$remote_addr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;proxy_set_header&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;X-Forwarded-For&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;proxy_set_header&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;X-Forwarded-Proto&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$scheme&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;proxy_pass&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;http://dash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;proxy_cache&lt;/span&gt;            &lt;span class=&quot;s&quot;&gt;STATIC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;proxy_cache_valid&lt;/span&gt;      &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;  &lt;span class=&quot;s&quot;&gt;1d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;proxy_cache_use_stale&lt;/span&gt;  &lt;span class=&quot;s&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;timeout&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;invalid_header&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;updating&lt;/span&gt;
                               &lt;span class=&quot;s&quot;&gt;http_500&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;http_502&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;http_503&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;http_504&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;I also added in some HTTP caching to speed some of the static assets up so those wouldn't need to also be sent over the tunnel. While not the speediest of options, it has worked reliably for me.&lt;/p&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;This was a really project to work on; I've wanted to build a custom case for a long time, and even though I spent hours and hours designing the case and quadruple checking that everything fit, I'm really pumped about the final result. Here are a few more pictures of my server today:&lt;/p&gt;

&lt;div class=&quot;imgrow imgrow5&quot;&gt;
    &lt;a href=&quot;/blog/images/2012-12-05/final1_full.jpg&quot; class=&quot;fancybox&quot; rel=&quot;img2&quot;&gt;&lt;img src=&quot;/blog/images/2012-12-05/final1.jpg&quot; /&gt;&lt;/a&gt;
    &lt;a href=&quot;/blog/images/2012-12-05/final2_full.jpg&quot; class=&quot;fancybox&quot; rel=&quot;img2&quot;&gt;&lt;img src=&quot;/blog/images/2012-12-05/final2.jpg&quot; /&gt;&lt;/a&gt;
    &lt;a href=&quot;/blog/images/2012-12-05/final3_full.jpg&quot; class=&quot;fancybox&quot; rel=&quot;img2&quot;&gt;&lt;img src=&quot;/blog/images/2012-12-05/final3.jpg&quot; /&gt;&lt;/a&gt;
    &lt;a href=&quot;/blog/images/2012-12-05/final4_full.jpg&quot; class=&quot;fancybox&quot; rel=&quot;img2&quot;&gt;&lt;img src=&quot;/blog/images/2012-12-05/final4.jpg&quot; /&gt;&lt;/a&gt;
    &lt;a href=&quot;/blog/images/2012-12-05/final5_full.jpg&quot; class=&quot;fancybox&quot; rel=&quot;img2&quot;&gt;&lt;img src=&quot;/blog/images/2012-12-05/final5.jpg&quot; /&gt;&lt;/a&gt;
&lt;/div&gt;

</content>
 </entry>
 
 <entry>
   <title>Deploying a Multi-Environment PHP site with Git</title>
   <link href="http://alexcoomans.com/blog/deploying-a-php-site-with-git"/>
   <updated>2011-05-20T00:00:00-05:00</updated>
   <id>http://alexcoomans.com/blog/deploying-a-php-site-with-git</id>
   <content type="html">&lt;p&gt;I've been working as the System Administrator/Operations Engineer/etc. for a PHP based site and I implemented a fairly simple system for deploying to different application environments.&lt;/p&gt;

&lt;!--more--&gt;


&lt;h3&gt;The Server&lt;/h3&gt;

&lt;p&gt;First, it will probably help to know a little about the server so that some of the later parts makes a little more sense. The server runs &lt;a href=&quot;http://nginx.org&quot;&gt;nginx&lt;/a&gt; to serve static files, and then proxies to &lt;a href=&quot;http://php-fpm.org/&quot;&gt;php-fpm&lt;/a&gt; to run the PHP files. For staging, it gets a little more complicated, as all requests are proxied through a central-authentication system (which I'd love to write about), but it essence is the same.&lt;/p&gt;

&lt;p&gt;Managing environments, however, isn't as simple as with Rails, mainly because Rails is engineered around the concept of environments. I didn't find anything for working with environments in PHP, so I coded up an Environment class to make all of this simpler (feel free to copy for your projects).&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;php&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Environment&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;production&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_SERVER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;SYS_ENV&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;production&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;staging&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_SERVER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;SYS_ENV&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;staging&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;development&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;production&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;staging&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;db_settings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;production&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;host&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;localhost&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;database&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;db&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;user&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;db&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;password&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;pw&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;elseif&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;staging&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;host&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;localhost&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;database&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;db-staging&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;user&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;db&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;password&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;pw&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;host&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;127.0.0.1:5000&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;database&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;db-development&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;user&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;db&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;password&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;pw&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The class simply checks against an environment variable that I set in our nginx file (shortened).&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;nginx&quot;&gt;&lt;span class=&quot;k&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;~&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;.php$&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;fastcgi_param&lt;/span&gt;  &lt;span class=&quot;s&quot;&gt;SYS_ENV&lt;/span&gt;  &lt;span class=&quot;s&quot;&gt;production&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;I also added the db_settings function so that our database configuration would be in one place, and then I simply call the function and it returns the appropriate settings, and you could copy and paste that method for other environment-specific variables.&lt;/p&gt;

&lt;h3&gt;Deployment&lt;/h3&gt;

&lt;p&gt;For this project, the team has a privately hosted git repo on GitHub, so I built a custom post-receive hook on an internal application that manages the deploys for us, and authentication is handled by HTTP Basic Authentication (hence the grayed out part of the url).&lt;/p&gt;

&lt;div class=&quot;image&quot; style=&quot;width:779px;&quot;&gt;&lt;img src=&quot;http://alexcoomans.com/blog/images/2011-5-20/github-hooks.jpg&quot; alt=&quot;GitHub Post-Receive Hooks&quot; width=&quot;779&quot; /&gt;&lt;span class=&quot;caption&quot;&gt;The GitHub Post-Receive Hooks Management Page&lt;/span&gt;&lt;/div&gt;


&lt;p&gt;The control application to which I referred to does a lot more than handle deployments, and I may open source the part that allows us to rollback deployments, but below is a simplified version of the Sinatra application. If you are unfamiliar, &lt;a href=&quot;http://sinatrarb.com/&quot;&gt;Sinatra&lt;/a&gt; is a Ruby web framework that helps you build and deploy simple applications quickly.&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;rubygems&amp;#39;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;sinatra&amp;#39;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;grit&amp;#39;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;crack&amp;#39;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;time&amp;#39;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;post&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;/github&amp;#39;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;basic_auth!&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;payload&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Crack&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:payload&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repos&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;include?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;ref&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;/&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;last&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;sb&quot;&gt;`cd &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;repos&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;ref&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;/&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;last&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gsub&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;.git&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt; &amp;amp;&amp;amp; git pull &amp;amp;&amp;amp; chown www-data:www-data -R .`&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;ok&amp;quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;kp&quot;&gt;private&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;repos&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@repos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&amp;quot;production&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Grit&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Repo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;/var/www/example.com&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&amp;quot;staging&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Grit&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Repo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;/var/www/staging.example.com&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;helpers&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;basic_auth!&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;authorized?&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;WWW-Authenticate&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;%(Basic realm=&amp;quot;Restricted Area&amp;quot;)&lt;/span&gt;
        &lt;span class=&quot;kp&quot;&gt;throw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:halt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;401&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Not authorized&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;authorized?&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@auth&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||=&lt;/span&gt;  &lt;span class=&quot;no&quot;&gt;Rack&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Auth&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Basic&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@auth&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;provided?&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@auth&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;basic?&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@auth&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;credentials&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@auth&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;credentials&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;github&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;auth&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;If you've never taken a look at the &lt;a href=&quot;http://help.github.com/post-receive-hooks/&quot;&gt;Post-Receive hook documentation&lt;/a&gt;, it simply lays out the information GitHub will POST to your server. In this case, I check to see if the push was to either the &quot;staging&quot; or &quot;production&quot; branches, and if so deploys it. I also have to perform the chown command since the web server runs under a different user than the application. It Github, you would add a URL like: http://github:auth@example.com/github to your post-receive hooks so that they can correctly authenticate. If you don't need authentication, you can simply remove that part of the Sinatra server and the github:auth@ part of the URL&lt;/p&gt;

&lt;p&gt;A git pull may not be the best command for this type of operation (I'd love to hear if something would work better), but because I know no one will have touched any of the files in those folders, I can't imagine having any problems. Also, you could change this server away from using the &lt;a href=&quot;https://github.com/mojombo/grit&quot;&gt;Grit&lt;/a&gt; library, but I have it since other parts of the application rely on those being Grit::Repo objects.&lt;/p&gt;

&lt;p&gt;When code is ready to be pushed to staging we simply merge the master branch into the staging branch and push, and when changes are ready for production, we merge in the staging branch into the production branch and push. Normally within a few seconds the changes have been updated on the server, and you can continue hacking.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Any thoughts on other environment strategies or improving this process?&lt;/strong&gt;&lt;/p&gt;
</content>
 </entry>
 
 
</feed>