Push deploy a Laravel application for free with GitHub Actions cover image

Push deploy a Laravel application for free with GitHub Actions

Samuel Štancl • April 20, 2020

For many people/teams, it makes sense to use Ploi (referral link) or Forge. You get automatic server provisioning, push deploys, backups, and other features. You can also use a Platform-as-a-Service like Heroku — you'll get push deploys but won't get servers.

However, if you have experience in configuring servers, you might be interested only in the push to deploy part and don't want to pay for other services just to get this one feature.

Here's how you can configure push deploys yourself, for free, using GitHub Actions.


A few notes

The git set up

So the code flows like this: master --> production --> deploy.

1. Server deployment script

Add this bash script to your repository, and name it server_deploy.sh:

set -e

echo "Deploying application ..."

# Enter maintanance mode
(php artisan down --message 'The app is being (quickly!) updated. Please try again in a minute.') || true
    # Update codebase
    git fetch origin deploy
    git reset --hard origin/deploy

    # Install dependencies based on lock file
    composer install --no-interaction --prefer-dist --optimize-autoloader

    # Migrate database
    php artisan migrate --force

    # Clear cache
    php artisan optimize

    # Reload PHP to update opcache
    echo "" | sudo -S service php7.4-fpm reload
# Exit maintenance mode
php artisan up

echo "🚀 Application deployed!"

The process explained:

A few notes:

2. Local deployment script

You run this script in your local environment when you want to deploy to production. Ideally, if you work in a team, you'll also have a CI Action with the phpunit safeguard for pull requests targeting the production branch.

Store this as deploy.sh:

set -e


(git push) || true

git checkout production
git merge master

git push origin production

git checkout master

This script is simpler. We're running tests, pushing changes (if we have not pushed yet; it's assumed we're on master), switching to production, merging changes from master and pushing production. Then we switch back to master.

3. The GitHub Action

Store this as .github/workflows/main.yml

name: CD

    branches: [ production ]

    runs-on: ubuntu-latest
    - uses: actions/[email protected]
        token: ${{ secrets.PUSH_TOKEN }}
    - name: Set up Node
    uses: actions/[email protected]
        node-version: '12.x'
    - run: npm install
    - run: npm run production
    - name: Commit built assets
    run: |
        git config --local user.email "[email protected]"
        git config --local user.name "GitHub Action"
        git checkout -B deploy
        git add -f public/
        git commit -m "Build front-end assets"
        git push -f origin deploy
    - name: Deploy to production
    uses: appleboy/[email protected]
        username: YOUR USERNAME GOES HERE
        password: ${{ secrets.SSH_PASSWORD }}
        script: 'cd /var/www/html && ./server_deploy.sh'


Note that you need to store two secrets in the repository:


With all this set up, install your Laravel application into /var/www/html once and checkout to the deploy branch.

For all subsequent deploys all you need to do is run this command from master in your local environment:


Or you can merge into production.

Performance and robustness

This approach is robust because it makes it impossible for a request to be received when the codebase and database are out of sync — thanks to artisan down. And it's also quite performant, with only the necessary things happening on the server, minimizing downtime.

See how this action runs:

A screenshot of the Action running on GitHub

The Deploy to production step took only 13 seconds, and the application downtime is actually shorter than that — part of the 13 seconds is GitHub setting up the appleboy/ssh-action action template (before actually touching your server).

Hope you found this useful.

You can sign up to my newsletter here or follow me on Twitter @samuelstancl.