DEV Community

LaTerral Williams
LaTerral Williams

Posted on

πŸš€ My First Secure CI/CD Pipeline on GitLab: A Beginner's Walkthrough

When I first opened GitLab and saw the Projects section, I'll admit, it was exciting to explore. I am familiar with GitHub but not GitLab.

I've explored pipelines, Docker, and DevSecOps, mostly in theory but I'd never built a pipeline myself.

So, I decided to find a simple project and learn how GitLab pipelines actually work.

This article is that journey... a step-by-step guide for beginners who want to see security, automation, and code come together in one workflow.


🧠 What You'll Learn

By following along, you'll learn how to:

  • Set up a GitLab project from scratch
  • Create a Python app
  • Build a Docker image
  • Configure a GitLab CI/CD pipeline
  • Add a security scan using Bandit
  • Understand what happens in each stage of the pipeline

No home lab, AWS account, or complex setup is required.. you can do everything inside GitLab.


🏁 Step 1: Create Your GitLab Project

  1. Go to GitLab.com and sign up.
  2. On your dashboard, click New Project β†’ Create Blank Project.
  3. Enter:
    • Project Name: secure-pipeline-demo
    • Visibility: Public (optional but good for portfolios)
    • βœ… Check Initialize repository with README

Click Create Project, and you'll have an empty repo ready to go.

Note: You may have to verify your account before being able to complete.


🧱 Step 2: Add Your First Python File

From your new project page, go to Repository β†’ Web IDE and create a
new file called main.py.

If you have trouble locating Repository, look for the code button, you'll see open with β†’ Web IDE.

def greet():
    print("Hello from the Secure CI/CD Pipeline Demo!")

if __name__ == "__main__":
    greet()
Enter fullscreen mode Exit fullscreen mode

Commit the change to the main branch.

This tiny file will be our "application"... simple, but perfect for testing automation.


βš™οΈ Step 3: Create the .gitlab-ci.yml Pipeline File

In the root of your project, create a new file named .gitlab-ci.yml.

This file tells GitLab exactly how to build, test, and scan your project.

Here's the full version you'll use:

# ======================================
# Secure CI/CD Pipeline Demo
# ======================================

# Include GitLab's built-in security scans
include:
  - template: Security/SAST.gitlab-ci.yml

stages:
  - build
  - docker_build
  - test
  - security_scan

# -----------------------
# 1. Build Stage
# -----------------------
build:
  stage: build
  script:
    - echo "Building project..."
    - mkdir dist
    - cp main.py dist/
  artifacts:
    paths:
      - dist/
    expire_in: 1 week

# -----------------------
# 2. Docker Build Stage
# -----------------------
docker_build:
  stage: docker_build
  image: docker:24
  services:
    - docker:dind
  variables:
    DOCKER_HOST: tcp://docker:2375/
    DOCKER_DRIVER: overlay2
  script:
    - echo "Building Docker image from Dockerfile..."
    - docker build -t secure-demo:latest -f Dockerfile .
    - echo "βœ… Docker image built successfully."
  artifacts:
    paths:
      - Dockerfile
    expire_in: 1 week

# -----------------------
# 3. Test Stage
# -----------------------
test:
  stage: test
  image: python:3.9
  script:
    - echo "Running syntax test..."
    - python3 -m py_compile main.py

# -----------------------
# 4. Security Scan Stage
# -----------------------
security_scan:
  stage: security_scan
  image: python:3.9
  script:
    - echo "Installing Bandit..."
    - pip install bandit
    - echo "Running security scan..."
    - bandit -r . -f txt -o bandit-report.txt
    - echo "βœ… Security scan complete. Report generated at bandit-report.txt"
  artifacts:
    paths:
      - bandit-report.txt
    when: always
Enter fullscreen mode Exit fullscreen mode

Commit this file.

πŸ’‘ What it does:

  • Each section defines a stage of your pipeline
  • GitLab runs them in order: build β†’ docker_build β†’ test β†’ security_scan
  • The include: line adds GitLab's own built-in SAST template for extra security checks

πŸ‹ Step 4: Add a Dockerfile

Pipelines that build Docker images need a Dockerfile.

Create one in the root of your project (same level as .gitlab-ci.yml).

Make sure it's named exactly Dockerfile (case sensitive!).

# =======================================
# Dockerfile for Secure CI/CD Pipeline Demo
# =======================================

FROM python:3.9-slim

WORKDIR /app
COPY main.py /app/
CMD ["python", "main.py"]
Enter fullscreen mode Exit fullscreen mode

Commit it and return to your project's main page.


πŸš€ Step 5: Watch Your Pipeline Run

Now go to CI/CD β†’ Pipelines.

You'll see a new pipeline triggered automatically.

It should look something like this:

build β†’ docker_build β†’ test β†’ security_scan
Enter fullscreen mode Exit fullscreen mode

Click each stage to view the job logs.

You'll see output such as:

Building Docker image from Dockerfile...
Successfully built abc123
βœ… Docker image built successfully.
Running security scan...
No issues identified.
βœ… Security scan complete.
Enter fullscreen mode Exit fullscreen mode

If all stages turn green, congratulations... you just ran your first full
GitLab CI/CD pipeline! πŸŽ‰


πŸ” Step 6: Analyze Security with Bandit

The security_scan stage uses Bandit, a Python static analysis tool
that checks for vulnerabilities.

To see it in action, edit main.py and add a small (intentional) issue:

import os

def greet():
    password = "supersecret"  # hardcoded password
    os.system("ls")           # command injection risk
    print("Hello from the Secure CI/CD Pipeline Demo!")

if __name__ == "__main__":
    greet()
Enter fullscreen mode Exit fullscreen mode

Commit and rerun the pipeline.

Open security_scan β†’ Job Log to view Bandit's report:

>> Issue: [B105:hardcoded_password_string] Possible hardcoded password
   Severity: MEDIUM   Confidence: HIGH
   Location: main.py:4

>> Issue: [B605:start_process_with_a_shell] Possible shell injection
   Severity: HIGH   Confidence: HIGH
   Location: main.py:5
Enter fullscreen mode Exit fullscreen mode

Now remove those risky lines, commit again, and rerun the pipeline. Bandit should report:

No issues identified.
Enter fullscreen mode Exit fullscreen mode

This is our intro to the foundation of DevSecOps: detect β†’ fix β†’ verify β†’ repeat.


πŸ“Š Step 7: View and Download the Bandit Report

Each time the pipeline runs, it generates a bandit-report.txt file as
an artifact.

You can download it by:

  • 1. Opening the security_scan job
  • 2. Expanding the Job artifacts section
  • 3. Clicking bandit-report.txt

That's your audit trail... a record of the scan results at that point in
time.


🧭 Step 8: Understanding the Big Picture

Here's what we've built:

Stage Purpose
build Prepares the app for packaging
docker_build Builds a Docker image from the app
test Runs a basic syntax check
security_scan Runs automated vulnerability analysis with Bandit

This may be a simplified example, but it closely mirrors the workflows DevOps and DevSecOps professionals build and review every day... only you’ve created yours entirely on GitLab, with zero infrastructure cost.


🧰 Step 9: Common Beginner Pitfalls

  • Failed to read dockerfile

    • Cause: File was named DockerFile or not in repo root
    • Fix: Rename to Dockerfile and place in root directory
  • Cannot connect to the Docker daemon

    • Cause: Missing Docker service configuration
    • Fix: Add services: - docker:dind and DOCKER_HOST variables
  • Bandit not found

    • Cause: pip install bandit missing
    • Fix: Ensure it’s installed in the job before running

πŸ”’ Step 10: Why This Project Matters

By completing this single project, you've practiced:

  • Continuous Integration (CI): automatically building and testing your code
  • Continuous Delivery (CD): preparing artifacts for deployment
  • DevSecOps mindset: integrating security early in the workflow
  • Version control hygiene: every change triggers a traceable build

These are the foundations used by DevSecOps professionals.


🧠 Key Takeaways

  • GitLab CI/CD pipelines are just scripts that run automatically after each change.
  • Docker + Python: lightweight, reproducible builds.
  • Bandit makes security visible and fixable early on.
  • Automation doesn't have to be about complexity, it's about consistency.

🎯 Next Steps to Explore

If you want to keep going:

  • Add a stage to push your image to GitLab's Container Registry.
  • Include other scanning tools like Trivy.
  • Replace the Python file with a small Flask or Node.js app.
  • Explore variables, rules, and environments in GitLab CI.

Each of these will teach you a deeper layer of automation.


πŸ† Final Thoughts

Building your first CI/CD pipeline can feel intimidating, until you realize it's just a sequence of automated steps you already do manually: build, test, check, repeat.

This small project is your first step of exploring security and automation working together.

Whether you're studying for certifications, switching careers, or just
exploring, you now have a living example to reference, modify, and showcase.

πŸ’¬ If you're reading this and you're on the fence about trying GitLab
pipelines, start small. The moment you see that first green check-mark,
you'll realize you've just automated your first deployment.


πŸ–‡οΈ Resources


🀝 Connect

If you enjoyed this walkthrough or you’re also learning DevOps, Linux, or Cloud automation, I’d love to connect, share ideas, and learn.

πŸ’¬ Feel free to reach out or follow my journey on πŸ‘‰ LinkedIn

Top comments (0)