diff --git a/packages/core/src/instance-settings/__tests__/instance-settings.test.ts b/packages/core/src/instance-settings/__tests__/instance-settings.test.ts index 6c7418001ce..d52358161f0 100644 --- a/packages/core/src/instance-settings/__tests__/instance-settings.test.ts +++ b/packages/core/src/instance-settings/__tests__/instance-settings.test.ts @@ -9,7 +9,6 @@ import { WorkerMissingEncryptionKey } from '../worker-missing-encryption-key.err describe('InstanceSettings', () => { const userFolder = '/test'; - const settingsFile = `${userFolder}/.n8n/config`; const mockFs = mock(fs); const logger = mock(); @@ -202,25 +201,16 @@ describe('InstanceSettings', () => { const settings = createInstanceSettings(); - const [instanceType, nanoid] = settings.hostId.split('-'); + const [instanceType, hostId] = settings.hostId.split('-'); expect(instanceType).toEqual('main'); - expect(nanoid).toHaveLength(16); // e.g. sDX6ZPc0bozv66zM + expect(hostId.length).toBeGreaterThan(0); // hostname or nanoID }); }); describe('isDocker', () => { - let settings: InstanceSettings; - - beforeEach(() => { - mockFs.existsSync.calledWith(settingsFile).mockReturnValue(true); - mockFs.readFileSync - .calledWith(settingsFile) - .mockReturnValue(JSON.stringify({ encryptionKey: 'test_key' })); - settings = createInstanceSettings(); - }); - it('should return true if /.dockerenv exists', () => { mockFs.existsSync.mockImplementation((path) => path === '/.dockerenv'); + const settings = createInstanceSettings(); expect(settings.isDocker).toBe(true); expect(mockFs.existsSync).toHaveBeenCalledWith('/.dockerenv'); expect(mockFs.readFileSync).not.toHaveBeenCalledWith('/proc/self/cgroup', 'utf8'); @@ -228,6 +218,7 @@ describe('InstanceSettings', () => { it('should return true if /run/.containerenv exists', () => { mockFs.existsSync.mockImplementation((path) => path === '/run/.containerenv'); + const settings = createInstanceSettings(); expect(settings.isDocker).toBe(true); expect(mockFs.existsSync).toHaveBeenCalledWith('/run/.containerenv'); expect(mockFs.readFileSync).not.toHaveBeenCalledWith('/proc/self/cgroup', 'utf8'); @@ -239,6 +230,7 @@ describe('InstanceSettings', () => { mockFs.existsSync.mockReturnValueOnce(false); mockFs.readFileSync.calledWith('/proc/self/cgroup', 'utf8').mockReturnValueOnce(str); + const settings = createInstanceSettings(); expect(settings.isDocker).toBe(true); expect(mockFs.existsSync).toHaveBeenCalledWith('/.dockerenv'); expect(mockFs.readFileSync).toHaveBeenCalledWith('/proc/self/cgroup', 'utf8'); @@ -252,6 +244,7 @@ describe('InstanceSettings', () => { mockFs.readFileSync.calledWith('/proc/self/cgroup', 'utf8').mockReturnValueOnce(''); mockFs.readFileSync.calledWith('/proc/self/mountinfo', 'utf8').mockReturnValueOnce(str); + const settings = createInstanceSettings(); expect(settings.isDocker).toBe(true); expect(mockFs.existsSync).toHaveBeenCalledWith('/.dockerenv'); expect(mockFs.readFileSync).toHaveBeenCalledWith('/proc/self/cgroup', 'utf8'); @@ -263,6 +256,7 @@ describe('InstanceSettings', () => { mockFs.existsSync.calledWith('/.dockerenv').mockReturnValueOnce(false); mockFs.readFileSync.calledWith('/proc/self/cgroup', 'utf8').mockReturnValueOnce(''); mockFs.readFileSync.calledWith('/proc/self/mountinfo', 'utf8').mockReturnValueOnce(''); + const settings = createInstanceSettings(); expect(settings.isDocker).toBe(false); }); @@ -272,12 +266,14 @@ describe('InstanceSettings', () => { throw new Error('File not found'); }); + const settings = createInstanceSettings(); expect(settings.isDocker).toBe(false); }); it('should cache the result of isDocker check', () => { mockFs.existsSync.calledWith('/.dockerenv').mockReturnValueOnce(true); + const settings = createInstanceSettings(); expect(settings.isDocker).toBe(true); mockFs.existsSync.mockClear(); diff --git a/packages/core/src/instance-settings/instance-settings.ts b/packages/core/src/instance-settings/instance-settings.ts index 2a69d9b65b3..df453cba210 100644 --- a/packages/core/src/instance-settings/instance-settings.ts +++ b/packages/core/src/instance-settings/instance-settings.ts @@ -7,6 +7,7 @@ import { createHash, randomBytes } from 'crypto'; import { ApplicationError, jsonParse, ALPHABET, toResult } from 'n8n-workflow'; import { customAlphabet } from 'nanoid'; import { chmodSync, existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from 'node:fs'; +import os from 'node:os'; import path from 'path'; import { WorkerMissingEncryptionKey } from './worker-missing-encryption-key.error'; @@ -60,7 +61,7 @@ export class InstanceSettings { const command = process.argv[2] as InstanceType; this.instanceType = ['webhook', 'worker'].includes(command) ? command : 'main'; - this.hostId = `${this.instanceType}-${nanoid()}`; + this.hostId = `${this.instanceType}-${this.isDocker ? os.hostname() : nanoid()}`; this.settings = this.loadOrCreate(); this.instanceId = this.generateInstanceId(); } @@ -76,12 +77,11 @@ export class InstanceSettings { instanceRole: InstanceRole = 'unset'; /** - * Transient ID of this n8n instance, for scaling mode. - * Reset on restart. Do not confuse with `instanceId`. + * ID of this n8n instance. Hostname-based when in Docker, or nanoID-based + * otherwise (resets on restart). Do not confuse with `instanceId`. * - * @example 'main-bnxa1riryKUNHtln' - * @example 'worker-nDJR0FnSd2Vf6DB5' - * @example 'webhook-jxQ7AO8IzxEtfW1F' + * @example 'main-bnxa1riryKUNHtln' (local) + * @example 'main-6bf523178bc6' (Docker) */ readonly hostId: string;