Heroku's deployment model has always had a single point of frustration - the requirement to take the site offline, if only for a couple of minutes. In certain circumstances this is solved by the 'preboot' labs feature.
I love Heroku's simple git deployment model, and having lived with it for almost a year, I can't imagine deploying any other way. There is, however, one persistent niggle. Because of the way in which Heroku works the standard process involves taking the site offline as the most recent deployment is shutdown and restarted (a new application 'slug' is generated and stored).
Coming from a corporate world, I was used to having applications deployed into managed data centres, with total control over the process. In the past we would make a great effort to prevent site downtime, and would generally deploy code to multiple servers in a rolling fashion (take server out of load balancer, deploy, re-engage), ensuring that the site was always being served. On a single server this could be replicated by pushing the latest code to a new folder in the background, and then updating a symlink to point the web server to the new folder.
Today I came across an old post that references the Heroku labs 'preboot' feature. It looks like it's been out some time, but I never got the memo, and it solves the zero downtime problem. It basically does what I've described above - it keeps the current application running, creates the new application slug in the background, and then just (!) updates the Heroku routing infrastructure to point to the new app rather than the old app. No more maintenance page!
This has enabled us to be a lot more aggressive in our deployments, as we don't need to base deployments around quiet times.
NB this does come with a fairly obvious (I hope) warning - it only really works with code-only deployments. If you are running database migrations, or pushing static content to a CDN as part of your deployment, then using preboot will get you in all sorts of trouble, with the new code pointing at the old database, etc.
We've managed to work around this fairly easily by adjusting our Fabric deployment script. We use a system of bitwise 'options' flags to determine which of the following actions to perform whilst deploying:
- Push the code (this is mandatory!)
- Run database migrations
- Load fixtures
- Push static content to CDN
We have updated the script to use preboot by default if (and only if) the only option selected is 'push code'. If any other options are selected, then preboot is off, and the old maintenance page is back on.
$ fab heroku_deploy_app:uat,options=1
Deploying git branch 'master' to 'xxxxx'
Target environment: uat
Git branch: master
Git remote: email@example.com
Force push: False
Heroku app: xxxxx
 Git push: True
 Syncdb: False
 Migrations: False
 Load fixtures: False
 Collect static: False
Please type '770601' at the prompt to continue:
Works a treat, thank you team Heroku.