GitLab CI pipelines can run Cypress tests, but they often fail because the CI environment doesn’t have a graphical display (a "display server") and Cypress requires one to launch the browser.

Here’s how to get it working, covering the most common pitfalls:

Cause 1: No Display Server (xvfb)

Diagnosis: You’ll see errors like Xvfb is not installed or Error: connect ECONNREFUSED 127.0.0.1:3000 (if Cypress tries to connect to a non-existent display).

Fix: Install and run xvfb (X Virtual Framebuffer) as a service in your CI job. xvfb creates a virtual display that Cypress can use without a physical monitor.

# .gitlab-ci.yml
your_cypress_job:
  image: cypress/base:18 # Use a Cypress image with xvfb pre-installed
  services:
    - xvfb
  script:
    - npm ci
    - npm run cypress:run

Why it works: The xvfb service starts automatically, and Cypress, when run in this environment, defaults to using it. The cypress/base image is convenient as it includes xvfb and other necessary dependencies.

Cause 2: Incorrect Browser Configuration

Diagnosis: Cypress might try to launch a browser that isn’t installed in the Docker image, leading to errors like Cypress failed to spawn a browser process.

Fix: Explicitly tell Cypress which browser to use and ensure it’s available. If using cypress/base, Chrome is usually pre-installed.

// cypress.config.js
const { defineConfig } = require('cypress');

module.exports = defineConfig({
  e2e: {
    setupNodeEvents(on, config) {
      // implement node event listeners here
    },
    browser: 'chrome', // Explicitly set to Chrome
  },
});

If you need Firefox:

# .gitlab-ci.yml
your_cypress_job:
  image: cypress/browsers:node18-chrome108-ff107 # Image with Chrome and Firefox
  services:
    - xvfb
  script:
    - npm ci
    - cypress.config.js # Ensure cypress.config.js sets browser: 'firefox'

Why it works: Specifying the browser ensures Cypress attempts to launch a known binary. Using images like cypress/browsers provides pre-installed, specific browser versions.

Cause 3: Insufficient Memory or Timeout Issues

Diagnosis: Jobs might fail with generic timeouts or memory exhaustion errors, especially on more complex test suites. Your GitLab Runner might not have enough resources allocated.

Fix: Increase the memory allocated to your GitLab Runner or the specific CI job. This is often configured at the GitLab Runner level (e.g., in config.toml if you manage your own runners) or by using a more powerful runner. You can also configure Cypress timeouts:

// cypress.config.js
const { defineConfig } = require('cypress');

module.exports = defineConfig({
  e2e: {
    // ... other configs
    defaultCommandTimeout: 10000, // Increase command timeout to 10 seconds
    requestTimeout: 30000,      // Increase request timeout to 30 seconds
  },
  // Increase viewport size if tests fail due to element visibility
  viewportWidth: 1920,
  viewportHeight: 1080,
});

Why it works: Longer timeouts give Cypress more time to perform actions, and increased memory prevents the process from being killed due to resource constraints. Larger viewports can ensure elements are visible and interactable.

Cause 4: Incorrect npm or yarn Dependency Installation

Diagnosis: Errors during npm ci or yarn install like command not found or missing Cypress binaries.

Fix: Ensure your package.json has Cypress as a dev dependency and use npm ci for cleaner, faster installs in CI.

// package.json
{
  "devDependencies": {
    "cypress": "^12.0.0"
  }
}
# .gitlab-ci.yml
your_cypress_job:
  # ... other configs
  script:
    - npm ci # Use npm ci for reproducible installs
    - npm run cypress:run

Why it works: npm ci installs dependencies directly from package-lock.json, ensuring consistency and avoiding potential issues with package.json version conflicts. It also performs a cleaner install than npm install.

Cause 5: Docker Image Caching Issues

Diagnosis: Intermittent test failures or slow pipeline runs where dependencies seem to be re-downloaded every time.

Fix: Configure Docker layer caching for your CI jobs. This speeds up subsequent runs by reusing layers from previous builds.

# .gitlab-ci.yml
your_cypress_job:
  image: cypress/base:18
  services:
    - xvfb
  cache:
    key: ${CI_COMMIT_REF_SLUG} # Cache per branch
    paths:
      - node_modules/
  script:
    - npm ci
    - npm run cypress:run

Why it works: By caching the node_modules directory, subsequent pipeline runs won’t need to re-download and reinstall all npm packages, significantly reducing execution time.

Cause 6: Incorrect cypress/base vs cypress/included Image Choice

Diagnosis: Errors related to missing browsers or tools when you expect them to be present.

Fix: Choose the appropriate Cypress Docker image. cypress/base is minimal and requires you to install browsers if needed. cypress/included comes with Chrome and Firefox pre-installed.

# .gitlab-ci.yml
your_cypress_job:
  image: cypress/included:18.0.0 # Use 'included' if you need browsers out-of-the-box
  services:
    - xvfb
  script:
    - npm ci
    - npm run cypress:run

Why it works: cypress/included simplifies setup by providing common browsers directly, avoiding the need to install them manually within the Dockerfile or CI script.

The next error you’ll likely encounter is related to test reporting and artifact management, where Cypress outputs are not saved correctly for review.

Want structured learning?

Take the full Cypress course →