os.tmpdir()

Returns the operating system's default directory for temporary files as a string.

Signature
os.tmpdir()
  • Added in Node.js v0.9.9

Return value

stringThe operating system's default directory for temporary files, as a string with no trailing separator.

Changelog

  • PHP v2.0.0The function is now cross-platform consistent and no longer returns a path with a trailing slash on any platform.

os.tmpdir() returns the platform's temporary directory: /tmp on most POSIX systems unless TMPDIR, TMP or TEMP override it (checked in that order), and on Windows TEMP then TMP, falling back to %SystemRoot%\temp. On macOS it is a long per-user path under /var/folders, not /tmp.

When to reach for it

As the base for scratch space — an intermediate file in a conversion pipeline, an atomic write staged before a rename, a download unpacked before it is moved into place. In practice you almost never use the path directly: pass it to fs.mkdtemp(path.join(os.tmpdir(), 'myapp-')), which creates a uniquely named private directory for you.

What trips people up

Writing a fixed filename into it. Every user and process on the machine shares this directory, so path.join(os.tmpdir(), 'output.json') is both a collision waiting to happen and, on a multi-user POSIX box, a symlink-attack surface. Always use mkdtemp.

Nothing cleans up for you — files persist until the OS decides otherwise, which on many systems means until reboot. Delete what you create in a finally block. And since v2.0.0 the returned path has no trailing separator, so joining is required.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Create a private scratch directory with fs.mkdtemp() for an unpack step
  • Stage a file write before atomically renaming it into its final location
  • Hold an intermediate artifact during a multi-step transform pipeline

Frequently asked questions

Why does os.tmpdir() return a long /var/folders path on macOS?

Because macOS gives each user a private per-session temporary directory under /var/folders and exports it as TMPDIR, rather than sharing /tmp. That is the correct location to use; hard-coding /tmp on macOS bypasses the isolation and the system's own cleanup.

How do I create a temporary file in Node.js?

Call fs.mkdtemp(path.join(os.tmpdir(), 'prefix-')) to get a unique private directory, then write your file inside it. Never join a fixed filename onto os.tmpdir() — the directory is shared by every process on the machine, so fixed names collide and invite symlink attacks.

Does Node.js delete files in os.tmpdir() automatically?

No. Nothing in Node cleans up after you, and most systems only clear the directory on reboot or on a schedule. Remove what you create in a finally block, and remember process exit handlers do not reliably run on SIGKILL.

More examples

Create and clean up a private scratch directory

mkdtemp appends six random characters and creates the directory with 0700 permissions, so two concurrent runs never collide and other users cannot read the contents. The finally block is not optional — nothing on the system removes these directories for you, and the last line confirms it is gone. This snippet needs a writable temp directory, which the sandbox does not provide.

Example · javascript
import os from 'node:os';
import path from 'node:path';
import fs from 'node:fs/promises';

const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'demo-'));
try {
  const file = path.join(dir, 'work.json');
  await fs.writeFile(file, JSON.stringify({ step: 1 }));
  const back = JSON.parse(await fs.readFile(file, 'utf8'));

  console.log('created under tmpdir:', dir.startsWith(os.tmpdir()));
  console.log('unique suffix present:', path.basename(dir) !== 'demo-');
  console.log('round-tripped:', back.step);
} finally {
  await fs.rm(dir, { recursive: true, force: true });
}

console.log('cleaned up:', !(await fs.stat(dir).catch(() => null)));

Atomic write via a temp file and rename

This is the counter-example to reaching for os.tmpdir() reflexively. An atomic replace depends on rename() being atomic, which only holds within one filesystem — staging in /tmp and renaming onto another mount throws EXDEV, so the staging file belongs beside the target.

Example · javascript
import os from 'node:os';
import path from 'node:path';
import fs from 'node:fs/promises';

async function writeAtomic(target, contents) {
  // Stage in the SAME directory as the target: rename across filesystems fails
  // with EXDEV, and os.tmpdir() is often on a different mount.
  const dir = path.dirname(path.resolve(target));
  const staging = path.join(dir, `.${path.basename(target)}.${process.pid}.tmp`);
  await fs.writeFile(staging, contents);
  await fs.rename(staging, target);
}

await writeAtomic('./settings.json', JSON.stringify({ theme: 'dark' }));
console.log('written:', await fs.readFile('./settings.json', 'utf8'));
console.log('tmpdir is elsewhere:', !path.resolve('.').startsWith(os.tmpdir()));
await fs.rm('./settings.json');

Inspect the environment overrides

The output shows which variable, if any, is driving the result. This is how a CI job redirects all temporary output into a workspace it can clean between builds, and how a surprising tmpdir on macOS turns out to be a per-user /var/folders path rather than /tmp.

Example · javascript
const os = require('node:os');

const order = os.platform() === 'win32' ? ['TEMP', 'TMP'] : ['TMPDIR', 'TMP', 'TEMP'];

console.log('platform:', os.platform());
console.log('checked in order:', order.join(' > '));
for (const name of order) {
  console.log(`${name}:`, process.env[name] ?? '(unset)');
}
console.log('os.tmpdir():', os.tmpdir());
console.log('came from env:', order.some((n) => process.env[n] === os.tmpdir()));

Discussion

  • Be the first to comment on this lesson.