CakePHP and Heroku

I was impressed with how straight forward it is to set up CakePHP apps on Heroku (or probably any Cloud solution probably).
And it is free here for basic applications, especially for trying it out.

Prepare your app

Your application needs a small adjustment to the composer.json in order to deploy

    "scripts": {
        "compile": [
            "echo '<?php return [...]; ' > config/app_local.php",
            "chmod +x bin/cake",
            "bin/cake migrations migrate --no-lock",
            ...
            "bin/cake asset_compress build"
        ],
        ...
    },

Heroku leverages the compile script rows if available.

The first line is the important one if you need to put any default app_default.php in there. You can also check on the existence and only try to load it if the file is there.
Some also use a specific template and copy it over like cp config/app_local.heroku.php config/app_local.php. Make sure you env()ed everything dynamic in there, though.

The second line makes sure, the shells are executable.
Then there is some migration needed, to populate the database with default data.
Finally, you want to build the assets and maybe clear some cache.

You could also add more commands for deployment, e.g. for seeding and alike. Bear in mind that they will be executed each time you deploy.

All you local keys, credentials and non-committed configs need to be env() based in your config.
This way they can be later be injected from the server.

// Your app_default.php
return [
    'MyCustomThing' => [
        'currency' => env('DEFAULT_CURRENCY', 'EUR'),
    ],

The second value is the default value in case no dynamic server based config is being passed in.

Prepare the server

Create a new Heroku server at dashboard.heroku.com and find a name, e.g. my-demo-app.herokuapp.com.

Resources

You want to add a few add-ons like

  • Heroku Postgres or ClearDB MySQL
  • Heroku Redis for caching
  • Heroku Scheduler for cronjobs

If you also want extensive monitoring, you can add New Relic APM.
For all basic modules, it will still be free of charge so far.

Settings

Here we will now connect the add-ons to our app and inject configs.

The default ones you need to set are:

  • CACHE_DEFAULT_URL should be set to the Redis URL redis://....
  • DATABASE_URL points to the postgres://... or mysql:// URL depending on the add-on you chose

On top of those you can now also set any of the other env() ones, e.g. our custom one:

  • DEFAULT_CURRENCY: USD

Deploy

Just give it a spin, try the "Manual Deploy" first, select a branch of your repository and hit "Deploy Branch".
There should be a success message and a link to the deployed site, otherwise check the log output.

Automate

Once everything is working fine, you can also automate it in a way, that every merge or push into master triggers an auto-deploy.

You can also set it up in a way that waits for green CI build before actually deploying and things like that.

Tips

.htaccess

Sometimes you don’t want to publish right away, or you have a testing/staging subdomain on top. Here you might want to restrict basic access.

Simple HTTP auth password protection can be achieved with a simple addition to the .htaccess file.
At the very end, add:

AuthUserFile /app/.htpasswd
AuthType Basic
AuthName "Restricted Access"
Require valid-user

You also need to add a .htpasswd file.

To add or change a password, run

htpasswd -c .htpasswd {your-username}

on the app root directory.

Logging to database

Instead of file or console logging, you can look into the DatabaseLog plugin, which can log everything to a MySQL or Postgres database here for you. The most basic setup would be to just log into the same one:

// app_default.php
    'Log' => [
        'debug' => [
            'className' => 'DatabaseLog.Database',
            'scope' => false,
        ],
        'error' => [
            'className' => 'DatabaseLog.Database',
            'scope' => false,
        ],
    ],
    'DatabaseLog' => [
        'datasource' => 'default',
    ],

You can then quickly access them via /database-log/logs backend off your Heroku app.

Summa Summarum

It really is super easy to deploy a Cake app this way, it is a good solution to show your progress an app to people, have it as basic staging server and alike.

If you have more complex apps, you might want to go into build pipelines and more sophisticated approaches.

Anything forgotten?

Please comment on it below!

1 Comment

  1. If you run a pipeline the commands in composer.json will only run on build to the first environment. After that, the slug is just promoted to the next environment. So the migrations command will not be run when app is promoted to production.
    Heroku now has a solution to this, a special process type called "release" that runs whenever a release is created – also when promoting and rollbacks between environments. I haven’t tried it out myself yet, but it seems promising:
    https://devcenter.heroku.com/articles/release-phase

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.