Of course I have a backup!

Random blobs of wisdom about software development

Getting a PHP app running on Heroku

Sunday, September 16, 2012

We were looking for an easy to set up hosting solution for our new PHP application. A colleague mentioned that we should try Heroku, since they are praised everywhere. I can agree, I don't even know what it is, but I have already seen it praised on various blogs.

I started reading up on what exactly Heroku is, taken from their homepage:

Heroku (pronounced her-OH-koo) is a cloud application platform – a new way of building and deploying web apps. Our service lets app developers spend 100% of their time on their application code, not managing servers, deployment, ongoing operations, or scaling.

Huh. Alright, let's look around more. Oh, there is a how it works page, with cool pictures, let's click through that. I still don't get it. The whole how-it-works page seems very well made, and call me slow, but I still don't get what exactly is this thing. Since I already went through with deploying an app to it, I'm going to sum it up, in my own words:

Heroku is like a server, that is managed by a third party. The whole architecture (PHP version, web server, etc.) is managed by them. You get a git repository address, that you can push code into, and each time you push, it will deploy your code automatically. You can also install a CLI client on your own machine, that you can use to interact with your remote application.

That would have been something that could have made it click it for me right away, but maybe I'm just slow. Heroku doesn't really advertise PHP as a supported language, I'm not sure why, but a little googling tells me that the whole reason PHP support was born, is because of some kind of agreement between them, and Facebook, but take this with a grain of salt.

The problems I encountered

When I first tried to push code onto the remote heroku repo, I was greeted with:

-----> Heroku receiving push
 !     Heroku push rejected, no Cedar-supported app detected

This was easily googled, and the reason for this is that my application had no index.php in the root folder. In order for Heroku to identify your application as a PHP app, the there must be a index.php, in the root folder. This is a major bad practice, the document root should only contain the public facing files, otherwise you will have to resort to blacklisting every directory that you don't want to be world-readable, and blacklisting is usually a bad choice when it comes to security. Our app was made in Symfony, and the index.php was in a folder called "web". Actually, it wasn't even named index.php (which is something I don't get in Symfony).

Using a dummy index file does the job, so I placed an empty index.php in the root. Next step, is to get Heroku to somehow recognize our web/ folder as the document root. I found a way to hook into the deployment process of Heroku, using a file called Procfile. I got this tip from a Github repo, that I no longer seem to find, but basically you can specify a shell script in your Procfile, which instructs Heroku how to boot your application. I created a file named Procfile in the root, that had a single line:

web:    sh www/config/web-boot.sh

This is important, because I think this is the only place (the shell script) where you can actually make permanent changes, that will get propagated to all the nodes (heroku distributes your code to many nodes). In case you are wondering, /www/ is the folder where the files you push to the git repository get deployed. The procfile is ran relative to /app.

So, let's take a look at web-boot.sh:

echo "Include /app/www/config/httpd/*.conf" >> /app/apache/conf/httpd.conf
touch /app/apache/logs/error_log
touch /app/apache/logs/access_log
tail -F /app/apache/logs/error_log &
tail -F /app/apache/logs/access_log &
export LD_LIBRARY_PATH=/app/php/ext
export PHP_INI_SCAN_DIR=/app/www
echo "Launching apache"
exec /app/apache/bin/httpd -DNO_DETACH

It will place a single line in Apache's conf, which instructs it to include every *.conf file in our applications 'config/httpd' folder. So, I created a virtual host, in our applications config/httpd/default.conf, and pushed it to the git repository:

DocumentRoot "/app/www/web"
<Directory "/app/www/web">
    Options Indexes FollowSymLinks
    AllowOverride All
    Order allow,deny
    Allow from all
</Directory>

This solves the DocumentRoot issue. I'm not sure what exactly the tail -f lines do, but I guess you need to have open file descriptors on them, in order to make the "heroku logs" command display them.

CLI tasks

Symfony includes a very useful console interface, that you can use to clear caches, generate your DB from your schema files, and so on. I needed to do the latter. I tried running heroku /app/bin/php /app/www/symfony propel:build-all, which is supposed to generate the database, but it failed with an error:

/app/bin/php: error while loading shared libraries: libmcrypt.so.4:
cannot open shared object file: No such file or directory

This references a libmcrypt.so file, which is a PHP module. So, I tried finding where the php.ini file is located on Heroku's server, because I can probably mess around with that using sed/grep/awk in the Procfile, in the same way as I did with httpd.conf. Using "heroku run bash" gives you a pretty much fully functioning shell, and running a "find -name 'php.ini', revealed that it is under /app/php/php.ini.

grep 'libmcrypt' /app/php/php.ini returns:

; Default: Compiled in into libmcrypt (usually /usr/local/lib/libmcrypt)

Which is a comment. The libmcrypt.so.4 file that the error message references is not even enabled in php.ini, which leads me to believe that it is the actual linux libmcrypt package that is broken on the Heroku installs, so I did not chase this further. I went ahead and and loaded the database manually, but this a major pita. When the application goes live, doing every database change by hand, then checking the database if they actually went in correctly, is a major source of errors, we really need the symfony console to automate this.

This was the point where I gave up. There are a few other things that I would have needed to figure out, like how to move the cache into memcache or something else (by default Symfony caches into the filesystem, which is a no-no if your app is distributed to many servers), or how to install PHP extensions.

In the end we went with a VPS, but doing some Google searches now, reveals more results, like there is an article that describes how to get a Symfony2 app running, that advocates compiling PHP on Heroku for the various modules you might need, and there is also a PHP Buildpack, which I have been told is a good way to get PHP running, but I'm not entirely sure what it is.

This wasn't such a surprise, the reason they don't really advertise PHP support anywhere is probably because it's still in a pretty early stage. If your application is simpler/smaller, I still think Heroku might be a good choice, and it will probably evolve as time goes on.

This was written by Norbert Kéri, posted on Sunday, September 16, 2012, at 17:08

Tagged as:
Brad wrote
The "Github repo, that I no longer seem to find" is possibly https://github.com/winglian/Heroku-PHP ?

2012-11-17 00:29:41

Norbert Kéri wrote
I'm not sure, I remember reading a readme on the repo, which this one doesn't seem to have.

2012-11-17 17:39:00

Jose Palala wrote
re: "Buildpack - not sure what it even it is" - it's kind of a way to "bundle" php extensions and tell Heroku about what kind of php extensions your app uses. the github for php buildpack reads "This is a build pack bundling PHP and Apache for Heroku apps."

In essence, all the configuration files for a php app will be installed for you in heroku when you use a buildpack

2012-12-14 08:02:34

Alon Carmel wrote
Not sure how relavent this is but i managed to run symfony 1.2 on heroku with your instructions pretty quick. works fine. NO CLI but i dont think you really need to run CLI on production anyhow.

socialdeck.herokuapp.com/slidme11 for example is running symfony.

2012-12-27 12:32:01

Norbert Kéri wrote
Well, you <em>could</em> run the app without the CLI just fine, but it saves so much time and work, that it wasn't worth it for me. Eg. doing database schema updates by hand? Oh god, never.

2012-12-27 19:23:55

Robert wrote
I was able to get past the libmcrpyt error by executing:
"export LD_LIBRARY_PATH=/app/php/ext" at the "heroku run bash".

2013-02-09 01:35:44

David wrote
Thanks for this - its a shame there is not a more detailed overview on setting up PHP apps on heroku - however thats a pleasure of computer science.

Cheers!

2013-09-06 12:13:04

Post a comment

Providing your email is optional, it is never published or shared, it is only used for auto approval purposes. If you already have at least 1 approved comment(s) tied to your email, you don't have to wait for moderation, otherwise the author must approve your comment.

Please solve this totally random captcha