Deploy Laravel app using CircleCI to shared hosting

Laravel is a very good and mature PHP framework which has really good documentation. It is also easy to understand for a first time developer. Its used by both junior and senior developers all around the world.

Most of the cheap shared hosting will support PHP. and Laravel is good candidate for small static site as well as a full blown web application.

Here i am going to explain how i used CircleCI to do all the testing a deployment to a shared hosting like GoDaddy, NameCheap, etc. All of the tools we’re going to cover will send your data over the internet in order to deploy your software. That being the case, having a secure connection is very important. Many examples in this post will use SSH keys to encrypt the connection. I suggest creating a brand new SSH keypair specifically for CircleCI. My favorite resource on creating SSH keys is by GitHub.

The private key should be stored on CircleCI via the web app. You can follow instructions here. The public key should be stored in the authorized_keys file for the user on the remote server you’d like to deploy to. For example, if you were deploying to a Linux server prod@example.com, the public key should be appended to the file /home/prod/.ssh/authorized_keys on that remote machine.

First lets build a simple Laravel app

You can follow the well documented Laravel documentation here to create a sample Laravel application and push it to bitbucket.

Rsync

rsync is a file transfer tool that has been around for a while. The name comes from “remote synchronization,” which means it allows you to transfer files from one directory or machine to another, but also keep them in sync. This is useful because this purpose means that rsync can compare files on a remote server and not transfer for them if they already exist, making for faster and more efficient deployments. This works very well when you have several files to transfer, some updated and some not, such as when building a statically-generated website with Hugo or Jekyll. Basic Rails, Flask, and React apps can be deployed this way as well.

Configuring the CircleCI

Login to CircleCI or create an account if you do not have any.

The official CircleCI Images and the third-party CI Builds Images don’t. However, installing it is fairly straightforward:

# Ubuntu & Debian based Docker images
sudo apt-get update && sudo apt-get install rsync

Setting a new SSH key in GoDaddy

Go to Cpanel and create a new ssh key. You can name the ssh key anything. But make sure you do not enter any passphrase. This is important.

CircleCI example

# PHP CircleCI 2.0 configuration file
#
# Check https://circleci.com/docs/2.0/language-php/ for more details
#
version: 2
jobs:
  build:
    docker:
      # Specify the version you desire here
      - image: circleci/php:7.1-node-browsers

      # Specify service dependencies here if necessary
      # CircleCI maintains a library of pre-built images
      # documented at https://circleci.com/docs/2.0/circleci-images/
      # Using the RAM variation mitigates I/O contention
      # for database intensive operations.
      # - image: circleci/mysql:5.7-ram
      #
      # - image: redis:2.8.19
    branches:
      only:
        - master
    steps:
      - checkout
      - add_ssh_keys:
          fingerprints:
            - ${fingerprint}

      - run: sudo apt update # PHP CircleCI 2.0 Configuration File# PHP CircleCI 2.0 Configuration File sudo apt install zlib1g-dev libsqlite3-dev
      - run: sudo apt-get install rsync

      # Download and cache dependencies
      - run: sudo composer self-update
      - restore_cache:
          keys:
            - v1-dependencies-{{ checksum "composer.lock" }}
            # fallback to using the latest cache if no exact match is found
            - v1-dependencies-

      - run: composer install -n --prefer-dist
      - save_cache:
          key: v1-dependencies-{{ checksum "composer.lock" }}
          paths:
            - /vendor

      - restore_cache:
          keys:
            - node-v1-{{ checksum "yarn.lock" }}
            - node-v1-
      - run: yarn install
      - save_cache:
          key: node-v1-{{ checksum "yarn.lock" }}
          paths:
            - /node_modules

      # Adding Host key to know_hosts file
      - run:
          name: 'Adding Host key to know_hosts file'
          command: ssh-keyscan -t rsa ${server} >> ~/.ssh/known_hosts

      # Create the env file
      - run:
          name: 'Create env file'
          command: |
            echo -e 'APP_NAME="Laravel"' > .env
            echo -e 'APP_ENV=production' >> .env
            echo -e 'APP_KEY='${APP_KEY} >> .env
            echo -e 'APP_DEBUG=false' >> .env
            echo -e 'LOG_CHANNEL="daily"' >> .env
            echo -e 'DB_CONNECTION="mysql"' >> .env
            echo -e 'DB_HOST="127.0.0.1"' >> .env
            echo -e 'DB_PORT="'${DB_PORT}'"' >> .env
            echo -e 'DB_DATABASE="'${DB_DATABASE}'"' >> .env
            echo -e 'DB_USERNAME="'${DB_USERNAME}'"' >> .env
            echo -e 'DB_PASSWORD="'${DB_PASSWORD}'"' >> .env
            echo -e 'BROADCAST_DRIVER=log' >> .env
            echo -e 'CACHE_DRIVER=file' >> .env
            echo -e 'QUEUE_CONNECTION=sync' >> .env
            echo -e 'SESSION_DRIVER=file' >> .env
            echo -e 'SESSION_LIFETIME=120' >> .env

      # prepare public files
      - run: yarn run prod

      # Create rsync exclude list
      - run:
          name: 'Create rsync exclude list'
          command: |
            echo -e '.circleci' > exclude-list.txt
            echo -e '.git' >> exclude-list.txt
            echo -e 'node_modules' >> exclude-list.txt
            echo -e '.editorconfig' >> exclude-list.txt
            echo -e '.env.example' >> exclude-list.txt
            echo -e '.gitattributes' >> exclude-list.txt
            echo -e '.gitignore' >> exclude-list.txt
            echo -e '.styleci.yml' >> exclude-list.txt
            echo -e 'exclude-list.txt' >> exclude-list.txt

      # Deploy
      - deploy:
          command: |
            rsync -azvW --delete --exclude-from 'exclude-list.txt' ~/crowbid/ ${username}@${server}:~/public_html/
            ssh -p 22 -i ~/.ssh/id_rsa ${username}@${server} 'cd public_html/; composer install --optimize-autoloader --no-dev; php artisan migrate --force; php artisan config:cache; php artisan route:cache'

Make sure you add the environment variables to the CircleCI project.

And now when you push you code th master branch in bitbucket CircleCI will start the build process and will push all the changes to the ‘public_html‘ folder.

Did it work for you. Please let me know in the comments.