Deploying Symfony on Scalingo

Detection

When a PHP application is deployed, Scalingo automatically detect the most well known frameworks, including Symfony.

During the deployment process you’ll see the following output, mentioning that the framework has correctly been detected.

-----> Detected Symfony App
...
-----> Setting up Symfony App

Configuration

The standard way to define environment-dependent variables in Symfony is to modify the app/config/parameters.yml file that is generated by Composer and usually excluded from Git (without that it wouldn’t be safe to store credentials in it). This method to define environment variables is very specific to Symfony, and has been thought to be compatible with shared hostings, where you drag and drop your files on a FTP server, without the possibility to set normal environment variables.

With Scalingo, it’s exactly the opposite: you can’t modify the parameters.yml file unless you add it to Git. But as we explained before, it’s a very bad practice! The good method, as specified in the 12 factors, is to define environment variables.

You now have to choose between two solutions to make your Symfony configuration compatible with environment variables:

Keeping parameters.yml in dev, using environment variables on Scalingo

Luckily, Incenteev/ParameterHandler (the package in charge of managing your parameters) supports environment parameters. Go to the Scalingo dashboard, and add the needed environment variables, ideally respecting the UPPERCASE_SNAKE_CASE. Then, edit your composer.json file and add a extra.incenteev-parameters key that describes the mapping between Symfony parameters and the environment variables:

{
    "extra": {
        "incenteev-parameters": {
            "env-map": {
                "database_host": "DATABASE_HOST",
                "database_port": "DATABASE_PORT"
            }
        }
    }
}

Now, your application will use the environment variables (when defined), and the parameters.yml file.

Get rid of parameters.yml, only use environment variables

For sure, this will make your configuration cleaner. But parameters.yml has the advantage of being easy to edit, and the parameters.yml.dist provides a template and configuration reference which is valuable when working in team. For instance, when a coworker enters composer install, the CLI will ask him for the missing values, so he gets notified of parameters changes.

In order to only work with environment variables, let’s remove Incenteev/ParameterHandler:

  • Delete the line "Incenteev\\ParameterHandler\\ScriptHandler::buildParameters", in your composer.json ;
  • Delete ParameterHandler’s configuration in composer.json under extra.incenteev-parameters key ;
  • Run composer remove incenteev/composer-parameter-handler.

Now, you’ll find that Symfony provides a native support for environement variables, but when Incenteev/ParameterHandler is installed (it is, by default), they are overwritten by it, since it is loaded at higher level in the framework.

So, if you have a %some.awesome.parameter% in your config, define the SYMFONY__SOME__AWESOME_PARAMETER variable in Scalingo’s dashboard.

It’s now up to you to define your environment variables in your development environment.

Reverse Proxy

Applications on Scalingo are deployed behind a reverse proxy. You need to instruct Symfony to use the information in the X-Forwarded-* HTTP headers. The instructions to do so are available in Symfony documentation. You basically need to update the public/index.php file to include:

Request::setTrustedProxies(
    // trust *all* requests
    ['127.0.0.1', $request->server->get('REMOTE_ADDR')],
    Request::HEADER_X_FORWARDED_ALL
);

Log Files

By default, we are getting the logs written on stdout and send them to our log aggregator system. The PHP logging does not work the same way (since the standard output is what is sent back to the browser), and Symfony is using its own log files.

These files are named var/logs/dev.log and var/logs/prod.log (app instead of var in Symfony 2 applications) by default. The logs stored in those files are visible in the Logs section of the dashboard.

Cache Warmup

Towards the end of the deployment process, you will see the following output:

Warming up cache

It means that we are preparing the cache of your application, to avoid making it at runtime and loosing performance. Under the hood, the following command is executed (replace app with bin in Symfony 3):

app/console cache:warmup --env=prod --no-debug

By default, this command executes the default cache warmers that the framework provides. You can add your own cache warmers: learn more about Symfony’s cache warmup here.

Asset Management with Assetic

If you are using Assetic to handle your assets, you may need to run a custom command once Symfony has been installed. To do so, you need to add the following piece of configuration in your composer.json:

{
  // ...,
  "extra": {
    "paas": {
      "compile": [
        "php app/console assetic:dump --env=prod --no-debug"
      ]
    }
  }
}

This configuration node (extra.paas.compile) defines that after installing Symfony, the assetic:dump command will be executed, preparing your assets for your application. Of course, you can add any other commands here!

Applying Database Migration

During the lifetime of your application, you may need to update the schema of your database. When using Symfony, these actions are handled using the Doctrine ORM. This Symfony documentation page explains how to use Doctrine with your application.

On Scalingo you can teach the platform to apply your database migration automatically during the deployment using a postdeploy hook. Here is the content of the Procfile to apply Doctrine migration to your application:

postdeploy: php bin/console doctrine:migrations:migrate

Note that the postdeploy hook execution time should not exceed 20 minutes. Therefore it should not be used for complex database migration on very large database. In that case you’d better use our CLI to perform such administrative tasks.

Symfony Messenger

Handling Symfony Messenger queues requires you to start another process in your application. Add the following line to the Procfile of your application:

worker: php bin/console messenger:consume async

After deploying your application, you must scale the newly created type of containers worker at least to 1.

Custom Nginx Configuration

You may want to customize the Nginx behaviour for a specific path. The use case for instance is to protect with a password the access to a specific path. In this case one should update the composer.json to include:

    "extra": {
        "paas" : {
            "nginx-includes": ["./nginx.conf"]
        }
    }

And add a nginx.conf at the root of your repository containing:

location ^~ /my/protected/path {
    auth_basic Restricted;
    auth_basic_user_file /app/.htpasswd;

    try_files $uri /index.php =404;
    fastcgi_pass php;
    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    include fastcgi_params;
}

Known Issues

Disable DebugBundle on Scalingo

When deploying a Symfony app on Scalingo, you may see the following error:

PHP Fatal error: Uncaught Symfony\Component\Debug\Exception\ClassNotFoundException: Attempted to load class "DebugBundle" from namespace "Symfony\Bundle\DebugBundle".

Did you forget a "use" statement for another namespace? in /app/src/Kernel.php:42

This is because your application is deployed in production mode on Scalingo. Hence, the DebugBundle dependency is not installed by Composer during the build. In order to let Symfony know to only use DebugBundle in development and test environment, you should update the file config/bundles.php:

Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true, 'test' => true],

Suggest edits

Deploying Symfony on Scalingo

©2024 Scalingo