Deploying a Vapor app to a remote server using Capistrano

timominous
8 min readApr 19, 2017

--

Great! You have a Vapor app running locally (or even on a remote machine). There’s just one problem. Before you can serve the latest changes from your remote machine, you’d have to go through a lot of steps which will most likely consist of the following:

  1. Pushing the changes to a remote repository
  2. Logging in to your remote machine
  3. Pulling the latest code from the remote repository
  4. Fetching the dependencies of your app
  5. Building the app using the latest code
  6. Running the built app from the background
  7. Logging out of your remote machine

The list could grow as you add more configurations into the mix and doing all of these manually would be tedious and time-consuming.

What you need to do is to streamline the deployment process so that you don’t have to go through all those steps every time you want to reflect the changes you’ve made to your app. There are a lot of tools that offer a set of functionalities that make deploying to your remote server a more enjoyable task but for now, we are going to discuss Capistrano.

Important notes

This guide assumes that you already have a working Vapor app and a remote server that can be accessed through SSH from the local machine. A working SSH configuration is essential to this guide as the scripting tasks would be tunneled through it. You can read this guide on how to get started with SSH.

A working Git repository setup is also essential for this guide and process as the latest code will always be fetched from the remote Git repository. You can read up on how to get started with Git here.

This guide will be using the default Vapor app. The machines used were a local Macbook Pro with macOS 10.12.3 and a remote Ubuntu Server 16.04.2. Both machines have Swift 3.1. The latest version of the Vapor toolbox is being used on the remote machine. You can find guides on how to install the latest toolbox to your remote machine here.

Throughout this guide, take notice of values enclosed in angle brackets <> as you will have to replace them with the appropriate values from your setup. For example:

$ ssh <server_user>@<remote_server_address>:<port>// Will become
$ ssh ubuntu@127.0.0.1:22

What is Capistrano?

Capistrano is a remote server automation tool that allows scripting and execution of arbitrary tasks and includes a set of built-in deployment workflows. It boasts its availability for use by projects of different languages while being written in Ruby. If you want to read more about what it is and how it works, you can click here.

The set of features Capistrano offers is exactly what we need to streamline our process. But first we have to install the Capistrano gem to our local machine by running this command:

$ gem intstall capistrano -v 3.5.0

If you don’t know what RubyGems are, you can find guides on how to work with them here.

Getting to work with Capistrano

Now that we have Capistrano installed on our local machine, the next step is to set up the needed configuration files for Capistrano to work.

In order for Capistrano to generate the needed files we need to run the following commands:

$ cd <project_directory>
$ cap install

This will add all the files and directories needed to run Capistrano. Specifically, it will add the following:

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

For this guide, we will be working with the basic provided configuration files only found under the config directory. We can safely ignore the Capfile and lib directory for now. Global configuration variables can be found and set in config/deploy.rb while target specific configuration variables can be found and set in config/deploy/<target>.rb.

Deployment Targets

In order for Capistrano to know where to deploy the project, we are going to modify target files. Inside the config/deploy directory, you will find the two generated target files: production and staging.rb. Open up production.rb and you will see something similar to this:

We won’t have to modify most of these variables for now. What we will do is add a server-based configuration to specify our remote machine. You can add a new line or uncomment and modify the existing ones.

You need to fill in the variables above with the appropriate values so that Capistrano will know where to deploy the project. You can also to the same to staging.rb if you have a staging environment.

Deploy Script

Now that Capistrano knows where to deploy our project, the next thing we need to set up is how to deploy our project. To do so, we are going to modify config/deploy.rb.

For this guide, we can safely remove lines 40–47 from the :deploy namespace of deploy.rb since we are not going to use the method. From the code above, you can see the :application and :repo_url configuration variables. You should modify them to match the values from your setup.

The :application variable will be the name of your application. The source code of the project will be deployed (by default) to /var/www/<my_app_name>. You can change the deploy directory by setting the :deploy_to variable but for this guide, we will be using the default.

The :repo_url variable will the Git URL of your remote repository. If you looked at the other variables in the configuration file, you can see that both the source control management system :scm and the :branch are configurable but, again, for this guide, we will be using the default values. The latest source code from :repo_url will be the one used by Capistrano for publishing.

If you’re done modifying :application and :repo_url, we could test out our deploy script by running this command:

$ cap production deploy

For now, all it does is it publishes the latest source code to the /var/www/<my_app_name>/current directory of your machine. If you take note of the console logs produced by the command, you can see that it’s actually executing a lot of steps. For simplicity, the following steps are equivalent to what it does:

  1. Logs in through SSH to your remote machine
  2. Creates a new release directory in /var/www/<my_app_name>/releases/
  3. Clones the latest source code in the newly created release directory
  4. Creates a symlink from the release directory to /var/www/<my_app_name>/current
  5. Logs the revision to /var/www/<my_app_name>/revisions.log
  6. Logs out of the SSH session

It may look like a lot of steps but all of these are automated and it’s a great start since it already eliminates steps 2, 3, and 7 from the list earlier. What we’re going to do next is to eliminate the remaining steps in between.

Automated building

Now that we have our deploy script pushing our code to the remote server, the next logical step is to automate the building process. Adding the following methods to the :deploy namespace of our deploy.rb would automate the fetching of dependencies and building of our app.

What these set of commands does is automate the fetching and building with the deploy process. The :deploy namespace should the be something similar to this:

Now if you try running the deploy command again, you will be able to see in the logs that, in addition to publishing our code, the script now fetches the dependencies and builds the app in release configuration.

Application service

Since we are already able to push our code to the remote machine and build it, we ought to find a way to start the application and get it running in the background. Since we are running Ubuntu 16.04.2 on our remote machine, we will be using systemd to accomplish that.

What we need to do is create a systemd service that runs our Vapor app in the background. First, you need to create the service file:

$ cd /lib/systemd/system
$ [sudo] touch vaporapp.service

Open up vaporapp.service in your favorite terminal editor and modify its contents.

Now we have what we need to start our Vapor app as a service, we can run this command to start it up:

$ [sudo] systemctl start vaporapp.service

You can now access your app at <remote_server_address>:<port>. What we need to do next is to add this to our deploy script so that our app is automatically served using the latest build everytime we deploy changes. We can also run

$ [sudo] systemctl enable vaporapp.service

to start up the service every time the remote machine reboots.

If you want to learn more about systemd, DigitalOcean has a great guide that can be found here. If you are running a version of Ubuntu lower that 15.04, you can achieve the same functionality by using Upstart.

Automated serving

Just like what we did when we automated the building process, we are going to add a couple methods to the :deploy namespace of config/deploy.rb so that serving the app will also be automated. You can add the following methods right below the :build method.

Notice that we added 3 methods to our build script: start, stop, and restart. We will be using the restart method but the other 2 are added for good measure and can be useful for future endeavors. Don’t forget to add the hook to call the restart method after the build hook.

You could try running the deploy command again and see that after publishing, the deploy script builds and serves your app. At this point, we already have most of what we need to streamline our deployment process. There’s just one more thing we need…

Persisting secrets

Every time we deploy our app, Capistrano replaces the /var/www/<my_app_name>/current directory with a symlink to a directory which contains our latest code. And since it’s very likely that we do not check-in (which we shouldn’t) our secrets configuration files to the remote repository, we should find another way to persist these secrets so that our app will be able to use them.

To achieve this, we have to manually upload our secrets directory to our remote machine initially and every time we make changes to the files within it. The easiest way to do this is through scp:

$ scp -r <project_directory>/Config/secrets server_user@remote_server_address:/var/www/<my_app_name>/shared/secrets

Our secrets folder should now be uploaded to /var/www/<my_app_name>/shared/secrets. Changes to this directory should be handled manually and not included in the deployment process. What we can do from our deploy script is to add a symlink to this directory from within our project directory so that our app can use it. You can add the method right below the :restart method in our :deploy namespace.

The method we added is named secrets and it handles the linking of our secrets folder. We also added a deploy hook that calls this method right after our source code is published and before the application starts fetching dependencies.

Finished!

We have already completed our Capistrano deploy script deploy.rb and it should look similar to this:

While it may have taken a little while to setup, it pays off in the long run by reducing our deployment process to two steps!

  1. Pushing the latest source code to the remote repository; then
  2. Running our deploy script
$ cap production deploy

This is a great improvement over the 7-step process we described earlier. You can now reallocate your time to building world-changing important features for your app.

Links

--

--