How to Fix: Static worker unexpectedly exited with code: null and signal: SIGTERM while building in Jenkins

7 min read

How to Fix “Static worker unexpectedly exited with code: null and signal: SIGTERM” During Next.js Builds in Jenkins

This error usually means your Next.js build worker did not crash because of bad application code first—it was terminated from the outside while generating static pages. In Jenkins, that commonly points to memory pressure, container limits, process timeouts, or CI-specific environment constraints that kill the worker before next build finishes.

The issue was reported in the Next.js tracker and is reproducible for some teams specifically in Jenkins rather than on local machines. You can review the original report on GitHub.

Understanding the Root Cause

The key clue is this part of the error: signal: SIGTERM. A process that exits with code: null and a termination signal was usually not allowed to fail naturally. It was told to stop by the operating system, the container runtime, Jenkins, or a parent process.

During next build, Next.js may spawn static generation workers to pre-render pages. In Jenkins, those workers can be terminated when one of the following happens:

  • Out-of-memory conditions: the JVM for Jenkins, Node.js, and other parallel jobs compete for RAM. When memory gets tight, the environment may terminate worker processes.
  • Docker or Kubernetes resource limits: if Jenkins agents run inside containers, the container may hit memory or CPU limits before the host itself does.
  • Jenkins pipeline timeouts or agent shutdowns: a pipeline step, shell wrapper, or ephemeral agent may stop child processes with SIGTERM.
  • Too many pages being statically generated at once: large builds with expensive getStaticProps, image processing, MDX transforms, or API fetches can amplify memory use.
  • Node.js heap configuration mismatch: Node may have a lower effective heap ceiling in CI than expected, especially across Node versions or containerized agents.

In short, this is rarely just a random Next.js bug. It is usually a build environment stability issue exposed by how Next.js parallelizes static work.

Step-by-Step Solution

The most reliable fix is to reduce build pressure and make Jenkins resource limits explicit.

1) Confirm the process is being killed by the environment

Start by collecting more detail in Jenkins. Print Node version, memory settings, and cgroup/container limits if applicable.

node -v
npm -v
printenv | sort
cat /proc/meminfo || true
cat /sys/fs/cgroup/memory.max || true
cat /sys/fs/cgroup/memory/memory.limit_in_bytes || true

If the build dies without a JavaScript stack trace and consistently shows SIGTERM, that strongly suggests an external termination.

2) Increase Node memory for the build

Set NODE_OPTIONS so the build has a larger heap. This is one of the most effective mitigations in Jenkins.

export NODE_OPTIONS="--max-old-space-size=4096"
npm ci
npm run build

In a Jenkins Pipeline:

pipeline {
  agent any
  environment {
    NODE_OPTIONS = '--max-old-space-size=4096'
  }
  stages {
    stage('Build') {
      steps {
        sh 'node -v'
        sh 'npm ci'
        sh 'npm run build'
      }
    }
  }
}

If your Jenkins agent has less than 4 GB available, use a smaller value like 2048. The correct value must fit the actual agent or container limit.

3) Check Jenkins agent and container memory limits

If Jenkins runs builds inside Docker, Kubernetes, or ephemeral cloud agents, verify the resource ceiling. Increasing NODE_OPTIONS alone will not help if the container itself is capped too low.

docker inspect <container-id> | grep -i memory

For Kubernetes-based Jenkins agents, review the pod template resources and increase both requests and limits if the current values are too low.

resources:
  requests:
    memory: "2Gi"
    cpu: "1000m"
  limits:
    memory: "4Gi"
    cpu: "2000m"

If your build includes many static pages, 2 GiB is often not enough.

4) Reduce build-time work in getStaticProps and page generation

If you are doing heavy data fetching, large JSON transforms, markdown compilation, syntax highlighting, image processing, or loading huge datasets during static generation, optimize that logic.

Typical improvements:

  • Fetch only required fields.
  • Cache expensive remote API responses.
  • Precompute content outside the build when possible.
  • Avoid loading entire datasets in memory at once.
  • Split very large page-generation jobs into smaller inputs.

Example optimization pattern:

export async function getStaticProps() {
  const res = await fetch(process.env.API_URL + '/posts?fields=id,title,slug')
  const posts = await res.json()

  return {
    props: {
      posts
    },
    revalidate: 60
  }
}

If your static build is generating thousands of pages, consider whether some routes should use Incremental Static Regeneration instead of fully rendering everything during CI.

5) Avoid unnecessary parallel workload in the Jenkins job

On shared agents, a Next.js build may compete with test runners, Docker builds, browsers, or other pipelines. If possible:

  • Run next build in its own stage.
  • Disable unnecessary parallel stages on the same node.
  • Use a dedicated build agent for large frontend builds.

Example pipeline separation:

pipeline {
  agent any
  environment {
    NODE_OPTIONS = '--max-old-space-size=4096'
  }
  stages {
    stage('Install') {
      steps {
        sh 'npm ci'
      }
    }
    stage('Build Next.js') {
      steps {
        sh 'npm run build'
      }
    }
    stage('Test') {
      steps {
        sh 'npm test'
      }
    }
  }
}

6) Disable CI wrappers that may terminate child processes early

Some Jenkins shell wrappers, timeout blocks, or agent cleanup behaviors can send SIGTERM to child processes. Review pipeline configuration for:

  • timeout(...) steps that are too aggressive
  • agent shutdown or workspace cleanup during long builds
  • custom wrappers that kill child processes after log inactivity

If a timeout is the cause, increase it:

timeout(time: 30, unit: 'MINUTES') {
  sh 'npm run build'
}

7) Align Node.js and Next.js versions between local and Jenkins

If the issue only happens in Jenkins, compare versions first. Differences in Node.js, package manager behavior, native dependencies, or libc can change memory usage and worker behavior.

node -v
npm ls next
npm ls react
npm ls react-dom

Use the same Node version locally and in CI, ideally through .nvmrc, Volta, or a pinned Docker image.

{
  "engines": {
    "node": ">=18.17.0"
  }
}

8) Test the build with resource diagnostics enabled

To validate the fix, rerun the build while logging memory usage before and after the Next.js build step.

free -h || true
node -e "console.log(process.memoryUsage())"
npm run build
node -e "console.log(process.memoryUsage())"
free -h || true

If the build becomes stable after increasing memory or reducing static generation work, you have confirmed the root cause.

9) Practical Jenkinsfile example

This example combines the most common fixes into one reproducible setup:

pipeline {
  agent any

  environment {
    NODE_OPTIONS = '--max-old-space-size=4096'
    CI = 'true'
  }

  options {
    timeout(time: 30, unit: 'MINUTES')
  }

  stages {
    stage('Environment') {
      steps {
        sh 'node -v'
        sh 'npm -v'
        sh 'free -h || true'
        sh 'cat /proc/meminfo | head || true'
      }
    }

    stage('Install') {
      steps {
        sh 'npm ci'
      }
    }

    stage('Build') {
      steps {
        sh 'npm run build'
      }
    }
  }
}

Common Edge Cases

Large dynamic route generation

If getStaticPaths returns a huge number of routes, memory and build time can spike. Move infrequently visited pages to fallback strategies or ISR where appropriate.

Remote APIs hanging in CI

Sometimes the worker is terminated because static generation blocks too long on a slow internal API only reachable from Jenkins. Add request timeouts, retries, and better logging around fetch calls.

const controller = new AbortController()
const timeout = setTimeout(() => controller.abort(), 10000)

const res = await fetch(url, { signal: controller.signal })
clearTimeout(timeout)

Native dependency differences

Packages used for image manipulation, MDX, canvas, or syntax parsing may behave differently on Jenkins Linux images than on local macOS or Windows machines. Rebuild native modules and ensure system libraries are present.

Monorepo memory amplification

In a monorepo, workspace tooling can increase memory usage before Next.js even starts building. If Turbo, Nx, or custom scripts run in the same step, isolate the frontend build.

Build log truncation hides the real cause

Jenkins sometimes makes it look like Next.js failed first, when the real issue was an earlier warning, OOM event, or agent interruption. Review full console output and host/container logs when possible.

FAQ

Why does this happen only in Jenkins and not locally?

Because Jenkins often runs inside a more constrained environment: smaller memory limits, shared CPU, timeouts, containers, or ephemeral agents. Your local machine may simply have more available resources and fewer process restrictions.

Does SIGTERM mean Next.js itself is broken?

Usually no. SIGTERM means the worker was asked to stop by something external. Next.js is the visible failing process, but the underlying cause is typically CI resource management, build workload size, or environment configuration.

What is the fastest fix to try first?

Start with three changes: set NODE_OPTIONS=–max-old-space-size=4096, increase Jenkins agent or container memory, and reduce expensive static generation logic. Those fix the majority of real-world cases.

Final Recommendation

If you see “Static worker unexpectedly exited with code: null and signal: SIGTERM” during Next.js builds in Jenkins, treat it as a CI process termination problem first, not just an application bug. Increase available memory, verify container limits, relax timeout settings, and reduce static build workload. Once Jenkins stops killing the worker, the build usually becomes stable without code changes to Next.js itself.

Leave a Reply

Your email address will not be published. Required fields are marked *