Advertisement

Deploying a Laravel Application Using Capistrano

by
Student iconAre you a student? Get a yearly Tuts+ subscription for $45 →

So you've just built a fancy web application and you're planning to put it online. This can be done in many ways. In this article I'd like to cover one approach to deploy your backend system to your production server. We'll go through the following steps by example of a Laravel application but this can be applied to any other language or technology.

Update

This article was updated to Capistrano 3. More about the new version can be found on capistrano's website.

The Past

Perhaps you have already put some websites online in the past. Probably you've used a FTP client and uploaded the bits and bytes by hand. Or perhaps you always logged into your server via ssh and pulled the changes manually.

The Idea

Our goal is to simplify this process as much as possible. The idea is to use your code repository as a source for every deploy. The deployment tool, in our case capistrano, will automatically log into your server and build your system right out of your repository.

Software deployment is all of the activities that make a software system available for use. - Wikipedia

What You'll Need...

...on Your Remote Server

Your remote server needs to provide ssh access. It also should have installed all necessary dependencies for your project such as GIT, PHP, MySQL, Composer, ... Besides that, you don't need any extra software on your production server.

...on Your Local Machine

In order to install and use capistrano, you need at least Ruby 1.9 (if you don't have Ruby installed, I recommend installing it using rbenv). To install capistrano, you simply have to run:

$ gem install capistrano

So why capistrano, you may ask. As always, there are many ways to accomplish a task but in my case, capistrano always seemed to be the easiest and most flexible approach. You can configure it to all your needs and there are a lot of plugins out there which simplify your work again.

Capistrano is a utility and framework for executing commands in parallel on multiple remote machines, via SSH. It uses a simple DSL (borrowed in part from Rake) that allows you to define tasks, which may be applied to machines in certain roles. It also supports tunneling connections via some gateway machine to allow operations to be performed behind VPN's and firewalls.

Prepare

Now we have everything we need, so let's setup our deployment settings. But first we have to create a folder on the remote server where all the files should be deployed to. Log into your server with SSH and create a folder. A common place is /var/www/. So let's do this:

$ sudo mkdir /var/www/my-app
$ sudo chown -R username:group /var/www/my-app

That's it. There is nothing more to do on the remote server, so you can close the ssh connection and move on. Go into your project (or any other folder, that doesn't matter right now) and run:

$ cd my-project
$ cap install

This command will create the basic files we need. After that your folder should look like this.

.
├── Capfile
├── config
│   ├── deploy
│   │   ├── production.rb
│   │   └── staging.rb
│   └── deploy.rb
└── lib
    └── capistrano
        └── tasks

The Capfile is like the mount point for capistrano but for now we'll just need to edit config/deploy.rb and config/deploy/production.rb. The first file is responsible for all the building steps, the second file represents a "stage". You can have several stages like production, staging, testing... In each stage-configuration file you can specify your server(s). Let's open those two files in your favourite text editor and replace the content with the following snippets. We'll go through the code afterwards.

We'll start with config/deploy/production.rb:

role :app, %w{username@123.456.789.123} # EDIT your ssh username and server ip address 

set :ssh_options, {
    auth_methods: %w(password),
    password: "" # EDIT your ssh password
}

Next we'll modify config/deploy.rb:

set :application, "Your app name"  # EDIT your app name
set :repo_url,  "https://github.com/laravel/laravel.git" # EDIT your git repository
set :deploy_to, "/var/www/my-app" # EDIT folder where files should be deployed to

namespace :deploy do
    
    desc "Build"
    after :updated, :build do
        on roles(:app) do
            within release_path  do
                execute :composer, "install --no-dev --quiet" # install dependencies
                execute :chmod, "u+x artisan" # make artisan executable
            end
        end
    end

    desc "Restart"
    task :restart do
        on roles(:app) do
            within release_path  do
                execute :chmod, "-R 777 app/storage/cache"
                execute :chmod, "-R 777 app/storage/logs"
                execute :chmod, "-R 777 app/storage/meta"
                execute :chmod, "-R 777 app/storage/sessions"
                execute :chmod, "-R 777 app/storage/views"
            end
        end
    end

end

You now have to put your data in every line with an #EDIT comment (ip address, git repo, ssh user, password, etc). The :deploy_to variable should be the folder we just created. Your webserver (Apache, Nginx, ...) should point to /var/www/my-app/current/public.

In the namespace :deploy block of the deploy.rb file you specify what actually should happen for each deploy. So there are two tasks. In the build task we install all your PHP dependencies, just like you're used to it during development. After that we make the artisan file executable in order to use it for migrations. In the restart task we fix the permissions for the storage folders.

All those tasks are invoked in the following order. You can hook into each task if you need to but for now we stick with our simple configuration.

deploy:starting    - start a deployment, make sure everything is ready
deploy:started     - started hook (for custom tasks)
deploy:updating    - update server(s) with a new release
deploy:updated     - updated hook
deploy:publishing  - publish the new release
deploy:published   - published hook
deploy:finishing   - finish the deployment, clean up everything
deploy:finished    - finished hook

Every deploy is stored in /var/www/my-app/releases/. The built-in task deploy:publishing creates a symbolic link of the recent deploy to the current folder. This way you can keep older releases and switch versions without going offline for a second. When this task ran, your newest version is online.

You can easily add your own tasks if your build process requires some extra steps. For more detailed information, I recommend you reading the official documentation.

After these basic configurations steps we are prepared for our first deploy.

Fire!

So that's the moment you were waiting for. The hardest part is done. For now on every time you want to deliver your application updates, you just have to run the following magical command. Capistrano will read your config/deploy.rb config/deploy/production.rb files and run each task. If a task fails, the deploy will stop and the old version is still online.

$ cap production deploy

You will see a bunch of text output and after little time (depending on your server) everything should be complete. That was easy, wasn't it?

Note: For now we just setup our production stage, but you could replace production with another stage, for example your testing server and run $ cap staging deploy.

Further Thoughts

Security

Perhaps you might be a little worried if you have to put your plaintext password in the configuration file. I just choosed that way to make the demonstration as straight forward as possible, but in the real world you might want to use a SSH key. You can import one like this:

set :ssh_options, {
    keys: %w("/path/to/your/key.pem"), # EDIT your ssh key
    auth_methods: %w(publickey)
}

Database

For now we have just focused on deploying the actual files to their new home but in many scenarios you might also do something with your database. Laravel has a perfect tool for that: migrations. You could just add a new step where you run these migrations. After that our build task might look like this:

desc "Build"
after :updated, :build do
    on roles(:app) do
        within release_path  do
            execute :composer, "install --no-dev --quiet" # install dependencies
            execute :chmod, "u+x artisan" # make artisan executable
            execute :php, "artisan migrate" # run migrations
        end
    end
end

You also have to add this task in the transaction block of the update task. Now everytime you deploy, the database will be updated to your latest migrations.

Rollback

Sometimes you deploy a non-working version of your application and you need to undo these changes. Capistrano has a built-in feature for that called "rollback". Just run:

$ cap production deploy:rollback

Conclusion

You've just learned a very simple way of deploying your application to your production server(s) with Capistrano. Once the configuration work is done, it just takes one command to deploy your latest version in seconds. But as mentioned earlier, this is not the only way to do this.

You should also check out the task runner grunt which suits perfectly for building and deploying JavaScript applications. A complete different approach takes docker which acts like a lightweight VM. The idea here is to deploy your whole environment as a virtual machine. Check them out!

Advertisement