Tweek is an open source feature management project that we’re developing here at Soluto. Or in other words – our baby. Like every open source project, Tweek also needs a reliable and fast CI process that can build and test it. Tweek has multiple services (.Net Core and NodeJs) and a website (NodeJs), and we chose Docker to deploy them. We need a CI solution to build and test each of the components separately, and then test all of them together to make sure everything interacts well with one another and that our baby is healthy.

The first CI iteration was building the services with Travis CI, and the website with CircleCI.


Travis was flexible. The Docker CLI worked as expected, even with features like volumes and Docker socket binding (for running Docker in Docker), but it was running an older version of Docker. It’s possible to update the Docker engine and docker-compose, but you have to write a script to do it. We also had to write a bunch of scripts and compose files for building our app, running tests, and deploying to DockerHub. In addition to all of that, there was no layer caching for Docker, which made the builds slower.
Our Travis config files

Take one

In CircleCi 1.0 we made caching work (but with hacks – see below), and deploying to DockerHub was easy, but it used a custom Docker fork which was old and not supported; because of that, it was unable to run newer versions of compose.
Our CircleCI 1.0 config file

The caching hack
if [[ -e ~/docker/tweek-images.tar ]]; then
  docker load -i ~/docker/tweek-images.tar
  echo "found cached images"
  echo "cached images were not found"
docker save soluto/tweek-api:latest soluto/tweek-management:latest backoffice_tweek-backoffice > ~/docker/tweek-images.tar
Take two

CircleCi 2.0 was an awesome improvement from 1.0, which we started with. They added caching for images (but since it was a premium feature we didn’t get to try it out), and they also added support for newer versions of compose files. The biggest problem was that we didn’t find a good way to share the build state between containers (volumes)*, and it was still in beta.
Our CircleCI 2.0 config file

* Some of the drawbacks of CircleCI could have been remedied if their new workflows and workspaces were available at the time

Time for testing

We had e2e tests in CircleCi, but they used the images of the services that were built in Travis. Because we were unable to control which build ran first, whenever we made a change in any of the services, it was only tested the next time the build ran.

A simple solution would have been to build all the parts together. On the one hand, CircleCi had a much better Docker integration than Travis, and on the other hand, Travis’ ability to share a state between build steps made it easier to build our .Net Core app. Because of that, it was very difficult to choose just one of them.

So, we searched for a CI tool that was able to build all of the parts together, and also test them and push the images to DockerHub.

…and then we found Codefresh.


In Codefresh, the entire CI process is Docker-focused.

They have support for multi-stage Dockerfiles ― which was a Docker Edge feature when we started ― that made building our images simpler, especially the .Net image.

The caching works well – unless you’re using a multi-stage Dockerfile.

There are dedicated build steps for testing an image with an entire environment composition. This means that we get a testing environment that’s almost the same as the production environment.

The volume is shared between steps, and each step can run a different container. This provides a whole lot of flexibility, like running one step in a node image and another in a microsoft/aspnetcore-build image.

Codefresh also has a repository with all the images that were built, with a simple publish to DockerHub feature and tagging capabilities. Each image can be automatically tagged with metadata (branch, quality indicator, commit, etc.).

Another cool feature is Environments, that can be used instead of a staging environment. Here at Soluto, we use this a lot to share our latest features with colleagues.
Our Codefresh config file

Codefresh’s native Docker capabilities helped us create a simple and fast CI process. The deep integration with Docker Compose enabled us to create test suites that run against a full (and isolated) environment. We can create live environments with our latest features and show them off to our colleagues.

Now we can sleep easy knowing our baby is well-fed and taken care of.