Marcin's Musings

Random bits of stuff

Elastic Beanstalk Post-deployment Scripts

| Comments

Some of this is extracted from an answer I posted to a Stack Overflow question.

Recently I’ve been helping out a friend with a website and we are deploying it to AWS via their Elastic Beanstalk service. I’ve learned a few quirks of the platform in my, albeit brief, time working with it. One of these is that there’s no official way to run a “post deploy” script. You can run “Container Commands”), which are executed in your application directory BEFORE it is deployed (ie. made live), but no way to run a script AFTER your application is deployed (ie. when your application is in /var/app/current and the web server has been restarted).

Why would you need to do this?

Well, in my case I am using delayed_job as a worker for this Ruby on Rails app, and need to restart delayed_job when the application is deployed. I can’t do this before the app is live, because at that point the app is still in /var/app/ondeck and this seemed to cause problems. Also at this point the deploy could still technically fail, and you’d be left with the “old” version of the application running, but with the “new” delayed_job instance. Not good.

So when I was looking at the eb-tools.log on the EC2 instance I found it was searching for scripts to run in a /opt/elasticbeanstalk/hooks/appdeploy/post directory after restarting the web server. Turns out if you drop shell scripts into this directory they will be executed post deployment, just like you want! However this directory does not exist by default, as Amazon does not use any post-deploy scripts, so we also need to make sure this directory is created.

You can use the regular config YAML files that go in your .ebextensions folder in order to deploy a shell script that will be run after the deployment is complete.

This is my 99delayed_job.config:

    command: "mkdir /opt/elasticbeanstalk/hooks/appdeploy/post"
    ignoreErrors: true
    mode: "000755"
    owner: root
    group: root
    content: |
      #!/usr/bin/env bash
      . /opt/elasticbeanstalk/support/envvars
      su -c "RAILS_ENV=production script/delayed_job --pid-dir=$EB_CONFIG_APP_SUPPORT/pids restart" $EB_CONFIG_APP_USER

What this config does is:

  • create the “post” directory if it doesn’t already exist (it won’t by default) – ignore any errors (such as if the directory already existed)
  • deploy the shell script with the appropriate permissions into the right directory

What the shell script does is:

  • setup the environment variables (I borrowed this line from the other shell scripts in /opt/elasticbeanstalk)
  • change to the “current” directory – this will probably be /var/app/current but we don’t make any assumptions
  • restart delayed_job as the app user (usually webapp), using a shared pids directory to make sure it kills the old version first

So after commiting the config file and doing a git aws.push you should see something like this in your /var/log/eb-tools.log:

2013-05-16 01:20:53,759 [INFO] (6467 MainThread) [] [root directoryHooksExecutor info] Executing directory: /opt/elasticbeanstalk/hooks/appdeploy/post/
2013-05-16 01:20:53,760 [INFO] (6467 MainThread) [] [root directoryHooksExecutor info] Executing script: /opt/elasticbeanstalk/hooks/appdeploy/post/
2013-05-16 01:21:02,619 [INFO] (6467 MainThread) [] [root directoryHooksExecutor info] Output from script: delayed_job: trying to stop process with pid 6139...
delayed_job: process with pid 6139 successfully stopped.
2013-05-16 01:21:02,620 [INFO] (6467 MainThread) [] [root directoryHooksExecutor info] Script succeeded.

As I said, putting stuff in this “post” directory is undocumented – but hopefully at some point Amazon add actual support to the .config scripts to run commands post-deploy, in that case you could just move the restart comamnd itself to the officially supported approach.