QA How-To
How to Fix "Cypress could not verify that this server is running"
Fix Cypress could not verify that this server is running by checking baseUrl, app startup, CI readiness, Docker hostnames, and component dev server issues.
23 min read | 3,103 words
TL;DR
Start the application, request the exact baseUrl from the environment where Cypress runs, and launch Cypress only after that URL responds. If the request fails, correct the server process, host, port, or container route; if it succeeds, inspect Cypress config overrides and proxy or TLS differences.
Key Takeaways
- Probe the exact URL from the same machine or container that runs Cypress.
- Start the E2E application before Cypress and retain its startup logs.
- Keep the server listener, Cypress baseUrl, and readiness check on the same host and port.
- Use a bounded HTTP readiness gate in CI instead of a fixed sleep.
- Use Compose service names for sibling containers; localhost stays inside one container.
- Diagnose Component Testing through component.devServer because Cypress owns that server.
- Confirm the fix with an HTTP request and a focused Cypress spec before rerunning the suite.
To fix Cypress could not verify that this server is running, check the URL printed by Cypress from the same machine or container that launches it. This startup error appears when an end-to-end run has a configured baseUrl but Cypress cannot reach the application at that address before executing a spec.
Cypress could not verify that this server is running:
> http://localhost:5173
We are verifying this server because it has been configured as your `baseUrl`.
TL;DR
Start the application outside Cypress, probe its exact baseUrl from Cypress's execution environment, and run Cypress only after the server responds. For a Vite project configured on port 5173, a quick local diagnosis is:
npm run dev -- --host 127.0.0.1 --port 5173 --strictPort
In a second terminal, from the same project directory:
curl -v --max-time 5 http://127.0.0.1:5173/
npx cypress run --config baseUrl=http://127.0.0.1:5173
A successful curl followed by a Cypress run confirms that startup and URL alignment are fixed. If curl fails, read its connection error and the application terminal. If curl succeeds but Cypress still reports this error, inspect the effective Cypress configuration, proxy settings, and the actual host from which Cypress runs. The Cypress framework setup guide provides broader project setup context.
What the Error Actually Means
For end-to-end testing, Cypress checks the configured e2e.baseUrl before running specs. The baseUrl also prefixes relative cy.visit() and cy.request() URLs. When the server cannot be verified, the failure occurs before test commands such as cy.get() execute. A longer DOM command timeout cannot fix a connection that has not been established.
This message does not prove that your application code failed. The process may never have started, may have exited during boot, may be listening on another port, or may be reachable only through another network address. It can also be an address resolution problem: localhost inside a Cypress container refers to that container, even if the application responds on the host computer. An HTTP response with an unexpected status is a different clue from a refused TCP connection; do not collapse both into "the server is down."
Cypress configuration and Cypress CLI options are the source of truth for these options.
Component Testing needs a separate diagnosis. Cypress normally launches its own Vite or Webpack dev server from component.devServer, then sets an internal URL. If the same headline appears during cypress run --component, inspect that dev server and its framework configuration; starting your production app may be irrelevant. The Cypress component testing guide covers that workflow.
Root-Cause Decision Table
| Symptom | Root cause | Fix |
|---|---|---|
curl reports connection refused and no app process is running |
Application was never started or crashed | Start it, read startup logs, then run Cypress |
| App log shows a different port from the Cypress URL | baseUrl and server port disagree |
Set the same host, scheme, and port on both sides |
| Manual run works but CI intermittently fails | Cypress starts before the app is ready | Use a readiness gate with a bounded timeout |
| Host browser works but Cypress container fails | localhost refers to different network namespaces |
Use a Compose service name or the correct host route |
localhost fails while 127.0.0.1 works, or vice versa |
Hostname resolution or bind-address mismatch | Probe both families and align the listener and URL |
| Only corporate or remote environments fail | Proxy, certificate, VPN, or access restriction | Test the path from the runner, then repair network configuration |
| E2E starts but Component Testing fails | Cypress-managed component dev server has a different failure | Inspect component.devServer and its build output |
1. Fix Cypress Could Not Verify That This Server Is Running When the App Is Not Started
Cypress does not generally start your application for an E2E run. Open a terminal for the application and leave it running. If your project uses Vite, run its existing dev script and pin the address during diagnosis:
npm run dev -- --host 127.0.0.1 --port 5173 --strictPort
--strictPort makes Vite fail rather than silently switching to a free port. That behavior is helpful here because an automatic move to another port would leave Cypress checking the old one. If your app uses another framework, use its server command and documented host and port flags; do not copy Vite flags into an unrelated CLI. Wait for the server's own "ready" output. If the command exits, investigate its first error, such as missing environment variables, a build failure, or an occupied port. Running Cypress again without fixing a crashing app only repeats the symptom. From a second terminal, verify the application independently:
curl -i --max-time 5 http://127.0.0.1:5173/
Expect an HTTP status and response headers. An HTML shell with status 200 is a straightforward positive check for a typical Vite app. If curl says "Failed to connect," inspect the server terminal and whether the process stayed alive. If the app deliberately redirects or requires authentication, record that result; it still distinguishes a reachable HTTP service from no listener.
Finally run npx cypress run --config baseUrl=http://127.0.0.1:5173. That command verifies the whole path from Cypress to the app. Keep separate app and test terminals until you introduce a supervised CI startup command. The application lifecycle is a prerequisite for E2E tests, not a beforeEach() task.
2. Fix Cypress Could Not Verify That This Server Is Running When baseUrl Points to the Wrong Address
Compare three values character by character: the URL in Cypress's message, the listening URL printed by the server, and the URL that responds to a request from the Cypress machine. Include scheme, hostname, port, and path. https:// and http:// are distinct; a service on port 5173 is not on port 3000 because a README used that example.
For a TypeScript Cypress project whose Vite server listens on 127.0.0.1:5173, use a single E2E configuration:
// cypress.config.ts
import { defineConfig } from 'cypress'
export default defineConfig({
e2e: {
baseUrl: 'http://127.0.0.1:5173',
},
})
A relative visit now resolves against that base URL:
// cypress/e2e/home.cy.ts
describe('home page', () => {
it('loads the application', () => {
cy.visit('/')
cy.get('body').should('be.visible')
})
})
Verify the file and any override with:
curl -i --max-time 5 http://127.0.0.1:5173/
npx cypress run --spec cypress/e2e/home.cy.ts
If the first succeeds but Cypress prints a different URL, look for --config baseUrl=..., CYPRESS_BASE_URL, or programmatic config changes in setupNodeEvents. An explicit one-run override helps isolate a stale environment value:
npx cypress run --config baseUrl=http://127.0.0.1:5173 --spec cypress/e2e/home.cy.ts
Do not assume the config file is authoritative when CI or a wrapper script injects overrides. The Cypress environment variables guide explains the distinction between Cypress configuration variables and test data passed through env.
3. Gate CI on Readiness Instead of Racing the Server
A backgrounded start command does not mean the app is ready. Package installation, bundling, database migrations, and port binding can take longer on CI workers than on a warm laptop. The stable sequence is start, observe readiness, then execute Cypress. The public start-server-and-test package is designed for this sequence and shuts down the server after tests finish.
For the Vite example above, install it in the project that already contains Cypress and the app scripts:
npm install --save-dev start-server-and-test
Add a single npm script. The following is a valid scripts object to merge into your existing package.json; retain unrelated scripts already present:
{
"scripts": {
"dev": "vite --host 127.0.0.1 --port 5173 --strictPort",
"cy:run": "cypress run",
"test:e2e": "start-server-and-test dev http://127.0.0.1:5173 cy:run"
}
}
Then run the same command locally and in CI:
npm run test:e2e
Verify that the log shows Vite becoming available, the URL responding, and only then the Cypress spec starting. If the app exits during startup, the command should fail with the server error rather than quietly proceeding. If the app has a health endpoint that depends on its database, wait for that endpoint instead of the root HTML. A 200 from a static page does not guarantee that dependent services are ready.
For GitHub Actions, the official Cypress action also provides start and wait-on inputs. Set start to your app command and wait-on to the same URL Cypress uses; its wait-on-timeout input is in seconds. Follow the official action's current input documentation for an action version that matches your workflow. The CI test framework guide helps place this readiness gate in a full pipeline.
4. Correct Docker and Compose Hostnames
Inside a container, localhost is the container itself. If the app runs in a sibling Compose service named app, configure Cypress to reach http://app:5173, and make the app bind to 0.0.0.0 inside its container. The service name is provided by Compose networking. A port published to the host is useful for your browser, but sibling services can use the internal service port directly.
Here is a Compose pattern for an npm and Vite project. Replace both image-tag placeholders with real tags that match your project's installed Node and Cypress requirements; do not guess version numbers:
services:
app:
image: node:<your-project-node-version>
working_dir: /work
volumes:
- .:/work
- deps:/work/node_modules
command: sh -c "npm ci && npm run dev -- --host 0.0.0.0 --port 5173 --strictPort"
healthcheck:
test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:5173/').then(r => { if (!r.ok) process.exit(1) }).catch(() => process.exit(1))"]
interval: 5s
timeout: 5s
retries: 12
cypress:
image: cypress/included:<your-installed-cypress-version>
working_dir: /e2e
volumes:
- .:/e2e
- deps:/e2e/node_modules
environment:
CYPRESS_BASE_URL: http://app:5173
depends_on:
app:
condition: service_healthy
entrypoint: ["sh", "-c"]
command: ["npx cypress run"]
volumes:
deps:
The named deps volume lets the app service install packages before Cypress uses them. The explicit healthcheck prevents Compose from treating mere process creation as application readiness. The CYPRESS_BASE_URL value overrides the local baseUrl only inside the Cypress service, so the earlier config still works for local terminal runs. Run the following after saving the Compose definition:
docker compose up --abort-on-container-exit --exit-code-from cypress
Verify that the app service becomes healthy and the Cypress log no longer checks localhost. If the app instead runs on the Docker host, use the host route appropriate for that Docker platform, and probe it from inside the container before changing Cypress. The official Cypress CI documentation describes its image families and how their tags relate to installed tools.
5. Resolve Loopback, Bind Address, and Port Conflicts
A service can be live but unreachable at the hostname Cypress resolves. localhost may resolve to an IPv6 loopback address while the server listens only on IPv4, or the reverse. A dev server may bind only to 127.0.0.1, making it unreachable from another container or machine even when a host browser works. A port conflict can also cause Vite to pick another port unless strict-port behavior is enabled.
Probe both loopback forms from the Cypress environment:
curl -4 -i --max-time 5 http://localhost:5173/
curl -6 -i --max-time 5 http://localhost:5173/
curl -i --max-time 5 http://127.0.0.1:5173/
The IPv6 command may fail on a system without IPv6; that result is diagnostic, not automatically a defect. Compare which request reaches the app with the server's listener output. On macOS or Linux, inspect the listener if available:
lsof -nP -iTCP:5173 -sTCP:LISTEN
If another process owns the port, stop that process through its normal project command or choose an unused port and update both app startup and Cypress baseUrl. Avoid killing an unknown PID just to make the error disappear. If Cypress is in a sibling container, binding the app to 0.0.0.0 inside the app container is usually required; keep the Cypress URL as the Compose service name, not 0.0.0.0. That address is a listener wildcard, not a stable destination hostname.
Verify the selected route with curl -i --max-time 5 <your-exact-base-url> in the Cypress environment, then run the focused spec with npx cypress run --spec cypress/e2e/home.cy.ts. The two commands separate a network path from a test assertion failure.
6. Diagnose Proxy, TLS, Authentication, and Remote Service Failures
Remote staging URLs bring extra boundaries: VPN routes, proxy variables, private DNS, TLS trust, and authentication gateways. First run a verbose request from the same runner that launches Cypress:
curl -v --max-time 10 https://staging.example.test/
Replace the example domain with your real staging URL. In the output, distinguish DNS failure, connection timeout, certificate validation, redirect, and HTTP authentication status. If proxy variables are configured, print their names and redacted values without leaking credentials:
node -e "for (const k of ['HTTP_PROXY','HTTPS_PROXY','NO_PROXY']) console.log(k, process.env[k] ? '[set]' : '[unset]')"
For an internal host, NO_PROXY may need to include the domain or service name so the request is not sent to an external proxy. Do not disable TLS validation globally to get a green run; that changes what the test proves and can conceal a real deployment defect. Work with the environment owner to install the correct CA or route. Cypress's troubleshooting debug guidance documents DEBUG=cypress:* if ordinary application and curl logs do not explain the path.
Verify the repair with curl -v --max-time 10 https://staging.example.test/ from CI, followed by npx cypress run --config baseUrl=https://staging.example.test/ --spec cypress/e2e/home.cy.ts. If the request succeeds but Cypress fails, compare the process environment used for both commands and inspect the URL Cypress prints. The CI troubleshooting questions cover related runner isolation and dependency checks.
7. Repair the Component Testing Dev Server Separately
Component Testing does not normally require your E2E application server. Cypress starts a dev server for the component harness, and its generated baseUrl points to that server. A failure there can indicate a bundler startup error, a framework mismatch, an unavailable port, or a config override that points the component run at an unrelated app.
For a React project using Vite, the Cypress configuration should identify the component framework and bundler:
// cypress.config.ts
import { defineConfig } from 'cypress'
export default defineConfig({
e2e: {
baseUrl: 'http://127.0.0.1:5173',
},
component: {
devServer: {
framework: 'react',
bundler: 'vite',
},
},
})
This extends the E2E config shown earlier; it does not create a second competing config file. Use the framework actually installed in your app. The official Component Testing configuration says the built-in Vite and Webpack integrations supply the dev server and set its runtime URL.
Run a component spec through the component mode, using a real file in your repository:
npx cypress run --component
Run this after adding at least one component spec. Verification is that Cypress starts the component dev server, loads a spec, and no longer reports a base URL verification failure. If the bundler prints a compile error, fix that error first. If E2E works but component mode fails, avoid changing the E2E baseUrl just because the same headline appears. The two modes have different server ownership. A reported historical localhost resolution issue in Component Testing is a reason to compare the resolved loopback address, not a reason to apply a blanket workaround without evidence.
How to Verify the Fix
Use a short proof sequence instead of treating a green full suite as the only evidence. First, record the exact URL from the error. Second, request it from the process or container that runs Cypress. Third, start the application with a deterministic port and wait for a meaningful response. Fourth, run one known spec. Finally, run the normal CI command once the focused path works.
curl -i --max-time 5 http://127.0.0.1:5173/
npx cypress run --config baseUrl=http://127.0.0.1:5173 --spec cypress/e2e/home.cy.ts
Use your real URL and spec path. The first command should return the intended app. The second should pass server verification; then use the single-spec Cypress workflow for any assertion failure. For CI, verify the startup gate also fails when the app cannot boot. Temporarily test on a disposable branch or local runner with a deliberately unavailable URL, then restore the correct value before committing. A pipeline that reports success despite no server is not a valid gate. Compare local and CI URLs, bind addresses, and environment variables in logs without printing secrets. If the defect was intermittent, run the focused job several times at normal CI concurrency; a one-off pass may hide a race. The flaky Cypress test guide helps distinguish later test instability from this startup failure.
Prevent It From Coming Back
Keep one documented source for the app host and port per environment. Local scripts should pin the port and fail on conflicts. CI should use a readiness check, not a clock-based delay, and its URL should match Cypress's resolved baseUrl. In Docker, make the network destination explicit: use a service name for sibling containers and test from inside the Cypress container.
Choose a readiness target that represents what your first test needs. A bare TCP open proves only that a process listens; a 200 from / proves it can answer that page; a health endpoint can additionally verify database or API dependencies. Avoid a health endpoint so strict that a healthy app is blocked by an unrelated optional integration. The contract belongs in the app and pipeline documentation.
Preserve the app server's stdout and stderr in failed CI jobs. These logs often explain the error before any Cypress debug stream is needed. A named, short smoke spec like home.cy.ts makes it cheap to confirm the transport path after infrastructure changes. Review CYPRESS_BASE_URL, --config, and setupNodeEvents whenever a new environment is added so override precedence remains visible.
Keep local and container workflows separate where their hostnames differ, but make both run the same E2E spec. Do not make the test silently switch hosts based on availability; that can run against the wrong deployment and yield false confidence. If the application is intentionally unavailable, fail early and clearly. The Cypress API wait guide addresses in-test request timing after startup, which is a different layer from server verification.
Interview Questions and Answers
Q: Why does Cypress verify baseUrl before E2E tests?
Because relative cy.visit() and cy.request() calls depend on that origin. Checking reachability before specs gives a direct configuration or startup failure instead of a sequence of misleading test failures.
Q: What is your first diagnostic command?
I run curl -v against the exact URL Cypress printed, from the same runner or container. That separates connection, DNS, TLS, redirect, and HTTP response outcomes before I edit test code.
Q: How do you remove a CI startup race?
Start the app, wait for a bounded readiness condition, then start Cypress. I use an HTTP endpoint that represents the app dependency the tests require and retain the server logs when the gate times out.
Q: Why does localhost fail in Docker when the app works on the host?
A container has its own loopback interface. I use the sibling Compose service name or a platform-appropriate host route, then verify it from inside the Cypress container.
Q: Can defaultCommandTimeout fix this message?
No. That setting applies to commands after a spec runs. A baseUrl verification failure occurs before the first test command, so I fix startup, address resolution, or network reachability.
Q: How is Component Testing different?
Cypress normally launches the component dev server itself and assigns its URL. I inspect component.devServer and bundler output rather than assuming the E2E application server is missing.
Common Mistakes
- Increasing
defaultCommandTimeoutorpageLoadTimeoutwhen Cypress has not reached the test phase. - Running
curlon the laptop while Cypress runs in a remote CI worker or container. - Assuming
localhostmeans the same machine across Docker services. - Letting a dev server silently move ports while
baseUrlstays fixed. - Starting the server in the background and immediately launching Cypress without a readiness gate.
- Waiting for an HTTP root page when the first test depends on an API that is still starting.
- Copying Vite
--hostor--strictPortflags into a different server command. - Disabling certificate checks instead of repairing a staging trust or routing problem.
- Editing the E2E URL to solve a Component Testing dev-server failure.
- Treating an HTTP authorization response and a refused connection as the same failure.
- Hiding the effective URL behind multiple shell scripts and CI environment overrides.
Conclusion
To fix Cypress could not verify that this server is running, make the URL Cypress checks reachable from Cypress's own environment before the spec begins. Start the app, align baseUrl with the real listener, gate CI on readiness, and use the correct network hostname in containers. Then prove the repair with an independent HTTP request and one focused Cypress run.
Interview Questions and Answers
What does the baseUrl verification failure tell you about test execution?
It occurs before an E2E spec starts because Cypress cannot reach the configured application origin. I first verify the server independently, then compare the printed URL with effective configuration. DOM retries cannot fix this boundary.
Which observation separates a dead server from a wrong Cypress setting?
I request the exact URL from the Cypress environment and inspect the server terminal. If the server responds at another address, the listener and baseUrl disagree. If no address responds and the process exited, startup failed.
How would you design a reliable CI startup sequence?
I start the app, poll a relevant HTTP endpoint with a bounded timeout, and run Cypress only after it responds. I preserve server logs and fail the job if readiness never arrives. A fixed sleep cannot express that condition.
How does container networking change the baseUrl?
Localhost resolves inside each container. For sibling Compose services, I configure the Cypress container to call the app service hostname and internal port. I verify the route inside the Cypress container rather than from the Docker host.
What do you inspect if curl succeeds but Cypress still prints a different URL?
I check --config, CYPRESS_BASE_URL, wrapper scripts, and setupNodeEvents for overrides. The URL in Cypress output reflects the setting it is actually trying to verify, so it is stronger evidence than one config file alone.
When would you suspect IPv4 or IPv6 resolution?
I suspect it when localhost fails but an explicit loopback address succeeds, or vice versa. I compare curl -4 and curl -6 from the runner and inspect what address the server binds. Then I align the listener and configured URL.
Why is a 401 response different from connection refused?
A 401 proves that an HTTP service answered and rejected authentication. Connection refused means no listener accepted that address and port. The first needs access or gateway investigation; the second needs startup, binding, or routing investigation.
How do you triage this error in Component Testing?
I inspect component.devServer and the bundler startup output because Cypress creates that server for component specs. I run a single component spec and verify the generated URL. I avoid changing E2E baseUrl without evidence that the E2E app is involved.
Frequently Asked Questions
Why does Cypress say it could not verify that the server is running?
Cypress could not reach the configured E2E baseUrl during its pre-test check. The app may be stopped, listening on another address, still starting, or isolated by a container or CI network.
Does Cypress start my web application automatically?
An ordinary E2E run expects your app server to be started separately. Use an npm script, CI action start input, or another process supervisor to start it and wait for readiness before running specs.
How can I check the exact Cypress baseUrl?
Read the URL in the error output, then compare it with e2e.baseUrl and any --config or CYPRESS_BASE_URL override. Probe that exact URL from the Cypress runner with curl.
Why does Cypress fail only in Docker?
A container's localhost is its own loopback, not the host or a sibling service. Use a Compose service hostname for another service and make the app listen on an address reachable over the container network.
Will increasing pageLoadTimeout fix server verification?
Usually not. pageLoadTimeout governs navigation after the app is reachable; server verification happens before specs. Fix the startup or network route and add a readiness gate if the app is slow.
What should I do when this happens in GitHub Actions?
Start the app in the job and wait for the same URL configured as Cypress baseUrl. The official Cypress action supports start and wait-on inputs, including a bounded wait-on-timeout.
Is this the same problem in Cypress Component Testing?
The headline can look similar, but Component Testing normally uses a Cypress-managed dev server. Inspect component.devServer, its framework and bundler settings, and its startup output before changing E2E app scripts.
Related Guides
- How to Fix "Cypress failed to start" and cypress verify Errors
- How to Fix Appium "The instrumentation process is not running"
- How to Fix Playwright "Element is not attached to the DOM"
- How to Fix "Playwright Test did not expect test() to be called here"
- How to Fix Cypress "cy.visit() failed trying to load"
- How to Fix Playwright element is not visible