Configuring Artifacts in CI/CD: GitLab and GitHub

Teams often lose hours rebuilding artifacts due to misconfigured CI/CD, slowing development and increasing costs. We configure artifacts in GitLab CI and GitHub Actions so build results pass between jobs without extra steps. Our team delivers turnkey projects—from pipeline audit to implementation and ongoing support—ensuring stable and fast operation of your processes.

Our competencies:

Frequently Asked Questions

Latest works

  • Development of a web application for FEEDME
    Development of a web application for FEEDME
    1365
  • Development of an online store for the company FURNORO
    Development of an online store for the company FURNORO
    1330
  • B2B Advance company logo design
    B2B Advance company logo design
    776
  • Development of a web application for Enviok
    Development of a web application for Enviok
    1077
  • AIDER company logo development
    AIDER company logo development
    1022
  • CRM development for Chasseurs
    CRM development for Chasseurs
    1113

We often see teams spending hours rebuilding artifacts due to incorrect CI/CD configuration. Instead of passing a compiled binary between jobs, they re-run all build steps. The result: pipeline time increases and developers wait. Incorrect artifact configuration leads to unnecessary CI minute costs and developer time. In this article, we'll cover artifact configuration in CI/CD for GitLab CI and GitHub Actions.

Recently, a microservices team approached us: their pipeline took 45 minutes because each service was rebuilt from scratch on every commit. After configuring artifact passing, the time dropped to 12 minutes—a 73% reduction, saving the team a significant portion of their CI budget. Such savings are possible if artifacts are configured correctly and implicit rebuilds are eliminated.

Why can't artifacts be replaced by cache?

They are often confused. Cache is for accelerating repetitive operations (dependencies, intermediate compilation files). Artifacts are for passing results between jobs within a single pipeline.

  • Cache: lives between pipeline runs, can be stale, hit is not guaranteed.
  • Artifact: created in the current pipeline, passed to subsequent jobs with certainty, deleted according to expire_in.

Comparison table:

Parameter Cache Artifact
Purpose Accelerate repeated steps Pass results between jobs
Lifetime Between pipelines Within a single pipeline (or longer)
Freshness guarantee No Yes
Typical data node_modules/, .m2/repository dist/, *.jar, reports/*.xml

How do artifacts speed up the pipeline?

Proper artifact configuration allows building DAG pipelines where jobs start immediately after receiving necessary artifacts, without waiting for an entire stage to finish. As a result, execution time is reduced by 30–70%. For example, in one project, we replaced a linear pipeline with a DAG using artifact passing—the pipeline ran in 8 minutes instead of 25. The average monthly savings amounted to $630–910 due to reduced CI minute costs.

How to configure artifacts in GitLab CI?

---
build:
  stage: build
  script:
    - npm ci
    - npm run build
  artifacts:
    paths:
      - dist/
      - package.json
    expire_in: 1 hour
    when: always
test:
  stage: test
  needs:
    - job: build
      artifacts: true
  script:
    - npm run test
  artifacts:
    reports:
      junit: reports/junit.xml
      coverage_report:
        coverage_format: cobertura
        path: coverage/cobertura-coverage.xml
deploy:
  stage: deploy
  needs:
    - job: build
      artifacts: true
  script:
    - rsync -av dist/ user@server:/var/www/app/

needs with artifacts: true—the deploy job downloads artifacts from build without waiting for intermediate stages. This is a DAG pipeline—a faster alternative to linear stages. More details in GitLab CI documentation.

How to pass artifacts in GitHub Actions?

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci && npm run build
      - uses: actions/upload-artifact@v4
        with:
          name: frontend-dist-${{ github.sha }}
          path: dist/
          retention-days: 1
          if-no-files-found: error
  deploy:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - uses: actions/download-artifact@v4
        with:
          name: frontend-dist-${{ github.sha }}
          path: dist/
      - run: ./scripts/deploy.sh dist/

if-no-files-found: error is an important setting. By default, upload succeeds even if no files exist, and the next job downloads an empty artifact. Better to fail explicitly. retention-days: 1 for intermediate build artifacts—they are only needed within this pipeline. For release artifacts, set longer. More details in GitHub Actions documentation.

Comparison between GitLab CI and GitHub Actions

Parameter GitLab CI GitHub Actions
Base syntax artifacts in job actions/upload-artifact step
Retention period expire_in (max 30 days for free) retention-days (max 90 days for free)
DAG pipelines needs with artifacts: true needs: at the job level
Reports Built-in support for JUnit and Cobertura Via custom actions
External storage integration Via scripts Via actions or scripts

When to use external storage?

For artifacts needed outside a single pipeline—release binaries, Docker images, npm packages—CI storage is not suitable. Here you need external storage:

  • GitHub Releases / GitLab Releases—artifacts attached to a tag, accessible via API.
  • S3 / GCS—for binaries and static files with versioning.
  • Nexus / Artifactory—for Maven, npm, Docker in corporate infrastructure.
  • Docker Registry—images tagged with git SHA and/or semver tag.

Publishing to Nexus from GitLab CI:

---
publish:
  stage: publish
  needs:
    - job: build
      artifacts: true
  script:
    - mvn deploy -s .mvn/settings.xml \
        -Dnexus.url=$NEXUS_URL \
        -Dnexus.username=$NEXUS_USER \
        -Dnexus.password=$NEXUS_PASS \
        -DskipTests
  rules:
    - if: '$CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+/'
---

Deploying to Nexus only on tags is a good policy. Storing artifacts in external storage reduces CI minute costs and frees up space in CI.

Step-by-step artifact configuration plan

  1. Audit current pipelines—identify all places where artifacts are created and consumed.
  2. Determine necessary artifacts—separate intermediate (dist/, reports/) from final (release binaries).
  3. Configure artifacts/upload-artifact—set correct paths, retention periods, and save conditions.
  4. Integrate with external storage—connect S3, Nexus, or Docker Registry for long-term storage.
  5. Test and monitor—verify artifacts are passed correctly, set up alerts for empty artifacts.

What's included in artifact configuration work

We assess your current pipeline, identify bottlenecks, and configure artifact passing turnkey. Result:

  • Pipeline audit: what artifacts are created, how long they live, where they are stored.
  • Configuration of artifacts / upload-artifact with optimal expire_in and retention-days.
  • Integration with external storage (S3, Nexus, Docker Registry) if needed.
  • Documentation on the process and recommendations for further optimization.
  • Support for one month after implementation.

We have helped teams reduce pipeline time by 30–70% through proper artifact configuration. Contact us for a pipeline audit—we evaluate your current configuration in one day and propose an optimal solution. Order turnkey artifact configuration and start saving time and budget today.

Typical mistakes when configuring artifacts

  • Too long retention: expire_in: never clogs storage. Set reasonable limits.
  • Missing if-no-files-found: error in GitHub Actions—the next step may silently deploy an empty artifact.
  • Passing dependencies instead of results: cache node_modules separately, don't drag them as artifacts.