Building simplicidade.org: notes, projects, and occasional rants

Easy staging server setup

Before a new release of a web site, I like to give it a whirl using the production environment, including servers and database. To make the experience of the staging server as close as possible to the production server, I want to use the same hardware and server name for both.

At first I used different IPs, aliased to the same server, one for production and another for staging, and I used my local /etc/hosts file to switch between the two environments. This works but its a pain, because I have to restart the browser to pick up the new address, and also, I have to do this on every computer that wants to check the new version.

The new setup is much better.

It uses two application servers, production and staging, running on the same server, on different ports, and then use the front-end Lighttpd reverse proxy to select between the two instances based on a cookie.

The relevant part of the Lighttpd configuration is this:

# Staging server
$HTTP["cookie"] =~ "(; )?app_version=staging:" {
  proxy.server  = (
    "" => (
      ( "host" => "127.0.0.1", "port" => 7005 )
    )
  )
}

# Production server
$HTTP["cookie"] !~ "(; )?app_version=staging:" {
  proxy.server  = (
    "" => (
      ( "host" => "127.0.0.1", "port" => 7000 )
    )
  )
}

This setup uses a cookie named app_version. If app_version is set to staging, Lighttpd will use the application server at port 7005. If not, it will fallback to port 7000. This works even if we don't have any cookie.

Currently, only internal users use the staging server, so I have an option in the back-office site to select the version they wants to use.

You should notice that I don't do any security checks on the cookie, so anybody could set the cookie and use the staging version. To prevent this, my app_version cookie includes a SHA1 checksum of the content using a secret string (so its really app_version=staging:40-char-hex-sha1-checksum). Yet, I don't check the SHA1 in the front-end proxy, that would be wasteful of CPU for the vast majority of users using the production server. Instead, I have optional code to validate the checksum in the main application server. That code is only enabled on the staging version and displays an error message.

The final problem I needed to solve is identification of the current server. People got confused, they didn't had a clear indication if they where using the staging or production server. To solve that, I just enable a small HTML '<div />' and appropriate CSS in the staging server, and this will float a nice "You are using the staging server. Switch to Production"-message. The "Switch to Production" part is a link to the back-office tool that removes the cookie.

With these things in place, I can push release candidates to staging without worrying to much about it, have the other people at the office try it out, and then if all is ok, run a script on the main server and deploy the staging version to production.

Update: this also helps for rolling updates. For example, you could use this setup to keep two production versions available, and use the login process to move users from the old version to the new one.