CoursesSecure CI/CD with GitLabGitLab CI fundamentals

Artifacts, cache & rules

Pass files, speed builds, run jobs conditionally.

Intermediate14 min · lesson 3 of 17

Jobs are isolated, so to pass files between them you declare artifacts. A build job produces a dist/ folder and lists it under artifacts:paths; GitLab uploads it and automatically makes it available to later stages. This is how a compiled binary reaches the deploy job or a test report reaches the UI — without it, each job starts from a clean checkout with nothing from the previous stage.

.gitlab-ci.yml
build:
stage: build
script: [make dist]
artifacts:
paths: [dist/]
expire_in: 1 week # do not keep build outputs forever
reports:
junit: report.xml # GitLab renders this as test results
deploy:
stage: deploy
script: [./deploy.sh dist/] # dist/ is here automatically, from build

Cache vs artifacts

They look similar but serve different jobs. Artifacts pass build outputs forward between stages and are tied to a pipeline. Cache speeds up jobs by reusing downloaded dependencies (node_modules, .m2, pip cache) across pipelines — it is an optimization, not a contract, and a job must still work if the cache is empty. Rule of thumb: artifacts for things you produce and need later; cache for things you can always re-fetch.

.gitlab-ci.yml
install:
stage: build
script: [npm ci]
cache:
key: ${CI_COMMIT_REF_SLUG} # per-branch cache
paths: [node_modules/]
policy: pull-push # read it, then update it

rules: run jobs only when they should

The rules: keyword decides whether a job runs, based on conditions — the branch, whether it is a merge request, changed files, pipeline variables. It replaces the older only/except and is how one .gitlab-ci.yml handles feature branches, merge requests, and production deploys without duplicating jobs. A deploy job, for instance, should have a rule that runs it only on the default branch.

.gitlab-ci.yml
deploy-prod:
stage: deploy
script: [./deploy.sh production]
rules:
- if: '$CI_COMMIT_BRANCH == "main"' # only on main
when: manual # and only when a human clicks
- when: never # otherwise this job does not exist
Do not cache secrets or artifact them
Cache and artifacts are stored by GitLab and downloadable; a credential file, a .env, or a token that ends up in a cached or artifacted path is exposed to anyone who can read the pipeline. Scope artifact paths tightly, set expire_in, and never include secret material — secrets come from CI/CD variables (covered later), never from files you cache or upload.