Create your FREE Codefresh account and start making pipelines fast. Create Account

Codefresh vs. GitlabCI

13 min read

If you have selected Gitlab as your git provider, you may automatically think about selecting GitlabCI as your CI/CD solution as well. Using the same vendor for source control and CI/CD might seem natural, but is not always the best combination. In fact, choosing a “good-enough” option just because it is part of the same solution is unwise in the long run if it doesn’t cover your needs.

Gitlab is one of the supported GIT providers in Codefresh, so another valid combination is using Gitlab for source control and Codefresh for CI/CD. In this article, we will look at the advantages of Codefresh compared to the GitlabCI platform.

Update: See Gitlab’s response to this comparison here

Table of contents

  1. GitlabCI isn’t designed for micro-services since everything is tied to a single project
  2. Codefresh allows you to create multiple pipelines per repo, while GitlabCI is constrained to just one
  3. Codefresh has explicit support for monorepos
  4. GitlabCI does not support inline editing for its configuration
  5. GitlabCI needs special configuration to share information between pipeline phases
  6. The Docker registry in Codefresh is fully automated
  7. The Docker registry in Codefresh is much more detailed
  8. Codefresh has explicit support for Kubernetes deployments
  9. Codefresh has explicit support for Helm deployments

GitlabCI isn’t designed for micro-services since everything is tied to a single project

This is probably the biggest difference between Codefresh and GitlabCI. Codefresh is task-based, while GitlabCI is project based. Everything that you do in GitlabCI is connected to a single project so if you are working on multiple projects you cannot get an overview of what is happening.

GilabCI is centered around projects
GilabCI is centered around projects

All major functions in GitlabCI are for single projects only. These include:

  • Viewing your last builds
  • Browsing Docker images
  • Managing Kubernetes clusters

If you work on multiple projects at the same time, it is not possible to get an overall project summary. This UI approach may have made sense in the age of monolithic applications, but is truly inconvenient in the age of microservices. This is especially painful if you work in a company that has embraced microservices and you need to work on a large number of projects at the same time.

Codefresh, on the other hand, assumes by default that you will work on multiple repositories, so all related functions are grouped per task. You can view builds for all projects at the same time and browse your internal Docker registry without any restrictions.

Builds from all projects are visible
Builds from all projects are visible

Of course, if you really want to filter results in Codefresh to a specific project, you can still do so by using filters with multi-select options.

Codefresh build filter
Filtering builds

The same issue applies to the internal Docker Registry. In GitlabCI it is impossible to view all Docker images from all your projects at the same time. (More on Docker registries later.)

Even things like the Kubernetes integration are constrained within a single project. Attaching a Kubernetes cluster in GitlabCI is a process that must be repeated for every single project.

Kubernetes integration in GitlabCI
Kubernetes integration in GitlabCI

As an example, if you work at a company that has 20 projects that need Kubernetes, you need to add the same cluster to all of them individually.

Codefresh mitigates this issue by allowing you to define your Kubernetes cluster just once, in a central location tied to your account instead of your project.

Codefresh Integrations
Codefresh Integrations

From a security standpoint, Codefresh also allows you to create separate teams/accounts that do not share resources, so you get the best of both worlds.

If you are already working with GitlabCI and manage multiple projects at the same time, please let us know in the comments section how you work around the limitation of everything being tied to a single project.

Codefresh allows you to create multiple pipelines per repo, while GitlabCI is constrained to just one

This was another big revelation during my time with GitlabCI. In GitlabCI you can only define a single pipeline per project. That’s it! There is no way to add more pipelines. In the screenshot below, you can see there is really only one “Run Pipeline” button:

Only one pipeline
Only one pipeline

A nontrivial application will need more than one pipeline. A common set of pipelines would be:

  • A pipeline that runs on every commit that packages code and probably creates a Docker image.
  • A pipeline the deploys to an environment (e.g. prod or staging).
  • A pipeline that runs load testing against a specific environment.
  • A pipeline for pull requests that might run code quality or other security tools.
  • A weekly build for cleanups or maintenance actions.

GitlabCI offers the capability to have conditionals in a pipeline, so in theory, you could create a really huge pipeline that acts as a superset of all the things that you might ever need. But this is not ideal, and there is even an issue that has been open since 2016 for this particular problem:

Codefresh does not suffer from such limitations. Even though Codefresh also allows you to define pipeline conditionals, you are still free to define multiple pipelines for every workflow imaginable.

Multiple pipelines
Multiple pipelines

In a similar manner, the git trigger capabilities in Codefresh can cover all webhook events sent by the git provider resulting in pipelines that can run against specific git events. This can be done right from the GUI with no special configuration in the build yaml file.

Codefresh git triggers
Codefresh git triggers

This limitation of GitlabCI essentially forces you into having a huge gitab-ci.yml file with multiple conditional steps that select the “proper” workflow when an event is triggering the pipeline. For a really really complex workflow, this can quickly get out of hand.

Codefresh has explicit support for monorepos

One of the must useful features of a CI platform is to be able to start a pipeline only when a specific subset of files have actually changed. This requirement is very common with applications that gather all their services in a single git repository. Ideally a pipeline that builds a service should only execute when
one or more files are affected in the specified folder.

Another common scenario is when a single repository acts as a collection of various configurations. Maybe you have a repository that contains multiple Dockerfiles, or multiple ansible scripts or multiple Kubernetes manifests. A smart CI solution will examine the changed files from each commit and only trigger the pipeline that affects the respective folder/file.

Codefresh has explicit support for monorepos right within the GUI. The trigger window allows you to define glob expressions. The pipeline will only trigger if the changed files match the expression.

Glob expression for monorepo
Glob expression for monorepo

GitlabCI has no such capability. In fact there are two open issues:

Until any of these issues is implemented, GitlabCI is unfit for monorepos.

GitlabCI does not support inline editing for its configuration

Both Codefresh and GitlabCI allow you to build a project without specifying a yml file for the build configuration. However, in most cases, you need to define a yml file that fully defines your build workflow. Both platforms come with their own templates that can help as a starting point, but given a big enough project, it is unavoidable that you will spend time modifying the yml file and starting a build to see the results.

Unfortunately, the only way to change the GitlabCI file is by actually performing a commit. This means that your project will soon have a long list of trivial correction commits until you get the pipeline to the desired state.

GitlabCI trivial commits
GitlabCI trivial commits

On a related note, GitlabCI has an integrated lint function that you can run to verify your yml file, but of course, it can only check the syntax instead of the functionality. Yet for some strange reason, the lint checker starts empty and you are expected to paste the yml file yourself. This might not sound important if you have to do it once, but it quickly becomes annoying when you are creating the yml file for a completely new project.

GitlabCI empty linter
GitlabCI empty linter

Codefresh is much more flexible in this regard. It allows you to edit your yml file in an inline manner and only commit it when it is ready. Therefore, you can edit the file quickly and rebuild without actually committing anything.

Codefresh inline yaml
Codefresh inline yaml

If you make a mistake with the syntax you will get a warning in the editor in real time. There is no need to run a separate lint function. This makes
creating a new yml file from scratch in Codefresh a much more friendly experience.

Catching yaml errors
Catching yaml errors

GitlabCI needs special configuration to share information between pipeline phases

The pipeline phases in GitlabCI are called “jobs” (in Codefresh they are called “steps”). These run in a completely independent manner so everything that needs to be shared between pipeline phases must be defined explicitly.

To accomplish this, GitlabCI introduces a special syntax for two different concepts: artifacts and caching.

If you need to transfer something from one phase to another you need to explicitly define it as cache. For example, let’s say you have two GitlabCI jobs that both use node modules. Unless you define the folder as cache, both of them will re-download everything.

The correct syntax in GitlabCI would be:

If you wish to just move things from one job to another you are expected to use artifacts. For example, if you create a jar file in one job and expect to find it in the next one, you first need to add the following to your GitlabCI file:

The distinction between caching and artifacts is not always very clear, forcing even the official documentation to offer advice on best practices for what to consider cache and what to consider an artifact.

So how does Codefresh deal with this issue?

In Codefresh, in order to transfer information from one step to the other, you do (drumroll please)….absolutely nothing. In Codefresh all pipeline phases share a common volume (which includes the code of your project). So everything that is created in the project folder is automatically available to the next phase.

Shared Codefresh volume
Shared Codefresh volume

This means that you don’t need to do anything special to cache node modules. In Codefresh yml the same thing would be:

The same goes for “artifacts”. There is no need to define something explicitly for binaries, test reports, or anything else that you wish to be visible to all pipeline steps.

The end result is that Codefresh pipelines are much more compact and readable. You can find more information about Codefresh pipelines in the documentation.

The Docker registry in Codefresh is fully automated

Both Codefresh and GitlabCI offer a built-in Docker registry that can be used for storage of Docker images. The Docker registry offered by GitlabCI acts as any other Docker registry and in order to use it, you have to explicitly call Docker commands in your yml file.

For example, in order to push an image in GitlabCI you need to do the following:

This yml syntax essentially reflects what you would write in locally on your workstation. Codefresh offers much better support for its own internal registry.

In order to push your image to the Codefresh registry you do (drumroll please)….absolutely nothing. By default, all your Docker images that are produced by your builds are automatically pushed to the internal registry. You don’t add anything in your yml file to enable this functionality.

Codefresh Registry
Codefresh Registry

And before you ask about wasted space, know the Codefresh pricing is independent of the number of Docker images you have, so don’t worry about overusing the internal registry. As we will also see in the next section, it is very easy to locate images even when their number is very big.

If you want to use an external registry, Codefresh helps you even further by allowing you to declare all your registries at your account level (including authentication details).

Adding a Docker Registry
Adding a Docker Registry

Once you want to use an external registry you only need a push step:

Notice the complete lack of login instructions in Codefresh. You only define the registry name as it was defined in the GUI. With GitlabCI you would need to replicate the Docker login commands as before.

In summary, working with the integrated Docker registry is completely automatic with Codefresh, while GitlabCI needs the exact push instructions.

The Docker registry in Codefresh is much more detailed

We have seen in the previous section how easy it is to use the Codefresh internal registry and talked about the yml syntax for each platform. Once you have many images in your Docker registry you need a way to find them and catalog them.

The GUI view of the registry in GitlabCI is very sparse:

GitlabCI registry
GitlabCI registry

You only see the tag, the size, and the date of the image. There is no other option available (which might make sense as we have already seen that the registry in GitlabCI is tied to a single project).

Codefresh, on the other hand, gives you several more options. As a starting point, you can narrow down your view using multiple filters

Image filtering
Image filtering

You can also do an analysis of the layers of each image to gain insights of the size:

Docker layers
Docker layers

And most importantly of all, you can annotate your Docker images with your own metadata. For example, you can mark images that have passed security scans, or code quality gates, or load testing approval. The metadata can be added either directly from the GUI or via the pipeline syntax.

Docker annotations
Docker annotations

This makes the Codefresh Registry very powerful, making it a central source of truth for developers, QA, operations and any other stakeholder of a project.

In summary, the integrated GitlabCI registry lacks a lot of features offered by the Codefresh version.

Codefresh has explicit support for Kubernetes deployments

Easy Kubernetes integration is one of the major features behind the development of Codefresh. Kubernetes support is one of the highlights of Codefresh functionality.

When you define a Kubernetes cluster in Codefresh, you get access to two major features: First, you have a built-in Kubernetes dashboard that shows you namespaces/pods and deployments:

Kubernetes Dashboard
Kubernetes Dashboard

Second, Codefresh allows you to deploy a Kubernetes service with just the GUI, which is a great way to quickly deploy demo apps.

Deploy on Kubernetes
Deploy on Kubernetes

In vanilla GitlabCI there is no such thing. Apart from the deploy boards, there is no other GUI that can help you with your first steps in Kubernetes management.

Codefresh has explicit support for Helm deployments

Helm is a package manager for Kubernetes that allows you to group multiple application into charts.

GitlabCI has some basic support for Helm. It allows you to easily install Helm on your Kubernetes cluster (i.e. the server component called Tiller).

Install Tiller
Install Tiller

Once Tiller has installed it is also very easy to use Helm to install packages.

Codefresh, on the other hand, takes Helm integration to the next level by providing:

  • an integrated Helm repository for all accounts,
  • a graphical app browser that can access both the internal and external helm repos,
  • and a Helm dashboard for monitoring deployments and even rolling them back.

None of these is offered by GitlabCI and having an internal Helm repository is really crucial for using Helm in the first place (as the built-in Docker registry is useful for Docker images).

Codefresh gives you a built-in Helm repository that you can use for your own charts. You can also connect other chart repositories and create an application catalog.

Helm Repository
Helm Repository

Once you install a Helm chart in your Kubernetes cluster you have full visibility on version history, active services, chart values, etc.

Helm Releases
Helm Releases

However, the most impressive capability is that you can rollback a version right from the GUI. And in this case, rollback means the Helm rollback (i.e. going to a previous version of the chart).

Helm Rollback
Helm Rollback

In summary, Helm support in Codefresh is much more extensive than GitlabCI and the presence of a built-in Helm repo and dashboard are very important factors for using Helm as part of the CI/CD process.


In this article we have seen some advantages of Codefresh compared to GitlabCI. These are

GitlabCI centers all its functionalities on the project level. You can only define
one pipeline per project and editing the build file is a time consuming process that requires a commit any time you change anything.
There is no support for monorepos resulting in excessive builds even when a project hasn’t actually changed.

Codefresh has automatic support for both caching and artifacts via the shared volume for all build jobs/steps. No explicit
instructions are needed for either caches or artifacts.

The Codefresh internal Docker registry is fully automated. All Docker images produced by builds are automatically pushed there.

Finally, Codefresh has explicit support for Helm, offering a dashboard and an integrated repository for all accounts.

GitlabCI is not the only solution for Gitlab projects. Codefresh has native support for Gitlab repositories and you can use each platform
on what it does best (Gitlab for version control and Codefresh for CI/CD)

Update: See Gitlab’s response to this comparison here

New to Codefresh? Create Your Free Account Today!

Kostis Kapelonis

About Kostis Kapelonis

Kostis is a software engineer/technical-writer dual class character. He lives and breathes automation, good testing practices and stress-free deployments.

Leave a Reply

* All fields are required. Your email address will not be published.