GitLab CI is a fully integrated CI/CD solution baked directly into GitLab, while Jenkins is a standalone, open-source automation server that requires significant setup and maintenance.

Let’s see GitLab CI in action. Imagine you have a simple Python application. Your .gitlab-ci.yml file, located at the root of your repository, might look like this:

stages:
  - build
  - test
  - deploy

build_app:
  stage: build
  script:
    - echo "Building the application..."
    - pip install -r requirements.txt
    - echo "Build complete."
  tags:
    - docker

run_tests:
  stage: test
  script:
    - echo "Running tests..."
    - pytest
    - echo "Tests passed."
  tags:
    - docker
  needs:
    - build_app

deploy_staging:
  stage: deploy
  script:
    - echo "Deploying to staging..."
    - ./deploy_script.sh staging
    - echo "Deployment to staging complete."
  environment:
    name: staging
    url: https://staging.example.com
  only:
    - main
  tags:
    - shell
  needs:
    - run_tests

When you push a change to your main branch, GitLab automatically picks up this .gitlab-ci.yml file. It spins up a runner (in this case, a Docker container tagged docker for the build/test stages and a shell runner for deployment). The build_app job installs dependencies. Then, run_tests executes your Python tests. Finally, deploy_staging runs a deployment script to push your application to a staging environment. The environment block tells GitLab where your app is running, and needs ensures jobs run in the correct order.

This integrated approach means you don’t need separate servers for your Git repository and your CI/CD pipeline. Everything lives within GitLab, simplifying management and reducing the overhead of keeping multiple systems in sync. GitLab CI leverages Docker for containerized builds, ensuring consistent environments across different machines and stages. It also supports various deployment strategies, from simple scripts to complex Kubernetes rollouts, all configured within the .gitlab-ci.yml file. You can define specific tags to route jobs to runners with particular capabilities (e.g., Docker support or specific hardware). The stages define the order of execution, and needs allows for more flexible DAG (Directed Acyclic Graph) pipelines than traditional linear stages.

The most surprising thing about GitLab CI’s pipeline execution is that jobs within the same stage, if they don’t have explicit needs dependencies on each other, can run in parallel on any available runner. This means your build_app and lint_code jobs (if they were in the same build stage and had no dependencies) could execute concurrently, significantly speeding up your pipeline if you have multiple runners.

The next concept you’ll likely explore is GitLab’s integrated security scanning features, which can be seamlessly added as jobs within your .gitlab-ci.yml.

Want structured learning?

Take the full Gitlab course →