Intro
Have you ever wondered if you'd had a small (or maybe big) Laravel project and how you can deploy it quite simply an painlessly. When you are developing on your application you also maybe want to deliver new code quickly. In this blog post I will cover this. This can also be set up and extended with autodeploy e.g. with a GitHub Action or another method to make a autodeploy pipeline.
I will cover what I would call the hard parts (At least what I'd considered hard as a newcomer), so there is also some small things to do which I'd not covered in this article, that I'll assume you already know. If not you are welcome to reach out to me on mail/contact form or via Discord. You can find my discord username in the bottom of this article.
The article will cover how to build the actual deployment recipe, and not how to setup autodeploy with e.g. a GitHub Action or another pipline tool, also not a webhost with phpfpm/nginx. I will in the future cover these questions, so I will eventually follow up with some other blog posts.
I do not cover how you can use it with Docker, Kubernetes, Ansible and so on. This can be highly recommended for improved maintainability, to create a more stateless and lower coupled system. The deployer recipe would also work very well in combination with containerization, and forexample Bitbucket pipeline or GitHub action - or whatever suits you best for better maintainability, scalability and a more streamlined delivery of code.
Prerequisites
- You'll need at least an Ubuntu 20.04 server or higher, at least this is what this deploy procedure is currently tested on, but would definitly also work on other distributions. It is of course recommended to use the newest version of whatever distribution you gonna use.
- A laravel app to deploy and a git repo - In this article I am demonstrating how to set it up with GitHub
- Basic Linux knowledge, particular Ubuntu in this article.
- A user should be created on the server, which is the "deploy-user" or the user which also own the application files. This user should also have bash as shell and should be accessible via. SSH from an remote source
- ACL should be installed with a sudo user or root. It can be done with
apt -y install acl
- A function webhost setup with PHP and eventually Nginx or Apache (I'll maybe cover this in another article)
Steps
1. Create deploy.yaml, the deploy recipe for Laravel 10/11 deployer
Add this file to your Laravel-application's codebase, ensure that you call it deploy.yaml
import:
- recipe/common.php
- recipe/composer.php
- recipe/laravel.php
- contrib/npm.php
- contrib/crontab.php
config:
repository: '<put-your-repo-ssh-url-here>'
shared_files:
- .env
shared_dirs:
- storage
writable_dirs:
- storage
- bootstrap/cache
hosts:
production:
remote_user: laraweb
hostname: <hostname-or-ip>
deploy_path: '/var/www/<site-folder-name>'
branch: master
keep_releases: 3
tasks:
crontab:jobs:
- run: 'cd {{release_path}} && crontab -l | { cat; echo "* * * * * php {{release_path}}/artisan schedule:run >> /dev/null 2>&1"; } | crontab -'
custom:down:
- run: 'cd {{release_path}} && php artisan down'
custom:migrate:
- run: 'cd {{release_path}} && php artisan migrate --force'
custom:vendor:publish:
- run: 'cd {{release_path}} && php artisan vendor:publish --force --all'
custom:event:cache:
- run: 'cd {{release_path}} && php artisan event:cache'
custom:config:cache:
- run: 'cd {{release_path}} && php artisan config:cache'
custom:view:cache:
- run: 'cd {{release_path}} && php artisan view:cache'
custom:route:cache:
- run: 'cd {{release_path}} && php artisan route:cache'
npm:run:prod:
- run: 'cd {{release_path}} && npm run build'
- run: 'echo "Build complete"'
deploy:
- deploy:prepare
- deploy:vendors
- custom:down
- artisan:optimize:clear
- artisan:storage:link
- custom:migrate
- custom:vendor:publish
- custom:event:cache
- custom:config:cache
- custom:view:cache
- npm:run:prod
- crontab:jobs
- artisan:up
- deploy:publish
before:
npm:run:prod:
- npm:install
after:
deploy:failed:
- deploy:unlock
deploy:success:
- crontab:sync
Remember to fill out the remote_user, hostname, deploy_path, repository. Some of these values are covered down below in this article, so please read further!
2. Install Deployer.org composer package
The next thing we want to do is to install the Deployer.org composer package.
You can install it with composer require deployer/deployer
3. Setup your github repo and proper access for serveruser to pull repo
As we can see in the deploy recipe, it needs to have a code repo where your Laravel 10/11 app can be cloned from everytime a new deployment takes place.
Usually, it would be something like git@github.com:nsommer89/fancy-laravel-code-repo.git - and remember to select "SSH" when you grab the repo URL. You can use HTTPS if the repo is public - authentication would also work different if you are using HTTPS, which is not covered in this tutorial.
........
config:
repository: '<put-your-repo-ssh-url-here>'
...........
The next thing in this step, is to ensure that the server, which the deployer runs on have access to pull and clone the code from the git repository:
- Login or sudo su to the deploy user and create a new SSH private key with the command
ssh-keygen -t rsa
and ensure to name it proper, e.g. like"github" in this example: /home/laraweb
/.ssh/github Create a ssh-config for the user, ensure that .ssh folder does exists and such
-mkdir /home/laraweb/.ssh
-nano /home/laraweb/.ssh/config
Put the following contents into the SSH config file and ensure to provide the correct IdentityFile path, in this case, it would be proper to call itgithub
Host github.com HostName github.com User git IdentityFile ~/.ssh/github IdentitiesOnly yes
- Set up the public key as an deploy key in GitHub repo.
- Print out the public key to the terminal with this command
cat /home/laraweb/.ssh/github.pub
- Set up your deploy key in the GitHub Repository Settings and find Deploy Keys and copy the key which you'd already outputted in the terminal with the
cat
command. Give it a proper name like 'production' or 'staging'.
Now the deploy user on the server should have access to pull code from the code repo, everytime a new deploy takes place.
4. Setup proper access from your development machine to the application/server user on the remote server
Now I'll cover how you can ensure that you have the correct access from your development machine to the remote server via. the deploy user on the server. When a new deployment is being runned, this will be done from your local machine - and the deployer will use SSH to execute all the commands defined in deploy.yaml on the remote server.
5. Ensure that you have a deploy path and correct permissions is set
You can deploy your application to a folder, in the path /var/www, that's what I usually do. Before you can do that you'll have to set the correct permissions. Run the following commands:
mkdir /var/www/<your website name>
mkdir /var/www/<your website name>/shared
chown -R laraweb:laraweb /var/www/<your website name>
And of course remember to fill out the blanks, in this case <your website name>
, usually i just call it the hostname - e.g. example.com, so the final deploy path will be /var/www/example.com
6. Add the laravel .env file to the server
The next step is to add your laravel .env file and configure it properly with database and such. You'll prolly know the drill.
Add the .env file so it will have the path /var/www/<your website name>/shared/.env
Now you are actually ready to run your first deployment.
7. Ensure that deployer from your local development machine has SSH access to the application user on the remote server
When you execute a deployment, you are doing it from your local machine in the terminal from your codebase/work folder. This will require you to set up a SSH key from your local user on your own machine to the remote deployment user, with the following steps:
- Grab your own SSH public key from your local machine, with e.g.
cat /home/niko/.ssh/<your ssh-key>.pub
- The important thing is that you've set your local ssh config up with the correct key and settings. See down below, where I'd provided an example of a SSH config on your local machine further down. - Copy your SSH public key - Add it to the remote server deployment user's .ssh folder in the path /home/laraweb/.ssh/authorized_keys. If the file authorized_keys does not exists, create it:
nano /home/laraweb/.ssh/authorized_keys
- Important: Remember to do this with the deployment user, to ensure the correct permissions. If you're creating it with sudo command or with root user, the deployment user will not have access to read it. So I'll recommend doing it with the deployment user, or at least afterwards ensure that the authorized_keys file is owned by the deployment user, e.g. in this example called laraweb
- Copy your local SSH public key to the remote server user's authorized_keys file, and you are all set.
Example of entry to add on your local development machine: (Run
nano ~/.ssh/config
)
Add these lines in the bottom of your ssh-config (~/.ssh/config)Host <ip or hostname> HostName <ip or hostname> User <laraweb-or-your-own-defined> Port 22 IdentityFile ~/.ssh/<name of your ssh key>
If you'd provided correct values in your local ssh config for Host, HostName, IdentityFile, you should be able to test if the SSH key is set correctly up by running:
ssh <ip or hostname>
8. Deploy your code
You will now meet your final boss - the actual first deployment. This is the crucial point, where you might meet some challenges.
First of all - to run a deployment to what we have defined as 'production' in the deploy.yaml file, whereas we'd defined server ip, deploy user, branch name and deploy path, we have to run the following command:
./vendor/bin/dep deploy production
When you run this, it might run smooth, or you will maybe meet some challenges as i mentioned.
To troubleshoot eventually errors, you can add the -vvv flag to get a full error message. I always do that in the first couple of deployments, so that I eventually have a full trace as deployment output of eventual errors. It can be handy sometimes to see the details. The command to run deployer with full error tracing is:
./vendor/bin/dep deploy production -vvv
The most common errors I usually get at this point is related to permissions or sometimes i forget to install ACL. Read the prerequisites section to see how you can install ACL. (Access Control List)
Finally..
This is usually how I deploy Laravel applications at the moment. If you have any feedback to how i can improve this set up, it's more than welcome. Feedback is good - the recipe should also change over time, due to changes and different requirements.
Nothing else left to say thank you for reading this article. Have any questions? Please feel free to contact me via. this site or the contact form. You are also welcome to add me on LinkedIn or Discord (xniko89).
Have a fantastic day!