Skip to content

Commit 308a6f6

Browse files
authored
feat(core): Use hostname in host ID for Docker (#16544)
1 parent a46fa60 commit 308a6f6

File tree

2 files changed

+15
-19
lines changed

2 files changed

+15
-19
lines changed

packages/core/src/instance-settings/__tests__/instance-settings.test.ts

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import { WorkerMissingEncryptionKey } from '../worker-missing-encryption-key.err
99

1010
describe('InstanceSettings', () => {
1111
const userFolder = '/test';
12-
const settingsFile = `${userFolder}/.n8n/config`;
1312

1413
const mockFs = mock(fs);
1514
const logger = mock<Logger>();
@@ -202,32 +201,24 @@ describe('InstanceSettings', () => {
202201

203202
const settings = createInstanceSettings();
204203

205-
const [instanceType, nanoid] = settings.hostId.split('-');
204+
const [instanceType, hostId] = settings.hostId.split('-');
206205
expect(instanceType).toEqual('main');
207-
expect(nanoid).toHaveLength(16); // e.g. sDX6ZPc0bozv66zM
206+
expect(hostId.length).toBeGreaterThan(0); // hostname or nanoID
208207
});
209208
});
210209

211210
describe('isDocker', () => {
212-
let settings: InstanceSettings;
213-
214-
beforeEach(() => {
215-
mockFs.existsSync.calledWith(settingsFile).mockReturnValue(true);
216-
mockFs.readFileSync
217-
.calledWith(settingsFile)
218-
.mockReturnValue(JSON.stringify({ encryptionKey: 'test_key' }));
219-
settings = createInstanceSettings();
220-
});
221-
222211
it('should return true if /.dockerenv exists', () => {
223212
mockFs.existsSync.mockImplementation((path) => path === '/.dockerenv');
213+
const settings = createInstanceSettings();
224214
expect(settings.isDocker).toBe(true);
225215
expect(mockFs.existsSync).toHaveBeenCalledWith('/.dockerenv');
226216
expect(mockFs.readFileSync).not.toHaveBeenCalledWith('/proc/self/cgroup', 'utf8');
227217
});
228218

229219
it('should return true if /run/.containerenv exists', () => {
230220
mockFs.existsSync.mockImplementation((path) => path === '/run/.containerenv');
221+
const settings = createInstanceSettings();
231222
expect(settings.isDocker).toBe(true);
232223
expect(mockFs.existsSync).toHaveBeenCalledWith('/run/.containerenv');
233224
expect(mockFs.readFileSync).not.toHaveBeenCalledWith('/proc/self/cgroup', 'utf8');
@@ -239,6 +230,7 @@ describe('InstanceSettings', () => {
239230
mockFs.existsSync.mockReturnValueOnce(false);
240231
mockFs.readFileSync.calledWith('/proc/self/cgroup', 'utf8').mockReturnValueOnce(str);
241232

233+
const settings = createInstanceSettings();
242234
expect(settings.isDocker).toBe(true);
243235
expect(mockFs.existsSync).toHaveBeenCalledWith('/.dockerenv');
244236
expect(mockFs.readFileSync).toHaveBeenCalledWith('/proc/self/cgroup', 'utf8');
@@ -252,6 +244,7 @@ describe('InstanceSettings', () => {
252244
mockFs.readFileSync.calledWith('/proc/self/cgroup', 'utf8').mockReturnValueOnce('');
253245
mockFs.readFileSync.calledWith('/proc/self/mountinfo', 'utf8').mockReturnValueOnce(str);
254246

247+
const settings = createInstanceSettings();
255248
expect(settings.isDocker).toBe(true);
256249
expect(mockFs.existsSync).toHaveBeenCalledWith('/.dockerenv');
257250
expect(mockFs.readFileSync).toHaveBeenCalledWith('/proc/self/cgroup', 'utf8');
@@ -263,6 +256,7 @@ describe('InstanceSettings', () => {
263256
mockFs.existsSync.calledWith('/.dockerenv').mockReturnValueOnce(false);
264257
mockFs.readFileSync.calledWith('/proc/self/cgroup', 'utf8').mockReturnValueOnce('');
265258
mockFs.readFileSync.calledWith('/proc/self/mountinfo', 'utf8').mockReturnValueOnce('');
259+
const settings = createInstanceSettings();
266260
expect(settings.isDocker).toBe(false);
267261
});
268262

@@ -272,12 +266,14 @@ describe('InstanceSettings', () => {
272266
throw new Error('File not found');
273267
});
274268

269+
const settings = createInstanceSettings();
275270
expect(settings.isDocker).toBe(false);
276271
});
277272

278273
it('should cache the result of isDocker check', () => {
279274
mockFs.existsSync.calledWith('/.dockerenv').mockReturnValueOnce(true);
280275

276+
const settings = createInstanceSettings();
281277
expect(settings.isDocker).toBe(true);
282278

283279
mockFs.existsSync.mockClear();

packages/core/src/instance-settings/instance-settings.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { createHash, randomBytes } from 'crypto';
77
import { ApplicationError, jsonParse, ALPHABET, toResult } from 'n8n-workflow';
88
import { customAlphabet } from 'nanoid';
99
import { chmodSync, existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
10+
import os from 'node:os';
1011
import path from 'path';
1112

1213
import { WorkerMissingEncryptionKey } from './worker-missing-encryption-key.error';
@@ -60,7 +61,7 @@ export class InstanceSettings {
6061
const command = process.argv[2] as InstanceType;
6162
this.instanceType = ['webhook', 'worker'].includes(command) ? command : 'main';
6263

63-
this.hostId = `${this.instanceType}-${nanoid()}`;
64+
this.hostId = `${this.instanceType}-${this.isDocker ? os.hostname() : nanoid()}`;
6465
this.settings = this.loadOrCreate();
6566
this.instanceId = this.generateInstanceId();
6667
}
@@ -76,12 +77,11 @@ export class InstanceSettings {
7677
instanceRole: InstanceRole = 'unset';
7778

7879
/**
79-
* Transient ID of this n8n instance, for scaling mode.
80-
* Reset on restart. Do not confuse with `instanceId`.
80+
* ID of this n8n instance. Hostname-based when in Docker, or nanoID-based
81+
* otherwise (resets on restart). Do not confuse with `instanceId`.
8182
*
82-
* @example 'main-bnxa1riryKUNHtln'
83-
* @example 'worker-nDJR0FnSd2Vf6DB5'
84-
* @example 'webhook-jxQ7AO8IzxEtfW1F'
83+
* @example 'main-bnxa1riryKUNHtln' (local)
84+
* @example 'main-6bf523178bc6' (Docker)
8585
*/
8686
readonly hostId: string;
8787

0 commit comments

Comments
 (0)