standard stack v1: git


we’ll use git to facilitate the process of pushing code to the vm.  because there’s a cardinal rule about not serving files from a repo, we’ll need to create a git host and use a githooks to update the web root when code is pushed to the repo.  i’m using the terms hub and prime introduced by Joe Maller in his post A web-focused Git workflow.

i don’t have a cool picture of the concept, like Maller did, but here’s one of a cute red panda (credit: tambako) to set the mood before we get started:

ok, here we go:


  • prime is the copy of the repo accessible by the web server
  • hub is the bare source of truth repository
  • project refers to the prime/hub pair
  • vm is the vmware vm running centos
  • laptop is the development computer you ultimately want to push files from


  • mac os x 10.5.8
  • vmware 2.0.5
  • centos 5.4
  • git


  1. set up
    1. on the vm, install git as root:
      yum install git
    2. on the vm, create a user to handle git-related activity:
      useradd git
    3. on the vm, get its inet ip address using ifconfig:
    4. on the vm, copy your rsa public key (you’ll be pushing git updates over ssh) from your laptop into the git user’s .ssh/authorize_keys file on the vm
    5. on the vm, make sure the correct permissions are set on the authorized_keys file and .ssh dir:
      chmod 700 /home/git/.ssh; chmod 644 /home/git/.ssh/authorized_keys
    6. on your laptop, run a sanity check by logging into the vm via public key. note: if you’re using an alternate ssh port and/or different pub key file name, define these in your laptop‘s .ssh/config file:
      ssh git@{ip address}
    7. on the vm, in /var/www/, as root, create a directory that git can push content to (note: if the dir isn’t owned by git or isn’t world-writable, git throws an “error: cannot open .git/FETCH_HEAD: Permission denied” error):
      mkdir /var/www/git/; chown git:git /var/www/git/
    8. on the vm, cd into the /var/www/git/ directory and su to the git user:
      cd /var/www/git/; su git
  2. create a new project
    1. on the vm, create a new directory {proj name} for the prime repo and cd into it:
      mkdir proj; cd proj
    2. on the vm, initialize a git repo:
      git init
    3. on the vm, create and add a file so we can clone prime later (git dissallows cloning an empty repo):
      touch readme;
      git add readme;
      git commit -m ‘initial commit’

      Note: if you haven’t already told git who you are, run:
      git config “”
      git config “”
    4. on the vm, define a remote repository for the soon-to-be-created hub:
      git remote add origin /home/git/proj
    5. on the vm, cd into git user’s home directory:
      cd ~
    6. on the vm, create the hub repo by cloning the newly created repo using the –bare flag (that’s a double ‘-‘ before bare):
      git clone –bare /var/www/git/proj
    7. on the vm, create a post-update hook in the hub repo to update the web directory when an update is pushed.  open /home/git/proj/hooks/post-update and add the following:
              # jump into web dir
              cd /var/www/sites/
              # w/o this, git throws "fatal: Not a git repository: '.'" error
              # ref:
              unset GIT_DIR
              # pull in the updates
              git pull origin master
  3. start working
    1. on the laptop, open a terminal on whatever machine your going to develop on and clone the new host repo:
      git clone git@{ip address}:proj
    2. on the laptop, edit the readme file in the repo, check in the change and observe in the output the results of the hook-initiated pull
    3. on the laptop, view http://{ip address}/readme to confirm the new code is displaying


setting up nginx and mochiweb on centos 5

  1. Install nginx on centos using cyberciti’s tutorial
  2. update default iptables to allow http traffic:
    # ref:
    # ref:
    # Firewall configuration written by system-config-securitylevel
    # Manual customization of this file is not recommended.
    :INPUT ACCEPT [0:0]
    :OUTPUT ACCEPT [0:0]
    :RH-Firewall-1-INPUT - [0:0]
    -A INPUT -j RH-Firewall-1-INPUT
    -A FORWARD -j RH-Firewall-1-INPUT
    -A RH-Firewall-1-INPUT -i lo -j ACCEPT
    -A RH-Firewall-1-INPUT -p icmp --icmp-type any -j ACCEPT
    -A RH-Firewall-1-INPUT -p 50 -j ACCEPT
    -A RH-Firewall-1-INPUT -p 51 -j ACCEPT
    -A RH-Firewall-1-INPUT -p udp --dport 5353 -d -j ACCEPT
    -A RH-Firewall-1-INPUT -p udp -m udp --dport 631 -j ACCEPT
    -A RH-Firewall-1-INPUT -p tcp -m tcp --dport 631 -j ACCEPT
    -A RH-Firewall-1-INPUT -m tcp -p tcp --dport 80 -j ACCEPT
    -A RH-Firewall-1-INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
    -A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT
    -A RH-Firewall-1-INPUT -j REJECT --reject-with icmp-host-prohibited
  3. install mochiweb using BeeBole’s tutorial.  For ease of use while testing, launch dev server using separate screen, as the mochiweb shell will own the terminal used to launched it by default, and add the following line to iptables so we can hit the server directly:
    -A RH-Firewall-1-INPUT -m tcp -p tcp --dport 8000 -j ACCEPT # allow access to mochiweb

    Test that mochiweb is available to localhost by running the following from the command line on the server:


    You should get something back like:

    <title>It Worked</title>
    MochiWeb running.

  4. Configure nginx to proxy api calls to mochiweb.  Put this in /etc/nginx/nginx.conf:
    user              nginx;
    worker_processes  1;
    error_log         /var/log/nginx/error.log;
    pid               /var/run/;
    events {
        worker_connections  1024;
    http {
        include       /etc/nginx/mime.types;
        default_type  application/octet-stream;
        log_format  main  '$remote_addr - $remote_user [$time_local] $request '
                          '"$status" $body_bytes_sent "$http_referer" '
                          '"$http_user_agent" "$http_x_forwarded_for"';
        access_log  /var/log/nginx/access.log  main;
        sendfile        on;
        keepalive_timeout  65;
        include /etc/nginx/conf.d/*.conf;
        server {
            listen       80;
            server_name  localhost;
            location ~ api { # <-- pass requests for 'api...' to mochiweb
            location / {
                root   /usr/share/nginx/html;
                index  index.html index.htm;
            error_page  404              /404.html;
            location = /404.html {
                root   /usr/share/nginx/html;
            error_page   500 502 503 504  /50x.html;
            location = /50x.html {
                root   /usr/share/nginx/html;

    As per BeeBole’s tutorial, edit the mochiweb request handler to handle requests for ‘api’:

    %% @author author <>
    %% @copyright YYYY author.
    %% @doc Web server for myapp.
    -author('author <>').
    -export([start/1, stop/0, loop/2]).
    %% External API
    start(Options) ->
        {DocRoot, Options1} = get_option(docroot, Options),
        Loop = fun (Req) ->
                       ?MODULE:loop(Req, DocRoot)
        mochiweb_http:start([{name, ?MODULE}, {loop, Loop} | Options1]).
    stop() ->
    loop(Req, DocRoot) ->
        "/" ++ Path = Req:get(path),
        case Req:get(method) of
            Method when Method =:= 'GET'; Method =:= 'HEAD' ->
                case Path of
                    "api" -> Req:ok({"text/html", [],["<h1>Congratulation</h1>"]}); % <-- the 'api' request handler
                    _ -> Req:serve_file(Path, DocRoot)
            'POST' ->
                case Path of
                    _ ->
            _ ->
                Req:respond({501, [], []})
    %% Internal API
    get_option(Option, Options) ->
        {proplists:get_value(Option, Options), proplists:delete(Option, Options)}.

    As per James Gardner’s post Streaming File Upload with Erlang and Mochiweb Multipart Post, rebuild the request handler by running make in the myapp directory. The mochiweb server will automatically restart

  5. confirm the proxy is working by hitting http://domain/ and http://domain/api.  The former should return the nginx install confirmation page, and the latter should return the simple “Congratulation” page.

attempt to restart/stop yaws failed

I’ve got yaws (git hash 5f35f5b7451ea4388c53df9f4e00caad0caa6b45) running on CentOS 5.3.  I just added a virtual host entry in yaws.conf and tried to restart yaws, but the restart failed:

[me@mymachine /]$ sudo etc/init.d/yaws restart
Stopping yaws:                                             [FAILED]

After hunting around a bit, it seems yaws will fail to restart (and stop) if the docroot doesn’t point to an actual directory.  In my case, I added the virtual host entry before actually creating the docroot directory.  Making the directory fixed the problem.

tutorial: fix “iptables: command not found” error on CentOS 5.2

Goal: enable us to use the iptables command in the terminal



  • CentOS 5.2
  • vi
  • bash


  1. Add /sbin to your path by editing your ~./bash_profile: user $ vi ~/.bash_profile
  2. Append /sbin to the PATH definition.  In my file, this looks like: PATH=$PATH:$HOME/bin –> PATH=$PATH:$HOME/bin:/sbin
  3. Save and close ~/.bash_profile
  4. Reload ~/.bash_profile: user $ source ~/.bash_profile

tutorial: “visudo: command not found”

screenshot: visudo: command not found


Install visudo on CentOS 5.2


We need visudo to edit the “sudoers” file.  We want to edit this file to enable users in the “wheel” group to conjure sudo (as described on the Slicehost site.  I’ve never known visudo to be unavailable, but, for whatever reason, it was absent from a fresh install of CentOS on my host.


It could be that it’s in a directory that’s not in your PATH, but this was not my problem.  Instead, I needed to install sudo.  Doesn’t that just sound weird?  Why wouldn’t sudo be installed?  Anyways, the fix was simple:

yum -y install sudo

tutorial: building an OpenSSH chroot jail on CentOS 5.3


allow users to log into a CentOS 5.3 server via ssh, but then constrain their mobility by using the chroot support introduced in OpenSSH 4.8p1.






I followed the tutorial in resource 2 for the most part, but the rpm build will fail with an error if the “/usr/src/redhat/RPMS/i386” and “/usr/src/redhat/BUILD” directories are not made in advance, a step in resource 1.  

With these two tutorials, I was able to build and install OpenSSH 5.1, but then I ran into a couple hiccups.  When I tried to log in using a dummy account (“random1”) assigned to the “sshusers” group described in resource 1, ssh rejected my log in with an error: “Permission denied (publickey,gssapi-with-mic).”.  Looking in the ssh logs (“/var/log/secure”), I saw: “Authentication refused: bad ownership or modes for file “/home/random1/.ssh/authorized_keys”.  Referring to the details provided in resource 3, I changed the permissions on “random1/.ssh” and “random1/.ssh/authorized_users” to 700.  Then I was able to ssh in, but immediately I received the error “/bin/bash: No such file or directory”, and was bounced out.  I moved the ls and bash executables placed in “/usr/bin” in resource 2, to “/bin” and then all was good 🙂

Special thanks to robbyt, author of resource 1, for his assistance.

installing Apache MySQL PHP on a CentOS 5.2 VMWare image

install apache and php

  • ref:
  • install apache w/ ssl support:
    sudo yum install httpd mod_ssl
  • launch apache:
    sudo /etc/init.d/httpd start
  • browse to your vm’s address (run ifconfig in the vm if you don’t know the address)
  • if your browser can’t find the address, write an iptable rule to allow access to port 80:
    • ref:
    • open the iptable definition file:
      vi /etc/sysconfig/iptables
    • plug in the new rule:
      -A RH-Firewall-1-INPUT -m state –state NEW -m tcp -p tcp –dport 80 -j ACCEPT 
    • note: the leading ‘-‘ in the rule is intentional
    • restart the iptables process:
      /etc/init.d/iptables restart
  • install php:
    sudo yum install php-common php-gd php-mcrypt php-pear php-pecl-memcache php-mhash php-mysql php-xml
  • reload apache:
    sudo /etc/init.d/httpd reload

set up mysql

upgrade beyond stock centos support so we can get php version > 5.2.2, which is required for phpmyadmin

install phpmyadmin

related post: running a CentOS 5.2 server using VMWare on Mac OS X 10.5

running a CentOS 5.2 server using VMWare Fusion on Mac OS X 10.5


  • An installation of VMWare Fusion Version 1.1 (62573)
  • A Mac w/ OS X 10.5.2 installed


  1. get CentOS
    a) Go to CentOS’s site and navigate to a download of this first install disk, e.g., “Downloads” -> “CentOS-5 ISOs” -> “i386” -> “” -> “CentOS-5.3-i386-bin-1of6.iso”.  I downloaded the torrent “CentOS-5.3-i386-bin-1to6.torrent”, but deselected all except the torrent for disk 1, i.e., it says “1to6”, but you can specify any or all of them, and we only need the first one.
  2. Once the iso is downloaded, launch VMware and create a new virtual machine
    a) Launch the wizard by clicking “File” -> “New…”
    b) Select “Linux” and “Red Hat Enterprise Linux 5” for the operating system and version, respectively, and click “Continue”
    c) Accept the defaults for name and location and continue
    d) Accept the default size for the virtual hard disk, e.g., 8GB, and continue
    e) Leave “Start virtual machine and install …” checked, select “Use operating system installation disk image file”, browse to the location of the iso you downoaded in step 1, and click “Finish”
  3. VMWare will now boot CentOS off the install disk.  Follow this guide ( ) regarding the configuration details of a bare-bones server installation.  Once the installation is complete, you’ll be prompted to reboot and, ba-boom, you’ll have a CentOS virtual machine running on your Mac.

possible next steps