Push deploy a Laravel application for free with GitHub Actions
How to set up push deploys for Laravel applications with GitHub Actions
Note: An updated version of this article was published on Laravel News
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 configured server running nginx with PHP
- A GitHub repository
A few notes
- Front-end assets are being compiled by GitHub. Not your local computer, nor your server.
- Tests are being run locally, not in the CI Action. The reason for this is that you might want to deploy a hot-fix that would break tests. Feel free to change this.
The git set up
public/assets are in
.gitignore, but are built on GitHub
masterbranch is used for development
productionis pushed and deployed
deployis created by the Action, by taking
productionand adding a commit with the built assets
So the code flows like this:
master --> production --> deploy.
1. Server deployment script
Add this bash script to your repository, and name it
#!/bin/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:
- We're putting the application into maintenance mode and showing a sensible message to the users.
- We're fetching
deployand hard resetting the local branch to the version on
- We're updating composer dependencies based on the lock file
- We're running database migrations
- We're updating Laravel & php-fpm caches
- We're putting the server back up
A few notes:
- The server is on the
- We're putting the application down for the shortest duration possible -
composer install, migrate, update cache. The app needs to go down. It would be irresponsible to simply
git pulland migrate on a production server. The code and database state need to be in sync.
artisan downexits with 1 when the app is already down, which is why we're doing the
() || truewrapping, to be able to re-deploy after a failed deploy, without manual intervention.
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
Store this as
#!/bin/sh set -e vendor/bin/phpunit (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
3. The GitHub Action
Store this as
name: CD on: push: branches: [ production ] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/[email protected] with: token: $ - name: Set up Node uses: actions/[email protected] with: 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] with: username: YOUR USERNAME GOES HERE host: YOUR SERVER'S HOSTNAME GOES HERE password: $ script: 'cd /var/www/html && ./server_deploy.sh'
- we set up Node
- we build the front-end assets
- we force-checkout to
deployand commit the assets
- we force-push the
- we connect to the server via SSH and execute
server_deploy.shin the webserver root
Note that you need to store two secrets in the repository:
- a Personal Access Token for a GitHub account with write access to the repository
- the SSH password
With all this set up, install your Laravel application into
/var/www/html once and checkout to the
For all subsequent deploys all you need to do is run this command from
master in your local environment:
Or you can merge into
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:
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.