A Definitive Guide to Using Cypress Environment Variables

July 28, 2025

Imagine your entire test suite crashing in the CI/CD pipeline. After frantic debugging, you find the culprit: a hardcoded base URL pointing to the staging environment, while the pipeline was deploying to production. This common, frustrating scenario highlights a critical principle in test automation: the separation of configuration from code. This is precisely the problem that Cypress environment variables are designed to solve. By externalizing environment-specific values—like URLs, API keys, and user credentials—you create flexible, maintainable, and portable tests that can run seamlessly across any environment. This guide provides a deep dive into the world of Cypress environment variables, exploring every method for setting them, understanding their order of precedence, and implementing best practices for a truly professional testing setup.

What Exactly Are Cypress Environment Variables?

At their core, Cypress environment variables are dynamic values that you can pass to your test suite. Unlike hardcoded constants, these variables can be changed without altering the test code itself. This is essential for managing configurations that differ between your local development machine, a shared staging server, and the live production environment. Think of them as placeholders for data that your tests need to run, such as:

  • Base URLs: http://localhost:3000, https://staging.myapp.com, https://myapp.com
  • API Endpoints: /api/v1/users, /api/v2/users
  • User Credentials: Usernames and passwords for different roles (admin, standard user, guest).
  • Third-party API Keys: Keys for services like Stripe, SendGrid, or Google Maps.

Using Cypress environment variables aligns with the best practices outlined in the Twelve-Factor App methodology, which advocates for storing configuration in the environment. This approach dramatically improves the scalability and maintainability of your test suite. A Forrester report on DevOps trends emphasizes that robust configuration management is a key differentiator for high-performing teams, directly impacting deployment frequency and stability. By mastering Cypress.env(), you are not just writing tests; you are engineering a resilient and professional automation framework.

Five Methods for Setting Cypress Environment Variables

Cypress offers several powerful mechanisms for defining environment variables, each suited for different use cases. Understanding these options is crucial for building a flexible configuration strategy. It's important to note that since Cypress v10, the primary configuration file has shifted from cypress.json to cypress.config.js (or .ts), which offers more dynamic capabilities. We'll cover both modern and legacy approaches.

1. The Configuration File (cypress.config.js)

This is the most common and recommended place to set default environment variables for your project. By defining them in cypress.config.js, you ensure they are available to all tests within the project. You can add an env key to your configuration object.

Example (cypress.config.js):

const { defineConfig } = require('cypress');

module.exports = defineConfig({
  e2e: {
    setupNodeEvents(on, config) {
      // implement node event listeners here
    },
    baseUrl: 'http://localhost:3000',
    env: {
      login_url: '/login',
      api_server: 'http://localhost:8080/api/v1',
      user_role: 'admin'
    }
  },
});

This method is excellent for non-sensitive, project-wide defaults. According to official Cypress documentation, this file is the heart of your Cypress setup and the first place you should look for project configuration.

2. The cypress.env.json File

For values you wish to override or keep separate from the main configuration, Cypress automatically looks for a cypress.env.json file. This file is often added to .gitignore to prevent sensitive data like API keys or credentials from being committed to version control. This practice is a cornerstone of secure software development, as highlighted by OWASP's security guidelines.

Example (cypress.env.json):

{
  "api_key": "super_secret_key_12345",
  "user_role": "power_user",
  "api_server": "https://staging.api.myapp.com"
}

If a variable is defined in both cypress.config.js and cypress.env.json, the value from cypress.env.json will take precedence. This makes it ideal for local overrides.

3. Command Line Flags (--env)

For ultimate flexibility, especially within CI/CD pipelines, you can pass environment variables directly through the command line when you run Cypress. This is achieved using the --env flag. This method overrides any values set in the configuration files.

Example (Command Line):

$ npx cypress run --env user_role=guest,api_server=https://prod.api.myapp.com

This dynamic approach is essential for modern DevOps practices. A guide on continuous integration from Atlassian underscores the importance of parameterizing build scripts for different stages, and the --env flag is Cypress's answer to this need.

4. System Environment Variables (Prefixed with CYPRESS_)

Cypress can automatically read system-level environment variables from your shell, provided they are prefixed with CYPRESS_. This is another powerful method for CI/CD environments where secrets are often managed as environment variables on the build agent.

Example (Shell):

$ export CYPRESS_API_KEY="ci_secret_key_from_vault"
$ export CYPRESS_USER_ROLE="ci_runner"
$ npx cypress run

Cypress will automatically strip the CYPRESS_ prefix and convert the variable name to lowercase. So, CYPRESS_API_KEY becomes available in your tests as api_key. This is a clean, secure way to inject secrets without them ever touching your codebase, a principle strongly advocated in GitHub Actions documentation for secret management.

5. The Plugins File (setupNodeEvents)

For the most advanced and dynamic scenarios, you can use the setupNodeEvents function within your cypress.config.js. This function runs in a Node.js environment, giving you the power to read files, make API calls, or perform any other Node-based logic to determine your configuration before the tests run. The returned config object will be the final configuration used by Cypress.

Example (cypress.config.js):

const { defineConfig } = require('cypress');
const fs = require('fs');

module.exports = defineConfig({
  e2e: {
    setupNodeEvents(on, config) {
      // Load a config file based on the target environment
      const environment = config.env.target_environment || 'staging';
      const configFile = `./config/${environment}.json`;
      const settings = JSON.parse(fs.readFileSync(configFile, 'utf8'));

      // Merge the new settings into the config.env object
      config.env = { ...config.env, ...settings };

      // It's important to return the updated config object
      return config;
    },
  },
});

This method allows you to build sophisticated configuration-loading strategies, such as pulling settings from a cloud provider's secret manager or a dedicated configuration service.

Understanding the Hierarchy: Which Variable Wins?

With multiple ways to define Cypress environment variables, it's critical to understand their order of precedence. When the same variable is defined in multiple locations, Cypress follows a strict hierarchy to decide which value to use. This prevents confusion and allows you to create a layered configuration strategy where defaults can be easily overridden.

The order of precedence, from lowest to highest (meaning the highest one wins), is as follows:

  1. cypress.config.js (env object): These are your base-level defaults.
  2. cypress.env.json: Values here override those in the main configuration file.
  3. System Environment Variables (prefixed with CYPRESS_): These override the cypress.config.js and cypress.env.json files.
  4. Command Line Arguments (--env flag): These have the highest precedence and will override all other methods.

Let's consider an example. Suppose you have the api_server variable defined in three places:

  • In cypress.config.js: env: { api_server: 'http://localhost:8080' }
  • In cypress.env.json: { "api_server": "https://staging-api.myapp.com" }
  • In the command line: npx cypress run --env api_server=https://prod-api.myapp.com

In this scenario, the test will run using https://prod-api.myapp.com because the --env flag has the highest priority. This hierarchical system is a feature, not a bug. It enables a robust strategy where you can set sensible defaults in your repo, allow developers to override them locally with cypress.env.json, and let your CI/CD pipeline have the final say with command line flags. This layered approach is a hallmark of mature testing frameworks, as discussed in research on flexible software design from MIT.

Accessing Environment Variables in Your Tests

Once you've set up your Cypress environment variables, accessing them within your test specs (.cy.js files) is straightforward using the Cypress.env() command. This command retrieves the final, resolved value of a variable after considering the entire precedence hierarchy.

Here's how you can use it in a typical test:

// In your spec file: e.g., login.cy.js

describe('Login Functionality', () => {
  it('should log in a user based on their role', () => {
    // Access the base URL (a top-level config option, not env var)
    const baseUrl = Cypress.config('baseUrl');
    // Access an environment variable
    const loginUrl = Cypress.env('login_url');
    const userRole = Cypress.env('user_role');
    const username = Cypress.env(`${userRole}_username`); // e.g., admin_username
    const password = Cypress.env(`${userRole}_password`);

    cy.visit(`${baseUrl}${loginUrl}`);

    cy.get('#username').type(username);
    cy.get('#password').type(password);
    cy.get('button[type="submit"]').click();

    cy.url().should('include', '/dashboard');
  });
});

In this example, we dynamically construct the keys for username and password based on the user_role environment variable. This allows the same test to validate login for an admin, a user, or a guest simply by changing the user_role variable when running the test suite. This dynamic test data approach is a significant step up from hardcoded values, making your tests more powerful and reusable. For more complex logic, you can retrieve all environment variables as an object by calling Cypress.env() with no arguments.

Best Practices for a Scalable Strategy

Simply knowing how to use Cypress environment variables is only half the battle. Implementing them effectively requires a thoughtful strategy to ensure your test suite remains clean, secure, and scalable.

  • Separate Configuration by Environment: Create distinct configuration files for each environment (e.g., config/development.json, config/staging.json) and use the setupNodeEvents plugin to load the correct one. This keeps environment-specific logic clean and centralized.

  • Never Commit Secrets: Always add cypress.env.json and any files containing sensitive data to your .gitignore file. For CI/CD, use the platform's secret management tools (e.g., GitHub Secrets, AWS Secrets Manager) and pass them as CYPRESS_ prefixed variables. A report on developer security practices by Snyk consistently shows that leaked secrets in Git repositories are a major source of security breaches.

  • Use a Clear Naming Convention: Adopt a consistent naming scheme for your variables, such as user_admin_username or api_v2_endpoint. This makes your configuration self-documenting and easier to manage as the project grows.

  • Document Your Variables: Maintain a README.md or a sample environment file (e.g., .env.example) in your repository. This file should list all the required environment variables, their purpose, and an example value, without containing any real secrets. This practice significantly lowers the barrier to entry for new developers joining the project.

  • Leverage baseUrl: For your application's main URL, prefer the top-level baseUrl configuration option over an environment variable. Cypress has special handling for baseUrl that simplifies commands like cy.visit() and cy.request(), as you can use relative paths (e.g., cy.visit('/login')). Use environment variables for other URLs, like external API servers.

Effectively managing Cypress environment variables is a cornerstone of professional test automation. It elevates your test suite from a rigid set of scripts to a flexible, resilient framework capable of adapting to any environment. By understanding the different methods of setting variables—from the cypress.config.js file to command line flags—and mastering their order of precedence, you gain complete control over your test configuration. This allows you to build a single, reliable test suite that can be executed with confidence against your local machine, staging, and production environments. Adopting these practices not only prevents common CI/CD failures but also establishes a scalable and maintainable foundation for your quality assurance efforts, ultimately leading to higher quality software.

What today's top teams are saying about Momentic:

"Momentic makes it 3x faster for our team to write and maintain end to end tests."

- Alex, CTO, GPTZero

"Works for us in prod, super great UX, and incredible velocity and delivery."

- Aditya, CTO, Best Parents

"…it was done running in 14 min, without me needing to do a thing during that time."

- Mike, Eng Manager, Runway

Increase velocity with reliable AI testing.

Run stable, dev-owned tests on every push. No QA bottlenecks.

Ship it

FAQs

Momentic tests are much more reliable than Playwright or Cypress tests because they are not affected by changes in the DOM.

Our customers often build their first tests within five minutes. It's very easy to build tests using the low-code editor. You can also record your actions and turn them into a fully working automated test.

Not even a little bit. As long as you can clearly describe what you want to test, Momentic can get it done.

Yes. You can use Momentic's CLI to run tests anywhere. We support any CI provider that can run Node.js.

Mobile and desktop support is on our roadmap, but we don't have a specific release date yet.

We currently support Chromium and Chrome browsers for tests. Safari and Firefox support is on our roadmap, but we don't have a specific release date yet.

© 2025 Momentic, Inc.
All rights reserved.