fix: Remove isolated-vm from Dockerfile npm rebuild (#26745)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Declan Carroll
2026-03-09 08:40:40 +00:00
committed by GitHub
parent 5fbcaeee05
commit 908a810e07
4 changed files with 83 additions and 2 deletions
+48
View File
@@ -0,0 +1,48 @@
name: 'Docker Build Smoke Test'
# Verifies the full Docker build chain works without any caching.
# Catches native module compilation failures (e.g., isolated-vm, sqlite3)
# that layer caching can mask in the regular E2E pipeline.
#
# Full chain: pnpm install → pnpm build (no Turbo cache) →
# build base image (no Docker cache) →
# build n8n + runners images (no Docker cache)
on:
pull_request:
paths:
- 'docker/images/n8n/**'
- 'docker/images/n8n-base/**'
- 'docker/images/runners/**'
- 'scripts/build-n8n.mjs'
- 'scripts/dockerize-n8n.mjs'
workflow_dispatch:
jobs:
docker-smoke-test:
name: 'Docker Build (no cache)'
runs-on: blacksmith-4vcpu-ubuntu-2204
if: ${{ !github.event.pull_request.head.repo.fork }}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Login to DHI Registry (for base image)
uses: ./.github/actions/docker-registry-login
with:
login-ghcr: 'false'
login-dhi: 'true'
dockerhub-username: ${{ secrets.DOCKER_USERNAME }}
dockerhub-password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build full chain (no cache)
uses: ./.github/actions/setup-nodejs
with:
build-command: 'pnpm build:docker:clean'
enable-docker-cache: true
- name: Verify n8n image starts
run: |
docker run --rm -d --name n8n-smoke-test n8nio/n8n:local
sleep 5
docker logs n8n-smoke-test 2>&1 | tail -20
docker stop n8n-smoke-test
+1 -1
View File
@@ -15,7 +15,7 @@ COPY ./compiled /usr/local/lib/node_modules/n8n
COPY docker/images/n8n/docker-entrypoint.sh /
RUN cd /usr/local/lib/node_modules/n8n && \
npm rebuild sqlite3 isolated-vm && \
npm rebuild sqlite3 && \
ln -s /usr/local/lib/node_modules/n8n/bin/n8n /usr/local/bin/n8n && \
mkdir -p /home/node/.n8n && \
chown -R node:node /home/node && \
+1
View File
@@ -16,6 +16,7 @@
"build:docker": "node scripts/build-n8n.mjs && node scripts/dockerize-n8n.mjs",
"build:docker:coverage": "BUILD_WITH_COVERAGE=true node scripts/build-n8n.mjs && node scripts/dockerize-n8n.mjs",
"build:docker:scan": "node scripts/build-n8n.mjs && node scripts/dockerize-n8n.mjs && node scripts/scan-n8n-image.mjs",
"build:docker:clean": "TURBO_FORCE=true node scripts/build-n8n.mjs && DOCKER_BUILD_NO_CACHE=true DOCKER_BUILD_BASE_IMAGE=true node scripts/dockerize-n8n.mjs",
"build:docker:test": "node scripts/build-n8n.mjs && node scripts/dockerize-n8n.mjs && turbo run test:container:standard --filter=n8n-playwright",
"typecheck": "turbo typecheck",
"dev": "turbo run dev --parallel --env-mode=loose --filter=!@n8n/design-system --filter=!@n8n/chat --filter=!@n8n/task-runner",
+33 -1
View File
@@ -119,7 +119,17 @@ const __dirname = path.dirname(__filename);
const isInScriptsDir = path.basename(__dirname) === 'scripts';
const rootDir = isInScriptsDir ? path.join(__dirname, '..') : __dirname;
const noCache = process.env.DOCKER_BUILD_NO_CACHE === 'true';
const withBaseImage = process.env.DOCKER_BUILD_BASE_IMAGE === 'true';
const nodeVersion = process.env.NODE_VERSION || '24.13.1';
const config = {
base: {
dockerfilePath: path.join(rootDir, 'docker/images/n8n-base/Dockerfile'),
get fullImageName() {
return `n8nio/base:${nodeVersion}`;
},
},
n8n: {
dockerfilePath: path.join(rootDir, 'docker/images/n8n/Dockerfile'),
imageBaseName: process.env.IMAGE_BASE_NAME || 'n8nio/n8n',
@@ -153,20 +163,35 @@ async function main() {
echo(`INFO: n8n Image: ${config.n8n.fullImageName}`);
echo(`INFO: Runners Image: ${config.runners.fullImageName}`);
echo(`INFO: Platform: ${platform}`);
if (noCache) echo(chalk.yellow('INFO: Docker layer cache disabled (DOCKER_BUILD_NO_CACHE=true)'));
if (withBaseImage) echo(chalk.yellow('INFO: Building base image first (DOCKER_BUILD_BASE_IMAGE=true)'));
echo(chalk.gray('-'.repeat(47)));
await checkPrerequisites();
if (withBaseImage) {
await buildDockerImage({
name: 'base',
dockerfilePath: config.base.dockerfilePath,
fullImageName: config.base.fullImageName,
buildArgs: [`NODE_VERSION=${nodeVersion}`],
});
}
const nodeVersionArgs = withBaseImage ? [`NODE_VERSION=${nodeVersion}`] : [];
const n8nBuildTime = await buildDockerImage({
name: 'n8n',
dockerfilePath: config.n8n.dockerfilePath,
fullImageName: config.n8n.fullImageName,
buildArgs: nodeVersionArgs,
});
const runnersBuildTime = await buildDockerImage({
name: 'runners',
dockerfilePath: config.runners.dockerfilePath,
fullImageName: config.runners.fullImageName,
buildArgs: nodeVersionArgs,
});
// Get image details
@@ -226,13 +251,18 @@ async function checkPrerequisites() {
}
}
async function buildDockerImage({ name, dockerfilePath, fullImageName }) {
async function buildDockerImage({ name, dockerfilePath, fullImageName, buildArgs = [] }) {
const startTime = Date.now();
const containerEngine = await getContainerEngine();
// Push directly if image name contains a registry (e.g., ghcr.io/...)
// This avoids the slow --load step (export/import tarball) when pushing to a registry
const shouldPush = fullImageName.includes('/') && fullImageName.split('/').length > 2;
const extraFlags = [
...buildArgs.flatMap((arg) => ['--build-arg', arg]),
...(noCache ? ['--no-cache'] : []),
];
echo(chalk.yellow(`INFO: Building ${name} Docker image using ${containerEngine}...`));
if (shouldPush) {
echo(chalk.yellow(`INFO: Registry detected - pushing directly to ${fullImageName}`));
@@ -243,6 +273,7 @@ async function buildDockerImage({ name, dockerfilePath, fullImageName }) {
const { stdout } = await $`podman build \
--platform ${platform} \
--build-arg TARGETPLATFORM=${platform} \
${extraFlags} \
-t ${fullImageName} \
-f ${dockerfilePath} \
${config.buildContext}`;
@@ -256,6 +287,7 @@ async function buildDockerImage({ name, dockerfilePath, fullImageName }) {
const { stdout } = await $`docker buildx build \
--platform ${platform} \
--build-arg TARGETPLATFORM=${platform} \
${extraFlags} \
-t ${fullImageName} \
-f ${dockerfilePath} \
--provenance=false \