Deploying Laravel Applications from GitHub to a Server Made Easy
As your Laravel application grows, manual deployments can become both time-consuming and risky. Continuous Integration and Continuous Deployment (CI/CD) pipelines address this by automating and standardizing the process—from running tests to delivering code changes to production servers.
This guide will walk you through setting up a CI/CD pipeline that deploys your Laravel application from GitHub directly to a remote server using GitHub Actions. Along the way, you’ll learn how GitHub’s automation triggers work, how to review the pipeline runs, and how to integrate these steps into your normal development workflow.
What is GitHub Actions and Why Use It?
GitHub Actions is an automation service provided by GitHub that allows you to run workflows (automated tasks) in response to events on your repository. These events can include pushing commits, creating pull requests, or running on a schedule. By using GitHub Actions for CI/CD, you can:
- Automate Testing: Ensure your code passes tests before it lands in production.
- Consistent Deployments: Automatically run the same set of steps every time you deploy, reducing the risk of forgetting a crucial step.
- Improve Developer Velocity: Commit code, let the pipeline handle the rest. No more manual FTP uploads or sshing into servers to run commands.
How the Pipeline Initiates and When It Runs
The pipeline is defined in a special file within your repository. Once you create this file, any changes you push to the branch you specify (e.g., main
) will automatically trigger the workflow. In other words, the pipeline is initiated by a GitHub “event”—in this case, a push event on the main
branch.
After you push your code:
- GitHub detects the push event.
- The workflow defined in your repository’s
.github/workflows
directory is triggered. - GitHub Actions runs the steps you’ve defined—checking out code, running tests, building assets, and deploying to the server.
You can view the entire process in real-time by visiting the Actions tab on your GitHub repository page. Each run provides detailed logs and statuses, making it easy to see where something may have gone wrong.
Prerequisites
Before setting up the pipeline, ensure you have:
- GitHub Repository: Your Laravel application code should be on GitHub.
- Remote Server with SSH Access: A server running PHP and Composer. You’ll deploy your code here. Make sure you can SSH into it.
- SSH Keys: Generate an SSH key pair and add the public key to your server’s
~/.ssh/authorized_keys
. Keep the private key handy; you’ll add it as a secret in GitHub. - .env File on Server: Set up your application’s
.env
file on the server beforehand, so the application can connect to the database and other services once deployed.
Setting Up SSH Keys for GitHub Actions
To allow GitHub Actions to deploy code, it must connect to your server over SSH. First, generate keys locally if you haven’t already:
ssh-keygen -t ed25519 -C "your_email@example.com"
Add the public key to your server:
cat id_ed25519.pub | ssh your-user@your-server "cat >> ~/.ssh/authorized_keys"
Now, add the private key to your GitHub repository’s secrets:
- Go to your repository’s page on GitHub.
- Click on Settings > Secrets and variables > Actions.
- Click New repository secret.
- Name it
SSH_PRIVATE_KEY
and paste in the private key’s contents.
Also, run this to get the server’s SSH host key and add it to a secret named SSH_KNOWN_HOSTS
:
ssh-keyscan your-server-ip-or-hostname
Other Necessary Secrets
Add these additional secrets to GitHub:
SSH_USER
: The SSH username (e.g.,deploy
).SSH_HOST
: Your server’s domain or IP.
Creating the CI/CD Workflow with GitHub Actions
In your local repository, create a new file at .github/workflows/deploy.yml
(if it doesn’t exist yet). Commit and push it. This file defines your pipeline:
name: Deploy Laravel
on:
push:
branches: [ "main" ]
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
# 1. Check out code from GitHub
- name: Check out code
uses: actions/checkout@v2
# 2. Set up PHP environment
- name: Set up PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.1'
extensions: mbstring, mysql, intl, bcmath
coverage: none
# 3. Install Composer dependencies without dev packages for production optimization
- name: Install Composer dependencies
run: composer install --no-dev --prefer-dist --no-interaction --optimize-autoloader
# 4. Run Laravel's test suite to ensure code quality
- name: Run Laravel Tests
run: php artisan test
# 5. Add SSH key from secret to authenticate with the server
- name: Add SSH key
uses: webfactory/ssh-agent@v0.5.4
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
# 6. Add known hosts so SSH trusts the server
- name: Add known hosts
run: |
mkdir -p ~/.ssh
echo "${{ secrets.SSH_KNOWN_HOSTS }}" >> ~/.ssh/known_hosts
# 7. Sync code and run post-deployment steps
- name: Deploy to Server
run: |
REMOTE_USER=${{ secrets.SSH_USER }}
REMOTE_HOST=${{ secrets.SSH_HOST }}
REMOTE_DIR=/var/www/your-project
# Sync files excluding the .git folder
rsync -avz --delete --exclude=".git" -e "ssh -o StrictHostKeyChecking=no" ./ $REMOTE_USER@$REMOTE_HOST:$REMOTE_DIR
# Run Laravel migrations, cache optimizations, etc.
ssh -o StrictHostKeyChecking=no $REMOTE_USER@$REMOTE_HOST << 'EOF'
cd /var/www/your-project
composer install --no-dev --optimize-autoloader --no-interaction
php artisan migrate --force
php artisan config:cache
php artisan route:cache
php artisan view:cache
EOF
How It Works:
- Trigger: A push to the
main
branch initiates the pipeline. - Checkout & Setup: The code is checked out, and PHP is set up with required extensions.
- Tests: The test suite runs to ensure there are no issues.
- Deployment: If tests pass, the code is synced to the server, and Laravel commands are run to finalize the deployment.
Initiating and Monitoring Your Pipeline
After you’ve committed and pushed deploy.yml
to the main
branch, the pipeline runs automatically. You don’t need to press any button to start it—it just happens whenever you push to main
.
To see the pipeline in action:
- Go to your repository on GitHub.
- Click the "Actions" tab. Here, you’ll see a list of all recent workflow runs.
- Select the latest run. You’ll see logs, status icons, and detailed output from each step.
If the run is successful, your code should now be deployed on your server. If there’s a failure, the logs help pinpoint the problem.
Initial Server Setup
Before the first automated deployment completes successfully, ensure on your server:
- Your
.env
file is properly configured with production credentials. - You have run
php artisan key:generate
if the application key wasn’t set initially. - Directories like
storage
andbootstrap/cache
are writable by the web server user.
Troubleshooting Common Issues
If something goes wrong:
- Check GitHub Actions Logs: The logs show exactly where a step failed.
- Check Server Logs: Look at
storage/logs/laravel.log
on your server for runtime errors. - Permissions: Ensure correct file permissions for Laravel’s writable directories.
- SSH Connectivity: Make sure your SSH keys and
SSH_KNOWN_HOSTS
secrets are correct, and the username and host are accurate.
Enhancing Your CI/CD Pipeline
As you get comfortable with your basic CI/CD setup, consider enhancements:
- Branch-based Deployments: Deploy
main
to production anddevelop
to a staging server by adjusting triggers and SSH secrets. - Static Analysis & Code Style Checks: Add steps for tools like
phpstan
orlaravel pint
before deployment. - Asset Management: Run
npm run production
or similar build commands before syncing your code, ensuring front-end assets are optimized. - Rollbacks: Use a directory structure that supports multiple releases, allowing you to quickly revert if a deployment fails.
So ...
By combining Laravel, GitHub Actions, and a well-configured remote server, you’ve established a CI/CD pipeline that automatically tests and deploys your code with each push. This reduces manual effort, lowers the chance of errors, and speeds up your development cycle.
Going forward, refine this pipeline further, adding more checks, stages, or even a canary deployment strategy. With every iteration, you’ll bring more stability, reliability, and confidence to your deployment process—letting you and your team focus on building great features rather than worrying about how to ship them.