We're live-coding on Twitch! Join us!
Continuous Integration - Debugging Live CircleCI Builds with SSH Access

Continuous Integration - Debugging Live CircleCI Builds with SSH Access


Why Bother About Continuous Integration

Continuous Integration (CI) is a development practice that enables teams to rapidly build, test and deploy while at the same time detecting problems as early as possible.

Some of the most popular CI tools are

  1. CircleCI
  2. Travis CI
  3. Jenkins
  4. Semaphore

The final decision on what CI tool to use entirely depends on your team's requirements. Most of these tools provide support to:

  • Quickly build code from Github using hooks.
  • Create an isolated environment, install all the dependencies and trigger other services such as tests through a circle.yml file.
  • Run your tests.
  • Secure remote access to you application to quickly debug without making unnecessary commits to your application repository.
  • Quickly deploy to hosting services such as Heroku, Amazon AWS and Google Cloud Platform

Introduction To Debugging With SSH Access

More often than not, your code will fail, and it is bound to fail. With your CI tool of choice, you may quickly identify and fix such bugs before pushing your code into production.

But once it fails, how do you fix it?

Now there are a number of ways to go about fixing your errors depending on the bug's complexity and urgency. We will work under the assumption that your team lead just assigned the error to you as a Github issue.

Do you create 5 to 10 new commits, push the code again and pray that the issue has been fixed, Circle CI passes and everyone goes home happy? Or do you find the cause of the error and make one commit that is sure to pass?

In this tutorial, we will be going through how to debug remotely in a CircleCI container build with SSH.

Setting Ourselves Up For Failure

To understand how debugging via SSH works. We will have to cause havoc on a project. Not to worry, the project we will be breaking in this case will not crash any financial markets or cause an increase in the temperature of the earth's atmosphere.

The Project

We will be working with a simple node.js application that has one route that returns a 500 status code when a GET request is made to / . Let's call it project-abc.


'use strict';

var express = require('express'),
  app = express();

app.get('/', function (req, res) {
    .send('Success is going from failure to failure without losing enthusiasm.');

app.set('port', process.env.PORT || 8080);

app.listen(app.get('port'), function () {
  console.log('Magic happens on port', app.get('port'));

module.exports.app = app;

project-abc will also contain a single test that asserts that when a GET request is made to /, a 200 status code is returned.


'use strict';

var request = require('supertest'),
  app = require('../index').app;

describe('GET /', function () {
  it('Should return HTTP response with status code 200', function (done) {
    request(app).get('/').expect(200, done);

While this is meant to fail, we will inspect the issue via CircleCI's secure SSH access, identify the bug, fix it remotely and run tests to ensure that it passes.

This tutorial works under the assumption that you are comfortable intergrating a project on Github with CircleCI, running tests using mocha and testing HTTP requests using supertest. The test will still be easy to understand without prior knowledge of testing.

A simple circle.yml file is also included in the repository to trigger the tests on CircleCI.

    - mocha

Obtaining The CircleCI Build Container Information

If everything worked out correctly (and by correctly I mean everything failed) with the above set up, Our CircleCI build should have failed with the following error:

Failing CircleCI build

Perfect! Things are failing!

In the next step we will go straight ahead to find the bug directly on the Isolated container that Circle CI Spins up for our project. On your CircleCI dashboard, go to the Debug via SSH section and click on Retry this build with SSH enabled

Navigating to Debug via SSH section

The rebuild works in the same way that a normal build would run with the exception that no deployment will take place. This is essential for us because more often than not, the reason for us remotely accessing the build via SSH is because something went wrong, and we would not like to push our failing tests to Heroku or Amazon AWS.

The build VM will remain available for 30 minutes after the build finishes running and then automatically shut down. (Or you can cancel it.)

A succesfull rebuild will present you with a load of information that will enable us to remotely SSH into our Ubuntu container.

Host and Port info for our Ubuntu container

Accessing The CircleCI Build Container On The Terminal

The Container Information

With the Container Information details ready and good to go, you can simply copy and paste the container details on your terminal, In my case this would be ssh -p 64711 ubuntu@

This contains:

  • The ssh command.
  • The port to connect to.
  • The unique IP to the Ubuntu container.

Note that at this point, you need to have set up your SSH keys . You can confirm by running:

ssh git@github.com

Getting Inside The Container Environment

Once you have setup the permissions and run the ssh command from Github, you will be presented with remote access to your container.

This means that we can run commands such as listing directory details, changing directories and even opening files (with vim of course).

View of Container

As you can see, I am able to see the project-abc directory, move into it and see the file contents. How cool is that?

And it gets better.

As we had said earlier, the CI tools provide you with an isolated environment that runs your application. The reason CircleCI is able to run tests is because it installs all the node packages from package.json and runs the test command mocha from the circle.yml file.

This means that I have access to the npm command and can therefore install packages directly on the build. So let's go ahead and install mocha globally and run our test.

Install mocha and run a failing test

There goes our good old failing test. Now let's fix it.

Debugging The Error In the Container

Now that we can see the failing test, we can go and assess the situation on the necessary script and get to the root of this matter.

As it turns out, the test expects a 200 status code but received a 500 status code. This means that we may be returning the wrong status in our index.js. Let's go fix that. This video provides a quick run through what we just discussed earlier as well as fixing the error to correct the test.


As you can see. We managed to do all the following without making a single commit to our Github repository.

  1. Open the file using vim.
  2. Edit the status code from 500 to 200.
  3. Save the file and re-run the test

With this, It will now be easy for us to go directly to the issue in our project and make a foolproof bug fix and push code with an assured pass on all our tests.


While this was a small case study of how we can remotely debug and tinker with a build on CircleCI, this should set the pace of the amazing things you can do in your container.

I have provided the link to the Github repo here if you would like to play around with the sample project. I have also found the CircleCI documentation quite helpful in getting my way around this topic and many more.

Like this article? Follow @_kar_is on Twitter