Deploying Symfony2 Applications On Heroku Cloud

Heroku Cloud Symfony2 application deployment

When working on my small home project on Symfony2, I needed to deploy it on staging for testing API from the outside. Sure, you need some hosting for this purpose: either have it already or buy it. Yet there is an alternative — the cloud services where you can host your site for free but with limited resources. There are a lot of such services now and they are competing with each other. Somehow I decided to try Heroku. I heard about it a long time ago and at that point it crossed my mind. I didn’t look on other services, this time I just wanted to try my luck with Heroku. Fortunately, Heroku still has a free web hosting option so I started to search all the information needed for Symfony2 application deployment.

Here’re some helpful links I came across:

A lot of things from these articles will be repeated in this post. But there are some missed or not fully disclosed points there, and I decided to fill this gap.

Preparing

For a start you need to create a Heroku account.

Then download Heroku Toolbelt for your platform. This tool allows to manage your applications via console. For example, to install Heroku on Debian/Ubuntu you only need to run this command:

$ wget -qO- https://toolbelt.heroku.com/install-ubuntu.sh | sh

Now you need to login into Heroku from console: heroku login. Enter an email and password from your account.

Enter your Heroku credentials.
Email: test@examle.com
Password (typing will be hidden): 
Authentication successful.

Run from you terminal heroku help to check available commands for Heroku console. The list is not full, because aliases aren’t shown there. For example, command heroku create is the alias for heroku apps:create.

Creating a Heroku Application

You can create a new Heroku application in two ways:

Creating an application from console

Heroku deploys PHP projects via Composer, so you need to have a valid composer.json and composer.lock in your project. If you just run heroku create without any parameters, a new application will be created in your cloud. It doesn’t do anything now and should be configured. It will have a random name, for example pure-savannah-3520. After application is created, you can change its name (if new name is available): heroku rename your-nice-name --app pure-savannah-3520. heroku rename command is the alias for heroku apps:rename. However, it’s better to set a name when you create an application heroku create my-blog, but it should be available. You can run heroku create in a different directory and it will simply create a new application in your cloud. But we want to deploy our project, so this command should be run from the project directory where Git is initialized. In addition for creating the project heroku create checks if Git is initialized in a current directory, and, if so, it adds its remote repository.

$ mkdir my-abracadabra
$ cd my-abracadabra/
$ git init
Initialized empty Git repository in /home/fresh/Desktop/my-abracadabra/.git/
 
$ heroku create my-abracadabra
Creating my-abracadabra... done, stack is cedar-14
https://my-abracadabra.herokuapp.com/ | https://git.heroku.com/my-abracadabra.git
Git remote heroku added
 
$ git remote -v
heroku  https://git.heroku.com/my-abracadabra.git (fetch)
heroku  https://git.heroku.com/my-abracadabra.git (push)

So you have two options: either you create a new project, initialize Git there and connect to Heroku, or you connect Heroku to the working project with initialized Git. Another case is when you’ve created an application on one computer and want to set it up at another. Or you’ve created an application through the web interface and just want to set it up at your terminal. In this case you just need to add remote Heroku repository to your local project. Address of repository may look like this https://git.heroku.com/your-nice-name.git. For example, if the project is called my-abracadabra, then you need to execute:

$ git remote add heroku https://git.heroku.com/my-abracadabra.git

or

$ heroku git:remote -a my-abracadabra

Creating an application from your dashboard

On this page https://dashboard.heroku.com/new you simply enter a valid name and select a server location to create a new application.

deploy to heroku

Application has been created, now you can continue configuring it via web interface.

Running Symfony2 on Heroku

Configuration of processes

In the root directory of the project you need to create a file called Procfile and write the following text there:

web: vendor/bin/heroku-php-apache2 web/

When deploying this command will tell Heroku that it should create a web-worker which will listen to HTTP-requests and the root directory of the site (in this case, Symfony) is located in web directory. Also in this command tell to run PHP on Apache web server. If you need Nginx, use this command:

web: vendor/bin/heroku-php-nginx

You can also try to run the project on HHVM:

web: vendor/bin/heroku-hhvm-nginx
web: vendor/bin/heroku-hhvm-apache2

You can add another workers in this file, e.g. background worker for the cron tasks.

Preparing the environment for Symfony

It’s also important to know that during the deployment of PHP application Heroku runs composer with the following parameters:

$ composer install --no-dev --prefer-dist --optimize-autoloader --no-interaction

It means that everything that you have added to the require-dev section of your composer.json file will not be installed.

By default all console commands run in the dev environment. We need to switch Symfony in the production mode before composer runs its scripts because few important commands required to set up the application (clear cache, dump assets) are run there. You can do it via environment variable SYMFONY_ENV and if it’s set, its value will indicate the environment for Symfony. We do it as follows:

$ heroku config:set SYMFONY_ENV=prod

Command must be run only once before the first deployment.

And now deploy

Deployment on Heroku is done by pushing commits to the remote repository.

$ git push heroku master

After running this command in the console, you will see the whole process of deployment. If deployment was successful, then use command heroku open to open your application in the browser. By default, the address of your application will be a subdomain to the herokuapp.com (for example, https://my-abracadabra.herokuapp.com/) but you can set up it to use your own domain.

View logs

To view the logs from the console, you need to change a production config of Symfony a bit. In file app/config/config_prod.yml you have to replace path: "%kernel.logs_dir%/%kernel.environment%.log" with path: "php://stderr".

monolog:
    handlers:
        nested:
            path: "php://stderr"

Now logs can be viewed with:

$ heroku logs --num 10

or you can monitor logs online:

$ heroku logs --tail

Console commands for running Symfony2 on Heroku

To get access to the bash while you deploy Symfony2 on Heroku Cloud, run the command heroku run bash.

Then you can run available commands from the application:

$ heroku run bash
Running `bash` attached to terminal... up, run.1516
~ $ app/console doctrine:schema:validate
[Mapping]  OK - The mapping files are correct.
[Database] FAIL - The database schema is not in sync with the current mapping file.
~ $ app/console doctrine:schema:create
ATTENTION: This operation should not be executed in a production environment.
 
Creating database schema...
Database schema created successfully!
~ $ exit
exit

You don’t need to specify the environment via --env=prod option, because it was set up through the environment variable.

You can also run interactive PHP shell from the terminal:

$ heroku run "php -a"
Running `php -a` attached to terminal... up, run.3436
Interactive shell
 
php > echo "Hello World";
Hello World
php > exit

Don’t forget to type exit.

Manage configuration settings

When we run composer update on the local machine and there are new parameters in the parameters.yml.dist file that have not been installed locally yet, Symfony console will ask you to add these parameters in interactive mode. It can’t be done on Heroku, since composer is run with the option --no-interaction. Therefore, we need to set these parameters somehow. The most convenient way that offers Symfony and Heroku is to write them into environment variables, and PHP will read them out. You can do it with the following command:

$ heroku config:set DATABASE_PASSWORD=secret_password
Setting config vars and restarting my-abracadabra... done, v32
DATABASE_PASSWORD: secret_password
$ heroku config:set DATABASE_USER=secret_user
Setting config vars and restarting my-abracadabra... done, v33
DATABASE_USER: secret_user

To check what you have set, use:

$ heroku config
=== my-abracadabra Config Vars
DATABASE_PASSWORD:          secret_password
DATABASE_USER:              secret_user

You can also add, edit and delete parameters from your dashboard.

Deploying Symfony2 applications on Heroku Cloud

It can also be nicely done in the composer.json file by using script Incenteev/ParameterHandler. It is registered in Symfony composer.json out of the box, we don’t need to do anything, simply use its feature to map environment parameters into parameters of application.

{
    "extra": {
        "incenteev-parameters": {
            "env-map": {
                "my_first_param": "MY_FIRST_PARAM",
                "my_second_param": "MY_SECOND_PARAM"
            }
        }
    }
}

Find the extra section in the composer.json file and change it to this:

{
    "extra": {
        "symfony-app-dir": "app",
        "symfony-web-dir": "web",
        "incenteev-parameters": {
            "file": "app/config/parameters.yml",
            "env-map": {
                "database_name": "SYMFONY__DATABASE_NAME",
                "database_user": "SYMFONY__DATABASE_USER",
                "database_host": "SYMFONY__DATABASE_HOST",
                "database_password": "SYMFONY__DATABASE_PASSWORD"
            }
        }
    }
}

Now we need to set the environment variables. In this case we need to specify the parameters of database.

Connecting a database to the project

To find a database (and any other tool you need) look on the add-ons page https://addons.heroku.com/. For each add-on you can find installation instructions.

Install PostgreSQL:

$ heroku addons:add heroku-postgresql
Adding heroku-postgresql on my-abracadabra... done, v9 (free)
Attached as HEROKU_POSTGRESQL_IVORY_URL
Database has been created and is available
 ! This database is empty. If upgrading, you can transfer
 ! data from another database with pgbackups:restore.
Use `heroku addons:docs heroku-postgresql` to view documentation.

MySQL is installed via add-on ClearDB MySQL Database:

$ heroku addons:add cleardb
Adding cleardb on my-abracadabra... done, v10 (free)
Use `heroku addons:docs cleardb` to view documentation.

When connecting add-on to the application, its connection settings are also stored in the environment variables:

$ heroku config
=== my-abracadabra Config Vars
CLEARDB_DATABASE_URL:        mysql://b9153ad7c7a263:a7f8d514@us-cdbr-iron-east-01.cleardb.net/heroku_ab3bbc34931b570?reconnect=true
HEROKU_POSTGRESQL_IVORY_URL: postgres://ylcarfkdjbxouy:tEDQdicyUIcnCk7-xNn9SZZCHZ@ec2-107-20-229-112.compute-1.amazonaws.com:5432/db7sm6in86mtir

If we parse this URL, we'll get a host, a database name, a user and a password. To make this thing done automatically while deploy we have to write a script for composer and subscribe it to the event pre-install-cmd. To do this, somewhere in the project (e.g. in the namespace AppBundle\Composer) create a new class:

If we parse this URL, we’ll get a host, a database name, a user and a password. To do it automatically when deploying we have to write a script for composer and subscribe it to the event pre-install-cmd. To do this somewhere in the project (e.g. in the namespace AppBundle\Composer) create a new class:

<?php
namespace AppBundle\Composer;
 
use Composer\Script\Event;
 
class HerokuEnvironment
{
    /**
     * Populate Heroku environment
     *
     * @param Event $event Event
     */
    public static function populateEnvironment(Event $event)
    {
        $url = getenv('CLEARDB_DATABASE_URL'); // If MySQL is chosen
        // $url = getenv('HEROKU_POSTGRESQL_IVORY_URL'); If PostgreSQL is chosen
 
        if ($url) {
            $url = parse_url($url);
            putenv("SYMFONY__DATABASE_HOST={$url['host']}");
            putenv("SYMFONY__DATABASE_USER={$url['user']}");
            putenv("SYMFONY__DATABASE_PASSWORD={$url['pass']}");
 
            $db = substr($url['path'], 1);
            putenv("SYMFONY__DATABASE_NAME={$db}");
        }
 
        $io = $event->getIO();
        $io->write('CLEARDB_DATABASE_URL=' . getenv('CLEARDB_DATABASE_URL'));
    }
}

Now add this script in composer.json:

{
    "scripts": {
        "pre-install-cmd": [
            "AppBundle\\Composer\\HerokuEnvironment::populateEnvironment"
        ]
    }
}

Now during our first deployment, composer will set database settings by itself.

Free version of PostgeSQL is limited to 10k records. Free ClearDB is limited to 5 megabytes.

Additional settings in composer.json

In the composer.json file in the extra section you can pass additional environment settings for Heroku. For example:

{
    "extra": {
        "heroku": {
            "framework": "symfony2",
            "document-root": "web",
            "php-config": [
                "date.timezone=Europe/Kiev",
                "display_errors=off",
                "short_open_tag=off"
            ]
        }
    }
}

Script php app/check.php will show what you still need to set up for Symfony to work properly. PHP settings can be set in php-config section. Also Symfony requires some PHP modules to be enabled. To tell Heroku to include them when a web-worker is created, you need to list them in the require section of composer.json.

{
    "require": {
        "ext-intl": "*",
        "ext-mbstring": "*"
    }
}

Notifications about successful deployment

There is an add-on Deploy Hooks which allows to configure notifications sending after successful deployment:

$ heroku addons:add deployhooks:email \
>     --recipient=me@example.com \
>     --subject="My abracadabra application has been deployed" \
>     --body="{{user}} has deployed my abracadabra to Heroku"
Adding deployhooks:email on my-abracadabra... done, v11 (free)
Use `heroku addons:docs deployhooks` to view documentation.

You can also configure other notification channels.

Connection with GitHub

If you keep your repository on Github, you can use your Heroku dashboard to connect it to automatic deployments after each commit to the master branch (or any other branch).

deploy symfony2 on heroku cloud

In this case, you will need to push only to GitHub. Heroku will catch it and redeploy production.

If you do not want to set up automatic deployment, then you have to push twice. Once to send the changes to your remote repository, the second time to send the changes to Heroku repository to create a new deployment:

$ git push origin master
$ git push heroku master

File App.json And Symfony2 Deploying To Heroku Open Source Projects In One Click

Another sweet feature of Heroku. For example, if you are developing an open source project in your public repository on GitHub and want to allow users to see the application in action without the need to download and deploy locally. You can add all the necessary configs for deployment in your repository, add a standard Heroku button and deploy it in one click.

Configuration file for Heroku is called app.json. It should be put in the root directory of your project. Details about all configs options can be found here. Here’s an example file:

{
    "name": "Name will be shown during the creation of a new app",
    "description": "Description will be shown during the creation of a new app",
    "keywords": ["something", "to", "describe", "app"],
    "website": "https://homepage-of-project.com",
    "success_url": "/homepage-or-smth",
    "repository": "https://github.com/your-nickname/your-repository",
    "logo": "http://exampe.com/this-image-will-be-shown-during-the-creation-of-new-app.webp",
    "addons": [
        "cleardb"
    ],
    "env": {
        "SYMFONY_ENV": {
            "value": "prod"
        }
    },
    "scripts": {
        "postdeploy": "php app/console doctrine:migration:migrate"
    }
}

You can set environment variables directly from config. For example, switch Symfony to production mode:

{
    "env": {
        "SYMFONY_ENV": {
            "value": "prod"
        }
    }
}

If you need to execute few commands after deployment:

{
    "scripts": {
        "postdeploy": "php app/console doctrine:migration:migrate; ls -la; sleep 5"
    }
}

I think everyone knows about the popular application TodoMVC, which is written as a tutorial for every JavaScript framework. Here is an excellent example of how you can deploy a web application Symfony Angular TodoMVC in one click. Here TodoMVC application is written using Symfony and Angular. Do not forget that you need to be registered on Heroku to be able to deploy it. Just find the button in the README of this repository and click it.

Deploying Symfony2 applications on Heroku Cloud

You will be redirected to your dashboard to create a new application from template. There is the parameter template in the URL which has a path to the file app.json from which application settings are pulled. It looks like this on the dashboard:

Deploying Symfony2 applications on Heroku Cloud

All information from app.json, is displayed on this page: name, description, logo. As you can see here the add-on for PostgreSQL is defined by default thanks to the one config option:

{
  "addons": [
    "heroku-postgresql:hobby-dev"
  ]
}

After you click on the “Deploy for Free”, the deployment will begin and in a few seconds you will get a working site. Provided that in the master branch you have a working code :) Here is more information about configuring one click deployment.

Add-ons for the popular tasks

If you need to add cron tasks there is an add-on Heroku Scheduler. Documentation describes how to work with it in details.

The Heroku platform itself doesn’t provide an email service. You will need external SMTP server for it. But in the list of available add-ons (Email/SMS category) you can find services which have already been integrated.

The Conclusion

This is the minimum you need to start working with Heroku. If you need additional tools not mentioned in this article, try to find them in the list of add-ons. Most popular tools should be there. Whether Heroku deserves to be a good enough production server? It is difficult to say for me now, because I don’t have much experience with it. But it is quite good as a staging with rapid deployment process. It is also suitable for open source projects that can be deployed in a single click.