When a developer opens a merge request against a Node.js or Python repository on GitLab, most teams want an automatic answer to one question: does this change introduce a new vulnerability? Snyk answers that question by shipping a CI/CD template — a .gitlab-ci.yml job definition built around the Snyk CLI — that developers drop into their pipeline configuration. The template authenticates against Snyk's platform, runs a scan (dependencies, containers, infrastructure-as-code, or first-party code), and translates the result into a pass/fail signal GitLab's runner can act on. That last step is what makes it a "gate" rather than just a report: a job that exits non-zero blocks the pipeline stage, and depending on how the job is configured, that can block the merge itself. Below is a mechanical walkthrough of how the pieces fit together, based on Snyk's publicly documented CI/CD integration for GitLab.
What is the Snyk GitLab CI/CD template, and how does it get added to a pipeline?
It is a reusable job block, distributed through Snyk's documentation, that teams paste into their project's .gitlab-ci.yml file rather than something GitLab installs for them automatically. Snyk publishes official Docker images (tagged by runtime, such as snyk/snyk:linux, snyk/snyk:node, snyk/snyk:python, and snyk/snyk:golang) that already have the Snyk CLI installed, so the template typically sets image: snyk/snyk:<runtime> for a job and defines a script: section that runs snyk auth followed by a scan command. Because it's plain YAML, teams commonly split it into multiple jobs — one per stage (test, security, deploy) — and scope each with rules: so the scan only runs on merge request pipelines or on pushes to protected branches, rather than every commit on every branch.
How does the job authenticate and actually run a scan?
Authentication happens through a CI/CD variable, not a hardcoded secret in the YAML file. Teams generate an API token from their Snyk account and store it in GitLab under Settings > CI/CD > Variables as SNYK_TOKEN, marked masked and protected so it's redacted from job logs and unavailable on unprotected branches. The job then runs snyk auth $SNYK_TOKEN (or passes the token as an environment variable the CLI reads directly), followed by a scan command matched to what's being checked: snyk test for open source dependency manifests (package.json, requirements.txt, go.mod, and similar), snyk code test for static analysis of first-party source, snyk container test <image> for container images, and snyk iac test for Terraform, CloudFormation, or Kubernetes manifests. Each command reads the relevant manifest or artifact in the job's working directory and calls out to Snyk's vulnerability database to evaluate it.
How does a scan result turn into an actual pipeline gate?
It comes down to the Snyk CLI's exit code, which GitLab's runner treats the same as any other shell command's exit status. Snyk's CLI returns 0 when no issues matching the policy are found, 1 when issues are found, 2 when the CLI itself failed to run (bad config, network error, and so on), and 3 when no supported project files were detected. A non-zero exit fails the GitLab job by default, which fails the pipeline unless the job is marked allow_failure: true. Teams tune what counts as "found" using CLI flags in the template: --severity-threshold=high limits failures to high and critical issues only, while --fail-on=upgradable fails the build only when a fix (an available upgrade) actually exists, avoiding gate failures on vulnerabilities with no remediation path yet. This is the mechanism that turns a scan from an informational report into an enforced check — the pipeline literally cannot proceed past a red job unless someone overrides it.
How do the findings surface inside GitLab's merge request and security views?
Findings can be piped into GitLab's native vulnerability reporting format, not just left in a raw CLI log. Snyk's output can be converted to GitLab's SAST/dependency report JSON schema and attached to the job as a report artifact (for example, under artifacts: reports: sast:), which GitLab then parses into the Merge Request's Security widget and the project's Security & Compliance > Vulnerability Report (a feature tied to GitLab's Ultimate tier). Without that conversion step, results still show up as plain-text output in the job log and as a pass/fail status check on the MR, which is the minimum most teams rely on. Separately, running snyk monitor at the end of a pipeline — typically only on the default branch — takes a snapshot of the project's dependency tree and uploads it to snyk.io, so Snyk can alert the team later if a new CVE is disclosed against a dependency that was clean at merge time.
What does the template cover beyond open source dependency scanning?
The same authentication and gating pattern extends across Snyk's four scan types, each invoked as a separate CLI subcommand and typically run as its own pipeline job. snyk test covers open source software composition analysis; snyk code test runs Snyk's static application security testing engine against first-party code for issues like injection flaws or hardcoded secrets; snyk container test scans a built image (including its OS packages) for known vulnerabilities before it's pushed to a registry; and snyk iac test checks infrastructure definitions for misconfigurations such as overly permissive security groups or public storage buckets. Because each is a distinct job, teams commonly run them in parallel stages so a container scan failure doesn't block an unrelated IaC check from also reporting, and can set independent severity thresholds per scan type — for instance, gating on critical for containers while gating on high for source code.
What are the practical limits of a CLI-based gate like this?
The gate only evaluates what's in the manifest or image at scan time, so it's as current as the last snyk test run and as complete as the CLI's language and ecosystem support allows for that repository. A pipeline gate configured with allow_failure: true, or a developer with permission to skip required checks on a protected branch, can still merge past a failing scan — the CLI produces a signal, but enforcement is only as strong as the GitLab branch protection rules and merge request approval settings layered on top of it. It also scans one repository's pipeline at a time; teams running dozens of services need the same job block replicated (or centralized via GitLab's include: mechanism) across every repository to get consistent coverage, and each addition or version bump to the template has to be rolled out and maintained separately.
How Safeguard Helps
Snyk's GitLab template is a solid example of gating a single pipeline on a single scanner's output, but most organizations run dependency scanning, container scanning, IaC checks, and secret detection from several different tools across dozens of repositories — and keeping severity thresholds, exemptions, and enforcement consistent across all of them by hand doesn't scale. Safeguard's software supply chain security platform sits above that layer: it aggregates findings from tools like Snyk alongside SBOM data, build provenance, and artifact attestations, so a single policy (say, "no unpatched critical vulnerabilities in production images") is enforced consistently whether the underlying scan ran in GitLab CI, GitHub Actions, or a separate build system. Instead of each team hand-tuning --severity-threshold flags and remembering to add snyk monitor to new pipelines, Safeguard gives security teams one place to see which repositories have gates configured, which don't, and where scan coverage has drifted — turning a per-pipeline CLI convention into an organization-wide, auditable control.