Skip to content

Instantly share code, notes, and snippets.

@rkeppner
Last active June 1, 2017 00:09
Show Gist options
  • Save rkeppner/3928314b9af725a19412 to your computer and use it in GitHub Desktop.
Save rkeppner/3928314b9af725a19412 to your computer and use it in GitHub Desktop.
new-heroku-pipeline.md

Creating a New Heroku Pipeline with Multiple Apps for Laravel Hosting

Scope

These directions are for setting up a new Heroku pipeline and adding 2 apps to it: staging and production. This is intended for hosting a Laravel app, which may not be the configuration for all projects.

Prerequisites

You need to have previously configured your Laravel Homestead environment and checked out the codebase from the GitHub repo. Those directions are found elsewhere.

Step-by-Step Instructions

  1. If you haven't already done so, create an account at heroku.com.

  2. If you haven't done so previously, install the Heroku Toolbelt on your machine.

  3. If not done before, install the Heroku pipeline addon:
    heroku plugins:install heroku-pipelines

  4. Change the current directory to the Laravel app project directory (e.g. ~/Code/russellkeppner.com).

  5. Modify bootstrap/app.php by adding the following code above the return $app; section:

    /*
    |--------------------------------------------------------------------------
    | Set Environment Variables
    |--------------------------------------------------------------------------
    |
    | Split the Heroku-provided environment variables into the individual
    | pieces that Laravel expects.
    |
    */
    
    if ($db_url = getenv('DATABASE_URL')) {
        if (parse_url($db_url, PHP_URL_SCHEME) == 'postgres') {
            putenv('DB_CONNECTION=' . 'pgsql');
        }
    
        putenv('DB_USERNAME=' . parse_url($db_url, PHP_URL_USER));
        putenv('DB_PASSWORD=' . parse_url($db_url, PHP_URL_PASS));
        putenv('DB_HOST=' . parse_url($db_url, PHP_URL_HOST));
        putenv('DB_PORT=' . parse_url($db_url, PHP_URL_PORT));
        putenv('DB_DATABASE=' . ltrim(parse_url($db_url, PHP_URL_PATH), '/'));
    }
    
    if ($redis_url = getenv('REDIS_URL')) {
        putenv('REDIS_USERNAME=' . parse_url($redis_url, PHP_URL_USER));
        putenv('REDIS_PASSWORD=' . parse_url($redis_url, PHP_URL_PASS));
        putenv('REDIS_HOST=' . parse_url($redis_url, PHP_URL_HOST));
        putenv('REDIS_PORT=' . parse_url($redis_url, PHP_URL_PORT));
    }
  6. Modify config/database.php Redis configuration to match the following:

    'redis' => [
    
        'cluster' => false,
    
        'default' => [
            'host'     => env('REDIS_HOST', '127.0.0.1'),
            'username' => env('REDIS_USERNAME', null),
            'password' => env('REDIS_PASSWORD', null),
            'port'     => env('REDIS_PORT', 6379),
            'database' => env('REDIS_DATABASE', 0),
        ],
    
    ],
  7. Change config/app.php logging configuration to:

    'log' => env('APP_LOG', 'errorlog'),
  8. Configure Laravel to trust the Heroku load balancer

    • Add the Laravel Trusted Proxy package:

      $ composer require fideloper/proxy
    • Add the service provider in config/app.php:

      'providers' => [
          # other providers omitted
          Fideloper\Proxy\TrustedProxyServiceProvider::class,
      ];
    • Publish the package config file:

      $ php artisan vendor:publish --provider="Fideloper\Proxy\TrustedProxyServiceProvider"
    • Register the HTTP Middleware in app/Http/Kernel.php:

      protected $middleware = [
          # other middlewares omitted
          \Fideloper\Proxy\TrustProxies::class,
      ];
    • Edit the published config/trustedproxy.php config file:

      return [
          'proxies' => '*',
          'headers' => [
              Illuminate\Http\Request::HEADER_FORWARDED => null,
              Illuminate\Http\Request::HEADER_CLIENT_IP => 'X_FORWARDED_FOR',
              Illuminate\Http\Request::HEADER_CLIENT_HOST => null,
              Illuminate\Http\Request::HEADER_CLIENT_PROTO => 'X_FORWARDED_PROTO',
              Illuminate\Http\Request::HEADER_CLIENT_PORT => 'X_FORWARDED_PORT',
          ]
      ];

Create Heroku pipeline and apps

Execute these commands in your local environment, to add the Heroku remotes for promotion:

  1. heroku apps:create --addons "heroku-postgresql,heroku-redis" -b https://github.com/heroku/heroku-buildpack-php -r production russellkeppner
  2. heroku pipelines:create -s production -r production russellkeppner
  3. heroku config:set APP_KEY=$(php artisan --no-ansi key:generate --show) -r production
  4. heroku apps:create --addons "heroku-postgresql,heroku-redis" -b https://github.com/heroku/heroku-buildpack-php -r staging russellkeppner-staging
  5. heroku pipelines:add -s staging -r staging russellkeppner
  6. heroku config:set APP_KEY=$(php artisan --no-ansi key:generate --show) -r staging
  7. git config heroku.remote staging

Add configuration files needed by Heroku

  • First, add the following at the top of the "scripts": {} section in composer.json

    "warmup": [
        "php artisan config:cache",
        "php artisan route:cache"
    ],
  • Procfile
    Directives for Heroku environment configuration:

    web: composer warmup && $(composer config bin-dir)/heroku-php-nginx -C nginx.conf public/
    
  • nginx.conf
    Nginx configuration for the Heroku environment:

    # prevent HTTPoxy vulnerability
    fastcgi_param HTTP_PROXY "";
    
    if ($host !~ "^(staging|www)\.expamle\.com$") {
        return 555;
    }
    
    error_page 555 = @auth;
    
    location @auth {
        auth_basic "Restricted";
        auth_basic_user_file /app/basic.htpasswd;
        try_files $uri @rewriteapp;
    }
    
    location / {
        # try to serve file directly, fallback to rewrite
        try_files $uri @rewriteapp;
    }
    
    location @rewriteapp {
        # rewrite all to index.php
        rewrite ^(.*)$ /index.php$1 last;
    }

    Modify the domain name appropriately for the environment.

  • basic.htpasswd Create this file using a tool like the Htpasswd Generator.

Deploy code to Heroku

  1. Commit changes to staging via Git (command line instructions given below, but this can also be done in SourceTree):

    $ git add -A .
    $ git commit -m "Added Heroku configuration."
    $ git push staging master
  2. Share/QA the results at: http://russellkeppner-staging.herokuapp.com

  3. When approved, promote the changes to production:

    $ heroku pipelines:promote -r staging
  4. Double-check site at: http://russellkeppner.herokuapp.com

Notes

To run a migration on a Heroku app:

$ heroku run -r staging 'php artisan migrate'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment