We'll learn to use Laravel's Envoy to handle deployment over SSH.We'll use Laravel's Envoy to deploy a PHP application to a production server. This will make a new release directory, clone the repository into it, run any needed build steps, and then finally swap the new code out with the older code, so that Nginx and PHP-FPM serve the new code..
Setup
The remote server is at IP address 104.236.85.162
. It has a user deployer
, and I created a local SSH key pair to connect to it. That key pair is called id_deployex
.
To generate this key pair, I used the following command:
ssh-keygen -t rsa -b 4096 -C "fideloper@gmail.com" -f id_deployex
To copy that to remove server (since I'm on a Vagrant server, not my Mac, which may not have this command), I can use the following to copy the public key into the deployer
user's authorized_keys
file:
# Since not running on mac, we have this command
# This will ask for the user's password to log in with, and assumes
# the server allows password-based authentication
ssh-copy-id -o "PubkeyAuthentication no" deployer@104.236.85.162
On the remote (production) server, we create SSH key to use within GitHub as a Deploy Key. This lets us push/pull to the GitHub repository from the production server, which we'll use to deploy new code.
# Create key, by default named "id_rsa"
ssh-keygen -t rsa -b 4096 -C "fideloper@gmail.com"
# Echo out the public key,
# so we can copy it and paste it into GitHub
cat ~/.ssh/id_rsa.pub
Then we can get to using Envoy with our Laravel application.
Install Envoy Globally
# Install envoy globally. May need to ensure that
# the composer vendor/bin directory is in your PATH
# so the envoy command is found
composer global require "laravel/envoy=~1.0"
# fails, if the PATH variable is not set with composer's
# bin directory
which envoy
vim ~/.profile
If the which envoy
test fails (if there's no output saying the path to the envoy command), edit .profile
, .bashrc
or whatever is appropriate for your system. Within this file, we'll append the following near the bottom. Adjust the file path as needed for your development server.
# Prepend the composer bin directory to your PATH
# This path is specific to my vagrant server
export PATH=/home/vagrant/.composer/vendor/bin:$PATH
Finally we can create an envoy file for our project:
cd /vagrant/app
vim Envoy.blade.php
Here's a sample we can use to test that Envoy can connect to our server:
@servers(['web' => 'deploy-ex'])
<?php $whatever = 'hola, whatever'; ?>
@task('deploy', ['on' => 'web'])
echo {{ $whatever }}
@endtask
To run that:
envoy run deploy
Now we'll employ a deployment strategy with Envoy:
@servers(['web' => 'deploy-ex'])
<?php
$repo = 'git@github.com:Servers-for-Hackers/deploy-ex.git';
$release_dir = '/var/www/releases';
$app_dir = '/var/www/app';
$release = 'release_' . date('YmdHis');
?>
@macro('deploy', ['on' => 'web'])
fetch_repo
run_composer
update_permissions
update_symlinks
@endmacro
@task('fetch_repo')
[ -d {{ $release_dir }} ] || mkdir {{ $release_dir }};
cd {{ $release_dir }};
git clone {{ $repo }} {{ $release }};
@endtask
@task('run_composer')
cd {{ $release_dir }}/{{ $release }};
composer install --prefer-dist;
@endtask
@task('update_permissions')
cd {{ $release_dir }};
chgrp -R www-data {{ $release }};
chmod -R ug+rwx {{ $release }};
@endtask
@task('update_symlinks')
ln -nfs {{ $release_dir }}/{{ $release }} {{ $app_dir }};
chgrp -h www-data {{ $app_dir }};
@endtask
Run that and test it via:
envoy run deploy
A notable command is the creation of the symlink to make the new code files live. This is the ln
command with the -nfs
flags used:
-s
- Create a symbolic link.-f
- Force the creation of the symlink even if a file, directory, or symlink already exists at that location (it will unlink, aka delete, the target directory!).-n
- If the target directory or file is a symlink, don't follow it. Often used with the-f
flag.
After testing that, we add new route to app/Http/routes.php
, thus changing the code to simulate a real development-deployment cycle.
Route::get('/test', function()
{
Return Request::header();
});
Update the remote GitHub repository:
git add --all
git commit -m "added new test route"
git push origin master
Then we can re-run the deployment script and see if it worked:
envoy run deploy
Notes and Resources
Here is the Envoy Repository and Documentation
Create the Laravel application within Vagrant:
cd /vagrant
composer create-project laravel/laravel app
Nginx Config on Vagrant and production server (the only difference between dev and production is only the server_name
directive value).
server {
listen 80;
root /vagrant/app/public;
index index.html index.htm index.php app.php app_dev.php;
# Make site accessible from ...
server_name 192.168.22.45.xip.io vaprobash.dev;
access_log /var/log/nginx/vagrant.com-access.log;
error_log /var/log/nginx/vagrant.com-error.log error;
charset utf-8;
location / {
try_files $uri $uri/ /app.php?$query_string /index.php?$query_string;
}
location = /favicon.ico { log_not_found off; access_log off; }
location = /robots.txt { access_log off; log_not_found off; }
error_page 404 /index.php;
# pass the PHP scripts to php5-fpm
# Note: .php$ is susceptible to file upload attacks
# Consider using: "location ~ ^/(index|app|app_dev|config).php(/|$) {"
location ~ .php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+.php)(/.+)$;
# With php5-fpm:
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param LARA_ENV local; # Environment variable for Laravel
fastcgi_param HTTPS off;
}
# Deny .htaccess file access
location ~ /\.ht {
deny all;
}
}