← All TILs · gitlab-ci

A simple GitLab CI pipeline for ansible-lint on a custom Python image

gitlab-ci - 2026-09-05

Installing ansible-lint with pip inside every single pipeline run works, but it's slow and re-downloads the same packages on every commit. Building a small custom image once — based on the official python image, nothing fancier — and reusing it is barely more setup and considerably faster.

The image

# Dockerfile
FROM python:3.13-slim
RUN pip install --no-cache-dir ansible-core ansible-lint

That's the whole image: the official Python base plus the two packages actually needed.

The pipeline

stages:
  - build
  - lint

build-image:
  stage: build
  image: docker:24.0.5-dind
  services:
    - docker:24.0.5-dind
  rules:
    - changes:
        - Dockerfile
  before_script:
    - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY"
  script:
    - docker build -t "$CI_REGISTRY_IMAGE/ansible-lint:latest" .
    - docker push "$CI_REGISTRY_IMAGE/ansible-lint:latest"

lint:
  stage: lint
  image: "$CI_REGISTRY_IMAGE/ansible-lint:latest"
  script:
    - ansible-lint .

Two things doing the actual work:

The very first pipeline run still has to build the image (no Dockerfile diff to compare against yet), so it'll run both jobs once regardless.

The honest caveat

docker:dind needs the runner configured for privileged mode, which is a real security tradeoff on a shared runner (a privileged container can potentially interact with the host). Fine on a personal or trusted-team runner; GitLab's own docs currently point at BuildKit or Buildah as ways to build without privileged mode if that matters for your setup — outside the scope of "simple," but worth knowing it exists before wiring this into anything shared.

Created 2026-09-05T19:56:41+02:00 · Edit