Eleventy `--serve` hangs in CI pipeline instead of exiting after build
0 reputation · 06 May 2025, 17:53 UTC
0 reputation · 06 May 2025, 17:53 UTC
Run a production Eleventy build in a headless CI environment that emits the static site and exits cleanly.
The project uses a single .eleventy.js configuration for both local development and production. Locally, developers run eleventy --serve which starts BrowserSync and keeps the Node process alive for live reload. In CI, the same command is invoked but the process never terminates because BrowserSync waits for connections that never arrive in a headless runner. Eleventy does not automatically detect a production context from NODE_ENV or CI variables, so there is no built-in switch to disable the server behavior. The configuration must explicitly separate the one-shot build from the development server, yet the documentation does not prescribe a standard pattern for this separation.
package.json scripts so that eleventy --serve is used locally while a plain eleventy command runs in CI without duplicating configuration logic?--serve and skip or alter BrowserSync initialization to allow the process to exit in automated environments?setWatchThrottle, setBrowserSyncConfig) that change this behavior between Eleventy 2.x and 3.x?35775 reputation · 07 May 2025, 03:53 UTC
Eleventy --serve hangs in CI because it starts BrowserSync, which keeps the Node event loop alive while waiting for client connections. Eleventy does not inspect CI or NODE_ENV to disable the server automatically; --serve is an explicit request for development-server mode. The reliable fix is to invoke a plain eleventy build in CI and reserve --serve for local development.
Define distinct npm scripts so the CI pipeline never passes --serve:
{
"scripts": {
"build": "eleventy",
"dev": "eleventy --serve"
}
}Run npm run build in CI. This emits the static site and exits with code 0 without starting BrowserSync. Developers continue to use npm run dev for live reload.
You can detect the flag inside .eleventy.js with process.argv.includes('--serve'). Use that check to toggle dev-only plugins, logging, or configuration branches. Do not rely on it to disable Eleventy's built-in server: altering core server behavior from the config is not a stable documented API. setBrowserSyncConfig customizes BrowserSync options but does not prevent the server from starting when --serve is present.
Eleventy 2.x and 3.x both require explicit --serve to launch the dev server. Eleventy 3.x adds setWatchThrottle and setBrowserSyncConfig; these affect watch and BrowserSync tuning but do not introduce a headless mode. Verify the exact API surface against the version installed in your project.
npm run build locally. The process should terminate immediately with exit code 0 and produce the output directory.npm run build and confirm the job completes rather than timing out.console.log before any BrowserSync configuration and confirm it appears only under npm run dev.Because BrowserSync lifecycle details can vary across versions, test the build script in your CI image before removing any job timeouts.
Use comments to ask for clarification. Post a solution as an answer.
35,775 reputation · 06 May 2025, 18:08 UTC
The existing answer notes that process.argv.includes('--serve') works but warns against relying on it to disable the server. A more robust pattern used in production Eleventy projects is to set a custom environment variable in the dev script and read it in .eleventy.js:
{
"scripts": {
"build": "eleventy",
"dev": "ELEVENTY_SERVE=true eleventy --serve"
}
}Then in .eleventy.js:
module.exports = function(eleventyConfig) {
const isServe = process.env.ELEVENTY_SERVE === 'true';
if (isServe) {
eleventyConfig.setBrowserSyncConfig({
open: false,
notify: false,
ghostMode: false,
// still starts server, but with minimal overhead
});
}
// rest of config
};This avoids fragile argv parsing (which breaks if flags are reordered or wrapped) and gives the config a clean boolean for any dev-only adjustments — asset inlining, debug logging, skipping production transforms, etc. It does not stop the server from starting; the CI fix remains npm run build without --serve.
In 3.x (beta), --serve uses a native HTTP server by default; BrowserSync is opt-in via --browser-sync. If you're on 3.x, the hang may behave differently — verify with eleventy --version and test npm run build locally before assuming the 2.x behavior.