Getting Started with Symfony2 on Heroku

Last Updated: 04 February 2015

php symfony

Table of Contents

This guide will get you up and running with a Symfony2 application. For an introduction to using PHP on Heroku, please refer to Getting Started with PHP on Heroku.

Prerequisites

Creating a Symfony2 application

The application in this tutorial is based on the Symfony2 Quick Tour guide. It’s worth a read before (or while) following the instructions in this article.

Installing a Symfony Standard Edition project

Use the composer create-project command to bootstrap a new project based on the Symfony Standard Edition, which is a fully functional Symfony application which includes some sample code. The command below sets it up in a directory named hello_symfony_heroku using Symfony version 2.4 or later.

After downloading an extensive number of dependencies, the installer will prompt you to enter a few details regarding a database connection and mailer transports. For now, you can simply hit enter at each of these prompts to accept the default values; you can always change them later.

After it’s done, Composer has set up a fully functional Symfony Standard Edition project in the directory you specified, so you can cd to it.

$ composer create-project symfony/framework-standard-edition hello_symfony_heroku/
Installing symfony/framework-standard-edition (v2.5.7)
  - Installing symfony/framework-standard-edition (v2.5.7)
    Loading from cache

Created project in hello_symfony_heroku/
Installing dependencies (including require-dev)
  - ...

$ cd hello_symfony_heroku

Initializing a Git repository

It’s now time to initialize a Git repository and commit the current state:

$ git init
Initialized empty Git repository in ~/hello_symfony_heroku/.git/
$ git add .
$ git commit -m "initial import"
[master (root-commit) e405693] initial import
 66 files changed, 4300 insertions(+)

Downgrading to Doctrine DBAL 2.4

The new project you just created uses (through another dependency) the latest Doctrine DBAL version. However, due to a regression in version 2.5, this would make it impossible to deploy the app.

Until the issue is fixed in a future 2.5.x release, you can force usage of version 2.4 of the component by explicitly requiring it in the composer.json of the project:

$ composer require doctrine/dbal:~2.4.4
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
  - Removing doctrine/dbal (v2.5.0)
  - Installing doctrine/dbal (v2.4.4)
    Downloading: 100%

Writing lock file
Generating autoload files
Updating the "app/config/parameters.yml" file
Clearing the cache for the dev environment with debug true
Trying to install assets as symbolic links.
Installing assets for Symfony\Bundle\FrameworkBundle into web/bundles/framework
The assets were installed using symbolic links.
Installing assets for Sensio\Bundle\DistributionBundle into web/bundles/sensiodistribution
The assets were installed using symbolic links.

Don’t forget to add and commit the changed files:

$ git add composer.*
$ git commit -m "force doctrine/dbal to 2.4"
[master 54a30da] force doctrine/dbal to 2.4
 2 files changed, 11 insertions(+), 18 deletions(-)

Greeting the world

The standard edition application contains a simple demo controller that will print the text “Homepage”, but it’s configured to respond to the URL /app/example; it’s more convenient to change this to / for the purpose of this tutorial.

Simply adjust the contents of src/AppBundle/Controller/DefaultController.php to look like the following:

<?php

namespace AppBundle\Controller;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;

class DefaultController extends Controller
{
    /**
     * @Route("/", name="homepage")
     */
    public function indexAction()
    {
        return $this->render('default/index.html.twig');
    }
}

You may have noticed that it was a really simple one line change - all you had to do was change the value in the @Route annotation from /app/example to /.

As always, add and commit that change:

$ git add .
$ git commit -m "greet the world on /"
[master 7437078] greet the world on /
 1 file changed, 1 insertion(+), 1 deletion(-)

You are now ready for your first deploy.

Deploying to Heroku

To deploy your application to Heroku, you must first create a Procfile, which tells Heroku what command to use to launch the web server with the correct settings. After you’ve done that, you’re gonna create an application on Heroku, configure the Symfony env to use, and then you can simply git push, and you’re done!

Creating a Procfile

By default, Heroku will launch an Apache web server together with PHP to serve applications.

However, two special circumstances apply to your Symfony application:

  1. The document root is in the web/ directory (not in the root directory) of the application;
  2. The Composer bin-dir, where vendor binaries (and thus Heroku’s own boot scripts) are placed, is bin/, and not the default vendor/bin.

Vendor binaries are usually installed to vendor/bin by Composer, but sometimes (e.g. when running a Symfony Standard Edition project!), the location will be different. If in doubt, you can always run composer config bin-dir to figure out the right location.

$ echo "web: bin/heroku-php-apache2 web/" > Procfile
$ git add .
$ git commit -m "Procfile for Apache and PHP"
[master 35075db] Procfile for Apache and PHP
 1 file changed, 1 insertion(+)

Creating a new application on Heroku

To create a new Heroku application that you can push to, use the CLI’s create command:

$ heroku create
Creating mighty-hamlet-1981 in organization heroku... done, stack is cedar-14
http://mighty-hamlet-1981.herokuapp.com/ | git@heroku.com:mighty-hamlet-1981.git
Git remote heroku added

You are now almost ready to deploy the application. Follow the next section to ensure your Symfony app runs with the settings for the right environment.

Configuring Symfony to run in the prod environment

If you don’t explicitly configure the environment (dev, prod etc) to use, Symfony will, by default, use the dev environment in console commands and at runtime. That would break the build, because in dev environments, Symfony uses the SensioGeneratorBundle to perform certain tasks, but that bundle is not installed upon a push - Composer does not install dev packages when pushing to Heroku.

For Symfony to know it needs to use the prod environment at all times, it reads from the SYMFONY_ENV environment variable. You can simply set environment variables using the heroku config feature, so run this one command as the last step before deploying your app for the first time:

$ heroku config:set SYMFONY_ENV=prod
Setting config vars and restarting mighty-hamlet-1981... done, v3
SYMFONY_ENV: prod

Pushing to Heroku

Next up, it’s finally time to deploy to Heroku:

You may see a message such as the following when you push to Heroku for the very first time:

The authenticity of host 'heroku.com (50.19.85.132)' can't be established.
RSA key fingerprint is 8b:48:5e:67:0e:c9:16:47:32:f2:87:0c:1f:c8:60:ad.
Are you sure you want to continue connecting (yes/no)?

In this case, you need to confirm by typing “yes” and hitting return - ideally after you’ve verified that the RSA key fingerprint is correct.

$ git push heroku master
Initializing repository, done.
Counting objects: 130, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (107/107), done.
Writing objects: 100% (130/130), 70.88 KiB | 0 bytes/s, done.
Total 130 (delta 17), reused 0 (delta 0)

-----> PHP app detected
-----> Setting up runtime environment...
       - PHP 5.5.12
       - Apache 2.4.9
       - Nginx 1.4.6
-----> Installing PHP extensions:
       - opcache (automatic; bundled, using 'ext-opcache.ini')
-----> Installing dependencies...
       Composer version 64ac32fca9e64eb38e50abfadc6eb6f2d0470039 2014-05-24 20:57:50
       Loading composer repositories with package information
       Installing dependencies from lock file
         - ...

       Generating optimized autoload files
       Creating the "app/config/parameters.yml" file
       Clearing the cache for the dev environment with debug true
       Installing assets using the hard copy option
       Installing assets for Symfony\Bundle\FrameworkBundle into web/bundles/framework
       Installing assets for Acme\DemoBundle into web/bundles/acmedemo
       Installing assets for Sensio\Bundle\DistributionBundle into web/bundles/sensiodistribution
-----> Building runtime environment...
-----> Discovering process types
       Procfile declares types -> web

-----> Compressing... done, 61.5MB
-----> Launching... done, v3
       http://mighty-hamlet-1981.herokuapp.com/ deployed to Heroku

To git@heroku.com:mighty-hamlet-1981.git
 * [new branch]      master -> master

And that’s it! If you now open your browser, either by manually pointing it to the URL heroku create gave you, or by using the Heroku Toolbelt to launch a new browser window, the application will respond:

$ heroku open
Opening mighty-hamlet-1981... done

Et voilà! You should be seeing “Homepage.” in your browser.

As an exercise, try adjusting the contents of this page by editing app/Resources/views/default/index.html.twig, then add and commit the change to your Git repository, and push the change to Heroku.

Cleanup

By default, the Symfony2 app will log into your application’s app/log/ directory, which isn’t ideal as Heroku uses an ephemeral file system. On Heroku, the best way to handle logging is using Logplex, and the best way to send log data to Logplex is by writing to STDERR or STDOUT. Luckily Symfony2 uses the excellent Monolog library for logging, and so a new log destination is just a config file change away.

Changing the log destination for production

All that’s required to have Symfony log to STDERR is changing app/config/config_prod.yml. Locate the monolog/handlers/nested section in this file and change the value of path from "%kernel.logs_dir%/%kernel.environment%.log"to "php://stderr". You can now add, commit and push as usual:

$ git add app/config/config_prod.yml
$ git commit -m "log to STDERR in prod"
[master 68e9250] log to STDERR in prod
 1 file changed, 1 insertion(+), 1 deletion(-)
$ git push heroku master

Next, run heroku logs --tail to keep the stream of logs from Heroku open in your terminal. Switch back to your browser and navigate to your Heroku application. As you refresh the page, you’ll see the access logs on your terminal.

Now, manually change the URL to /foobar. A brief moment later, the internal error logged by Symfony will appear on your terminal:

2014-04-22T14:21:00.097081+00:00 app[web.1]: 10.239.18.93 - - [22/Apr/2014:14:20:59 +0000] "GET /foobar HTTP/1.1" 404 565 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_5) AppleWebKit/537.75.14 (KHTML, like Gecko) Version/6.1.3 Safari/537.75.14"
2014-04-22T14:21:00.097087+00:00 app[web.1]: [22-Apr-2014 14:20:59] WARNING: [pool www] child 63 said into stderr: "[2014-04-22 14:20:59] request.ERROR: Uncaught PHP Exception Symfony\Component\HttpKernel\Exception\NotFoundHttpException: "No route found for "GET /foobar"" at /app/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php line 144 {"exception":"[object] (Symfony\\Component\\HttpKernel\\Exception\\NotFoundHttpException: No route found for \"GET /foobar\" at /app/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php:144, Symfony\\Component\\Routing\\Exception\\ResourceNotFoundException:  at /app/app/cache/prod/appProdUrlMatcher.php:103)"} []"

Press Ctrl+C on your keyboard again to leave the heroku logs command.

Further reading