How To Deploy A Laravel Project To DigitalOcean Droplet Using Github Action
Tags: digitalocean, php, laravel, github
Published: 2023-03-04 20:46:45

In this article, I will walk you through the step-by-step process of how you can deploy your Laravel application on the digital-ocean droplet, Before we get started below are some of the prerequisites for you to follow along in this article.

## Prerequisites
To follow along in the article you should have a Laravel project already built and pushed to your GitHub repository, a basic understanding of [GitHub Actions](https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions), and a digital-ocean account required. You can get started with Laravel [here](https://laravel.com/), and get started on Digital-ocean [here](https://cloud.digitalocean.com/registrations/new). You will also need to create a droplet and set up your droplet, the following articles below can get you started.
1. [Create a droplet](https://docs.digitalocean.com/products/droplets/how-to/create/)
2. [Initial setup for a droplet](https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-22-04)
3. [Install the LEMP stack on the droplet](https://www.digitalocean.com/community/tutorials/how-to-install-linux-nginx-mysql-php-lemp-stack-on-ubuntu-22-04)

## Digital Ocean
Digital Ocean is a unique cloud hosting provider that offers cloud computing services to business entities so that they can scale themselves by deploying DigitalOcean applications that run parallel across multiple cloud servers without compromising on performance!

## Github Action
GitHub Actions is a continuous integration and continuous delivery (CI/CD) platform that allows you to automate your build, test, and deployment pipeline.
You can create workflows that build and test every pull request to your repository or deploy merged pull requests to production. GitHub Actions goes beyond just DevOps and lets you run workflows when other events happen in your repository.
For example, you can run a workflow to automatically add the appropriate labels whenever someone creates a new issue in your repository. GitHub provides Linux, Windows, and macOS virtual machines to run your workflows, or you can host self-hosted runners in your own data center or cloud infrastructure.

## Overview of GitHub Action Workflow
GitHub Action workflow consists of one or multiple jobs which contain one or multiple steps. To deploy our application, we need to create the following jobs:

**Create GitHub Action build artifacts for deployment**
We want to achieve the following for our Laravel application artifacts:
1. Install NPM dependencies.
2. Compile CSS and Javascript assets.
3. Install Composer dependencies.
4. Archive our build and remove unnecessary data (e.g., node_modules).
5. Store our archive so we can deploy it to our servers.

**Prepare release on all our servers**
We want the release preparation job to do the following:
1. Ensure we have a directory that holds every release.
2. Ensure we have a storage directory that shares data between releases.
3. Ensure we have a current directory that links to the active release.
4. Extract our build files into our releases directory.

**Run optional before hooks**
This is an optional feature, but I want to execute specific commands before the release is activated (e.g., chmod directories). So there needs to be a way to configure these so-called before hooks.

**Activate the release**
Now we are ready to activate our new release without any downtime. We can do this by changing symbolic links, this essentially swaps the underlying release, which is linked to our current directory, to a new release inside our releases directory. I'm running PHP FPM on my servers, so I also want to reload PHP FPM to detect the changes.

**Run optional after hooks**
This is an optional feature as well, but I want to execute specific commands after the release is activated to send a notification that my deployment is completed for example. So there needs to be a way to configure these so-called after hooks.

**Cleaning up**
Given that we are uploading and extracting new releases, we take up more disk space after each release. To make sure we don’t end up with thousands of releases and a full disk, we need to limit the number of release artifacts living on every server.

## Project scaffolding
To start you will need a GitHub repository as testing your workflow requires you to commit and push your workflow .yml file, you should have one already with your Laravel project, I recommend you create a separate branch for this, Be sure to verify that in your project everything works before you continue.
Lastly, from your project root directory create a new workflow file, feel free to give it any name you would like and place it in the .github/workflows.

```
name: Deploy Application

on:
  push:
    branches: [ master ]

jobs:
  # Magic
```
Inside the .github/workflows/any_name_you_like.yml file write the following, make sure you choose a different branch if you don’t want to clutter your commit history, as you might end up pushing to GitHub several times, as you follow along.

## Create deployment Artifacts
let's kick off by checking out our project by using the predefined [checkout action by GitHub](https://github.com/actions/checkout). Before we have something to deploy, we will start the build our Laravel application as we would typically do.

```
# // code from earlier is ommited for clearity

jobs:
  create-deployment-artifacts:
    name: Create deployment artifacts
    runs-on: ubuntu-latest

  steps:
  - uses: actions/checkout@v3
```
GitHub will checkout the code from our repository in the container; no further steps are necessary. Now that we have our code, we can continue compiling our assets.

```
# // code from earlier is ommited for clearity
 
- name: Compile CSS and Javascript
    run: |
      npm install
      npm run prod
```

> Tip: If you use a CDN to deliver your static files, be sure to implement your own solution. The front-end assets are now shared amongst all servers individually, which is not ideal. It may impact your website speed since assets could be loaded from different servers on every request depending on your load-balancer.

We can now continue with our back-end code. Before we can install any composer packages, we need to make sure PHP is installed first. We will use the `setup-php` action by [Shivam Mathur](https://github.com/shivammathur/setup-php), which makes this a breeze.

```
# // code from earlier is ommited for clearity

  - name: Compile CSS and Javascript
    run: |
      npm ci
      npm run prod

  - name: Configure PHP 8.1
    uses: shivammathur/setup-php@master
    with:
      php-version: 8.1
      extensions: mbstring, ctype, fileinfo, openssl, PDO, bcmath, json, tokenizer, xml
```

This will configure PHP 8.1 and install the required extensions. If your application requires additional extensions, be sure to add them to the list. We can continue by installing our Composer dependencies.

```
# // code from earlier is ommited for clearity

  - name: Composer install
    run: |
    composer install --no-dev --no-interaction --prefer-dist
```
Time to test out what we’ve got so far! Commit your code changes and push them to GitHub. Once you’ve done so, visit the Actions page (https://www.github.com/<username>/<repository>/actions)

You can click each job to see the execution output. If a job fails, it will show a red cross instead of a checkmark. The execution output will often provide you with the information you need to resolve the issue.

We’ve successfully compiled our front-end assets and installed our Composer dependencies. Now we need to store the results. GitHub provides an Upload-Artifact helper. This will help us to share the GitHub Actions artifacts between jobs.

You can upload single files, multiple files, and directories. Since we just want all our files deployed, I prefer to create a TAR archive, so we have a single file to work with. You could also create a ZIP archive, but this will require installing additional software in the build container as Ubuntu doesn’t ship with the required libraries.

```
  - name: Create deployment artifact
    run: tar -czf app.tar.gz *
```
This will create a new tar archive called app.tar.gz containing all the files, including the additional build artifacts we've made in the previous steps.

This works just fine, but the archive now contains files we don’t need, like the node_modules directory. We only required these to run the npm run production command. Let's fix this by excluding directories from our archive.

```
# //
- name: Create deployment artifact
  run: tar -czf app.tar.gz --exclude=*.git --exclude=node_modules --exclude=tests *
```
Our archive will now skip the .git, node_modules, and tests directories from our archive. If you have additional files that are not required to be on your production server, exclude them now. By making the archive smaller, your deployment will be quicker.

I want to change our archive's filename so it's easier to identify which commit our archive contains. GitHub has some global variables you can use in your .yml file, so let's change the name to the commit hash.