Update 2022: A lot has changed since this was written. Use Docker instead.

In the beginning I used to do a lot of server setup. Every project would require that I do some sort of setup, then do it again for production. As projects became more complex the differences between what I was developing on and what I would eventually host my projects on became greater. So much so that I would end up spending an incredible amount of time duplicating much of the effort I would make getting my development environment up and running.

To that end I decided to make things simpler. Keep my environments close to each other as possible so that I could spend much more time developing cool functionality. To that end I started building I moved development to virtual machines and now if I need a clean slate to start from, I just clone my base VM and start from a point where everything works the way its supposed to.

To that end, I wanted to update my “base” VM and decided it was a good time to write down what exactly I did to it. Now for 24-ish easy steps!

  1. Install Parallels, VMWare, or VirtualBox. I personally use Parallels, but I’ve used all three at some point in my career. For me Parallels works really well for my workflow.

  2. Download CentOS (Version 6.2 at this time) and install. There are several options.

    1. Download minimal ISO (this walkthough assumes minimal)
    2. Download LiveDVD
    3. Download LiveCD
    4. Download Netinstall

    The minimal is very quick to download and will allow us to only install that which we need. If you want a GUI, download one of the Live options. I don’t really see a benefit to the Netinstall for this walkthrough.

    The process for installing a VM using Parallels is pretty straight forward. If you are using the minimal ISO, just go through the process. There is the super-scary step where it formats your hard drive and warns about losing all your data, just plow on through, its sandboxed in your VM container. If you’re installing in VM its not getting rid of anything.

  3. Login as root

  4. Enable networking.

    Throughout this walkthrough I will be using vi as my editor. Feel free to use whatever you wish, emacs, joe, kate, etc.

    1. Disable IPV6 :

      vi /etc/systconfig/network
      

      add the line

      NETWORKING_IPV6=no
      
    2. Enable DHCP (for now) :

      vi /etc/sysconfig/network-scripts/ifcfg-eth0
      

      add the line

      BOOTPROTO=dhcp
      
    3. Or if you want to jump straight to static routing :

      vi /etc/sysconfig/network-scripts/ifcfg-eth0
      

      add the following lines replacing with their proper values the omitting < and > symbols.

      BOOTPROTO=none  
      GATEWAY=<ipaddress>  
      NETMASK=<ipaddress>  
      IPADDR=<ipaddress>  
      USERCTL=false  
      PEERDNS=yes  
      DNS1=<ipaddress>  
      DNS2=<ipaddress>
      
    4. Save the file and restart

      /etc/init.d/network restart
      
  5. Update currently installed packages :

    yum update
    

    At this point you should have a updated vanilla CentOS 6 install.

  6. Create admin user that is part of the admin group :

    groupadd admin  
    useradd -Gadmin <username>
    
  7. Install sudo :

    yum install sudo
    
  8. Add admin group to the sudoers file

    visudo
    

    Add the following line to the end of the file :

    %admin ALL=(ALL) ALL
    
  9. I prefer to only use ROOT when absolutely necessary. You can continue to use ROOT for the remainder of this walkthrough if you wish, you will just need to remove the sudo command at the beginning of each command that requires elevated permissions. Log out of root and back in as your admin user.

    (OPTIONAL) At this point, you should also be able to ssh into your VM server. So if you would like to configure public key authentication, this is where I would do it. Then SSH into the server and continue the walkthough.

  10. Install development packages :

    sudo yum install make automake gcc gcc-c++ man man-pages
    
  11. Install packages specific to the type of development you will be doing. In my case this will be specifically Python/Django related packages as well as web serving technology. Your packages may vary.

    sudo yum install python-devel python-setuptools openssl openssl-devel zlib zlib-devel git libjpeg libjpeg-devel freetype freetype-devel lcms lcms-devel python-lcms python-imaging python-imaging-devel libxml2 libxml2-devel
    
  12. Install your webserver, in this case, Nginx. CentOS does not offer the latest version of Nginx in its repositories so you need to add the repository that does, in this case the EPEL repo hosted by Fedora as well as the Nginx repo. You man not need the EPEL repo, but I like having it available. If you are not using CentOS 6.2 the following URL may be different.

    sudo rpm -Uvh http://download.fedoraproject.org/pub/epel/6/i386/epel-release-6-5.noarch.rpm  
    sudo rpm -Uvh http://nginx.org/packages/centos/6/noarch/RPMS/nginx-release-centos-6-0.el6.ngx.noarch.rpm
    
    sudo yum install nginx-release-centos.noarch  
    sudo yum install nginx
    

    Start nginx

    /etc/init.d/nginx start
    
  13. Configure basic firewall

    See what your current firewall configuration looks like.

    sudo iptables --line-numbers -n -L
    

    Add a rule to allow port 80 through. In this command I am inserting the rule using -I in position 2.

    sudo iptables -I INPUT 2 -m state --state NEW -p tcp --dport 80 -j ACCEPT
    

    Save the rules

    sudo service iptables save
    

    Check to see if Nginx is running properly by going to the IP of your server in a web-browser.

  14. Install Memcached

    sudo yum install libevent-devel memcached  
    sudo service memcached start  
    sudo chkconfig memcached on
    
  15. Install MySQL

    sudo yum install mysql mysql-server mysql-devel  
    sudo chkconfig mysqld on  
    sudo service mysqld start
    

    Secure your MySQL installation by setting a root password
    and cleaning up the default DBs.

    sudo /usr/bin/mysql_secure_installation
    
  16. Prepare your Python environment. CentOS 6.2 comes with Python 2.6.6. I could just live with that, but I’ve been using 2.7 fairly extensively and I like to keep everything the same across all my development platforms. I also like to keep the standard Python executable intact so that I don’t accidentally make a change that has a far-reaching effect that I did not foresee and have to debug later. For that reason, I install any updated Python versions side-by-side with the system default. You can install a different version without overriding the “active version” symlink using the altinstall command.

    curl -O http://www.python.org/ftp/python/2.7.2/Python-2.7.2.tgz  
    tar xvf Python-2.7.2.tgz  
    cd Python-2.7.2  
    ./configure  
    make
    
    # Using "install" here will override your the python symlink
    # in /usr/local/bin. Depends on what you want your default 
    # version of Python to be.
    
    sudo make altinstall
    
  17. Install pip, virtualenv and, virtualenvwrapper

    sudo easy_install pip  
    sudo pip install virtualenv  
    sudo pip install virtualenvwrapper
    
  18. Set up virtualenvwrapper. Virtualenvwrapper needs a few settings to work properly. Set these variables in your ~/.bash_profile replacing the <values>.

    export VIRTUALENV_USE_DISTRIBUTE=True  
    export WORKON_HOME=</path> export PROJECT_HOME=</path> source /usr/bin/virtualenvwrapper.sh
    

    Log out then in again, or source ~/.bash_profile to have these changes recognized. WORKON_HOME is the directory your virtualenvs will live and PROJECT_HOME is where your Django projects will live.

  19. At this point you should be able to create a new virtualenv. The mkvirtualenv command will create a virtualenv in your WORKON_HOME

    mkvirtualenv -p </path> <virtualenv>
    

    It should be activated at this point. If you are coming back to this walkthrough and it is not active, you can simply activate it with the command:

    workon <virtualenv>
    
  20. Install Python packages using Pip

    If you’re virtualenv is not active you can install you python modules into it anyway using the -E flag:

    pip install <python_module_name>
    

    If it is not active, then you can add -E <virtualenv> to the previous command:

    pip install -E <virtualenv> <python_module_name>
    

    Repeat these steps until you have installed all your requirements. You can also create a requirements file which contains a list of modules you wish to install. A requirements file is a simple text file with one module on each line. Please see the Pip documentation for details on requirement files. Use the -r flag to have pip use it.

    A requirements file might look like this:

    # requirements.txt  
    Django==1.3.1  
    MySQL-python==1.2.3  
    python-memcached==1.48  
    uWSGI==1.0.4
    

    Save, then:

    pip install -r /path/to/requirements.txt
    
  21. Install the Python Imaging Library. Normally I would just use pip to install this, but since yum installs the libraries PIL depends on in a place the installer doesn’t look, some modifications need to be made to the installer.

    wget http://effbot.org/downloads/Imaging-1.1.7.tar.gz  
    tar xvzf Imaging-1.1.7.tar.gz  
    cd Imaging-1.1.7  
    vi setup.py
    

    Update the library pointers to know where to look for the libraries we installed earlier. This assumes you are using an x86_64 build. If not, use /usr/lib instead. This should start right around line 35.

    TCL_ROOT = None  
    JPEG_ROOT = "/usr/lib64"  
    ZLIB_ROOT = "/usr/lib64"  
    TIFF_ROOT = "/usr/lib64"  
    FREETYPE_ROOT = "/usr/lib64"  
    LCMS_ROOT = "/usr/lib64"
    

    Save, then build and install

    python setup.py build  
    python setup.py install
    
  22. Install and configure uWSGI

    pip install uwsgi
    

    Now configure uWSGI
    Configure WSGI. Create a file called wsgi.py in the root of your Django project and put the following in it. Updating it for your site by replacing the <values>:

    SITE_DIR = '</path>'  
    import site  
    site.addsitedir(SITE_DIR)
    
    import os  
    import sys  
    sys.path.append(SITE_DIR)
    
    os.environ['DJANGO_SETTINGS_MODULE'] =
    '<your_django_project>.settings'  
    import django.core.handlers.wsgi  
    application = django.core.handlers.wsgi.WSGIHandler()
    

    Create another file in your Project root called uwsgi_conf.ini

    vi uwsgi_conf.ini
    

    Insert the following, changing the <values> for your own.

    [development]  
    uid = www-data  
    socket = /tmp/uwsgi_gasc.sock  
    chmod-socket = 644  
    pythonpath = /path/to/<your_project>  
    pythonpath = /path/to/ # The directory holding your project  
    virtualenv = /path/to/<virtualenv>  
    master = true  
    processes = 4  
    env = DJANGO_SETTINGS_MODULE=<your_project>.settings  
    module = django_wsgi  
    logdate = true  
    harakari = 120  
    optimize = 2  
    logto = /path/to/<logfile>.log  
    chdir = /path/to/<your_project>
    
    # You don't need the following in your ini file, but this is where you can make multiple settings for different deployments.
    
    ; [staging]
    
    ; [production]
    

    Daemonize uWSGI with Upstart:

    sudo touch /etc/init/uwsgi_<your_django_project_>.conf  
    sudo vi /etc/init/uwsgi_<your_django_project_>.conf
    

    Insert the following changing out <your_django_project>:

    # file: /etc/init/uwsgi_<your_django_project>.conf  description "uWSGI server for <your_django_project>"
    
    start on runlevel [2345]  
    stop on runlevel [!2345]
    
    respawn
    
    # This is where you stipulate which set of settings in your ini file you will use.  
    # You could specify "production" at the end of this command instead of "development" to use a different configuration setup in your ini file.  
    exec /path/to/<virtualenv>/bin/uwsgi --ini
    /path/to/uwsgi_conf.ini:development
    

    Add a dedicated system user to run uWSGI:

    sudo useradd -M -r --shell /bin/sh --home-dir /opt/www-data www-data
    

    Reload Upstart and start uWSGI:

    sudo initctl reload-configuration  
    sudo start uwsgi_<your_django_project>
    
  23. Setup NginX

    sudo vi /etc/nginx/uwsgi_params # it may already exist
    

    Put the following in it:

    uwsgi_param QUERY_STRING $query_string;  
    uwsgi_param REQUEST_METHOD $request_method;  
    uwsgi_param CONTENT_TYPE $content_type;  
    uwsgi_param CONTENT_LENGTH $content_length;
    
    uwsgi_param REQUEST_URI $request_uri;  
    uwsgi_param PATH_INFO $document_uri;  
    uwsgi_param DOCUMENT_ROOT $document_root;  
    uwsgi_param SERVER_PROTOCOL $server_protocol;
    
    uwsgi_param REMOTE_ADDR $remote_addr;  
    uwsgi_param REMOTE_PORT $remote_port;  
    uwsgi_param SERVER_PORT $server_port;  
    uwsgi_param SERVER_NAME $server_name;
    

    Then create the nginx conf file for your project:

    sudo vi /etc/nginx/conf.d/<your_app_name>.conf
    

    Insert the following:

    server{  
        listen 0.0.0.0:80;  
        server_name www.<your_address>.com <your_address>.com;  
        access_log /path/to/access/log.log;  
        error_log /path/to/error/log.log;
    
        location /site_media {  
    
         # Point this wherever the static files for your django app are stored
    
         (i.e. MEDIA_ROOT)  
         alias /opt/apps/<your_app_name>-env/site/media;  
        }
    
        location / {  
         uwsgi_pass unix:///tmp/uwsgi_<your_app_name>.sock;  
         include uwsgi_params;  
        }
    
    }
    

    Edit /etc/nginx/nginx.conf. Change the user from nginx to www-data

    sudo vi /etc/nginx/nginx.conf
    

    Restart Nginx and make sure it comes back at reboot:

    sudo /etc/init.d/nginx restart  
    chkconfig nginx on
    
  24. That’s it! Load your site up in browser and crack a beer.