The Ultimate Cheatsheet for Deploying Laravel Apps To Heroku

Thursday, April 06, 2017

If you’re like me, you use Heroku to host many of your personal web projects. And why not? Heroku has become so popular because it spares developers the hassle of managing servers and all that, allowing you to achieve a lot with a few clicks or commands. Best of all, its free tier actually gives you a lot of freedom.

And if you’re like me, you’ve realized that, while Heroku supports PHP apps, it really wasn’t built for the traditional PHP/MySQL stack, thus you might find yourself running into a few snags here and there. Having hosted a few Laravel projects on Heroku, I decided to come up with this list of helpful tips so others can avoid many of the issues I battled with initially.

To get the most out of this article, you should read Heroku’s Devcenter article on deploying Laravel apps to Heroku first.

CLI vs Web Dashboard

Heroku has this nifty tool called the heroku-cli that allows you to manage your apps from the command line. Most of these functions can also be accessed online at your Heroku app dashboard at http://dashboard.heroku.com/app/<appname>. I recommend installing the CLI though. The web dashboard is useful for when you’ce on the move. And speaking of “on the move”…

Use the mobile app

Bet you didn’t know there was a mobile app. Well, you do now. An unofficial one called LovelyNezumi. I use it a lot to monitor logs, set config vars, run migrations, add collaborators and a lot more. It’s got a clean and simplistic UI, just like Heroku, and it’s easy to use. Best of all, it’s free. Kudos to the developer. Get it here.

Config vars

Config vars in Heroku replace your .env file. They can be set in your app dashboard or via the heroku-cli:

heroku config:set APP_ENV=local

At a minimum, you’ll need to set the APP_KEY var for your app to run. Here’s a little script that does that (if you have PHP7 and your config.app.cipher is set to the default AES-256-CBC):

# on Bash
KEY=`php -r "echo 'base64:’.base64_encode(random_bytes(32));"`
heroku config:set APP_KEY=$KEY

# on Windows PowerShell
$KEY=`php -r "echo 'base64:’.base64_encode(random_bytes(32));"`
heroku config:set APP_KEY=$KEY

If you don’t have PHP7, just copy APP_KEY from a local .env

See here for more on config vars

Procfile and buildpacks

Before making your first push to Heroku, you should create a file named Procfile in your project directory with the following contents:

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

This tells Heroku your public document root.

Also, sometimes you might run into the error “Could not detect buildpack” when pushing to Heroku. To fix this, just run heroku buildpacks:set heroku/php in your terminal.

More details: Procfile, buildpacks

File storage

Bad news here: Heroku has an ephemeral file system. This means that any files you store to the local disk get deleted at least once every 24 hours without you doing anything. Worse, they’re also deleted every time you push to Heroku.

How to get around this? Use a different storage location. Laravel supports different drivers for file storage, including Amazon S3. Instructions for using S3 can be found here.

Logging

Because of the filesystem’s temporary nature discussed above, using Laravel’s default log config won’t work. You need to change the log driver, as described here. I prefer doing this without changing any code, so I can retain the usual settings on your local. Just set config var APP_LOG=errorlog. You can view your app logs on your dashboard/mobile app or by running heroku logs.

Cache and Sessions

The default driver in Laravel for both cache and sessions is file. If you plan on using caching/sessions in your application, you’ll need to change these to either database or memcached (or cookie for sessions). Again, all you need to change are config vars CACHE_DRIVER and SESSION_DRIVER.

If you choose to use memcached, you’ll need to install the Heroku add-on Memcachier. (Don’t forget to composer require ext-memcached:*.) See here for more details on Memcachier and here for more on sessions.

Database

SQLite

Using SQLite on Heroku is a very bad idea. Don’t do it, except you want your database getting deleted every time you push. You can read more on that here.

MySQL

Ah, the traditional companion of PHP. Heroku supports MySQL through add-ons such as ClearDB and JawsDB.After installing the add-on, you’ll need to set the config var DB_CONNECTION=mysql and modify your config/database.php:

<?php

$dbUrl = parse_url(env("CLEARDB_DATABASE_URL"));

return [
// …
    'mysql' => [
        'driver' => 'mysql’,
        'host' => isset($dbUrl["host"]) ? $dbUrl["host"] : env("DB_HOST"),
        'port' => isset($dbUrl["port"]) ? $dbUrl["port"] : env("DB_PORT"),
        'database' => isset($dbUrl["path"]) ? ltrim($dbUrl["path"], '/') : env("DB_DATABASE"),
        'username' => isset($dbUrl["user"]) ? $dbUrl["user"] : env("DB_USERNAME"),
        'password' => isset($dbUrl["pass"]) ? $dbUrl["pass"] : env("DB_PASSWORD"),
        'charset' => 'utf8mb4’,
        'collation' => 'utf8mb4_unicode_ci’,
        'prefix' => '’,
        'strict' => true,
        'engine' => null,
    ],
];

The configuration above is exactly the same for JawsDB; all you need to do is replace CLEARDB_DATABASE_URL with JAWSDB_URL. More details: ClearDB, JawsDB.

PostgreSQL

This is Heroku’s recommended database. To use it, install the add-on and change your DB_CONNECTION to pgsql. Then update your config/database.php :

<?php

$dbUrl = parse_url(env("DATABASE_URL"));

return [
// …
    ’pgsql' => [
        'driver' => 'pgsql’,
        'host' => isset($dbUrl["host"]) ? $dbUrl["host"] : env("DB_HOST"),
        'port' => isset($dbUrl["port"]) ? $dbUrl["port"] : env("DB_PORT"),
        'database' => isset($dbUrl["path"]) ? ltrim($dbUrl["path"], '/') : env("DB_DATABASE"),
        'username' => isset($dbUrl["user"]) ? $dbUrl["user"] : env("DB_USERNAME"),
        'password' => isset($dbUrl["pass"]) ? $dbUrl["pass"] : env("DB_PASSWORD"),
        'charset' => 'utf8’,
        'prefix' => '’,
        'schema' => 'public’,
        'sslmode' => 'prefer’,
    ],
];

More details on Heroku Postgres can be found here.

Queues

To use queues on Heroku, youll need to add this line to your Procfile:

queue: php artisan queue:work

Essentially, this creates a worker dyno called queue (you can name it whateer you wish), started with the command php artisan queue:work which is the command for starting the queue listener (see the documentation);

SSH (or close enough, anyway)

Heroku has this awesome feature called one-off dynos that allow you to perform admin tasks, such as running custom scripts, migrations, caching routes and clearing cached items. Essentially, you can run any terminal command. Here’s all you need to do:

$ heroku run php artisan migrate
Migrations completed succesfully
$ heroku run bash
Running bash attached to terminal... up, run.1
~ $ php artisan db:seed
Seeding data...done.
~ $ ls
app
bootstrap
config
...
Procfile
~ $ php artisan inspire
"Genius is one percent inspiration and ninety-nine percent perspiration. - Thomas Edison"

For the full gist on one-off dynos, go here.

Laravel and Heroku are both powerful platforms which are straightforward to use, for the most part. While I do try out other SaaS providers, most of the time I stick with Heroku, because of its roomy free tier, wonderful add-ons and complete documentation. The nice UI doesn’t hurt too. Have a lovely day with Heroku and Laravel.

Do you know any other useful tips for hosting Laravel apps on Heroku? Mention it down in the comments so we can all learn.

Thanks for reading. If you enjoyed this article, please recommend and share.


Hey👋. I write about interesting software engineering challenges. Want to get updated when I publish new posts? Just visit tntcl.app/blog.shalvah.me.

(Confession: I built Tentacle.✋ It helps you keep a clean inbox by combining your favourite blogs into one weekly newsletter.)

OTHER POSTS

Powered By Swish