beginner By Mathias Paulenko

Generate Temporary Files

How to create temporary files and directories safely with automatic cleanup across Python, Node.js, Java, and Bash.

Note: This guide follows English-language naming conventions and terminology standards common in international development teams. Examples use English identifiers and comments to maximize compatibility across codebases and tooling.

Overview

Temporary files are essential for caching intermediate data, storing uploads during processing, or holding secrets that should not persist on disk. Creating them incorrectly can lead to security vulnerabilities (predictable filenames), resource leaks (files never deleted), or cross-platform incompatibility.

When to Use

  • Staging uploaded files before validation and permanent storage
  • Holding decrypted data or secrets briefly during processing
  • Caching intermediate computation results within a single process lifetime
  • Running tests that need isolated filesystem state
  • Swapping data that does not fit in memory during batch processing

When NOT to Use

  • Long-term storage of user data — use permanent paths with proper backups
  • Data that must survive process restarts — temp directories may be wiped on reboot
  • Highly sensitive secrets on shared systems — use memory-only approaches or encrypted volumes
  • Files that multiple processes need to discover by name — temp names are randomized

Step-by-Step Implementation

Python

import tempfile
import os

# Temporary file (auto-deleted when closed)
with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=True) as f:
    f.write('{"key": "value"}')
    f.flush()
    print(f"Temp file: {f.name}")
    # File is automatically deleted when exiting the context

# Temporary directory (auto-deleted with cleanup=True)
with tempfile.TemporaryDirectory() as tmpdir:
    path = os.path.join(tmpdir, 'report.txt')
    with open(path, 'w') as f:
        f.write('Temporary report data')
    print(f"Temp dir: {tmpdir}")
    # Directory and all contents deleted on context exit

# Manual cleanup (use when passing path to external process)
tmp = tempfile.NamedTemporaryFile(mode='w', suffix='.csv', delete=False)
try:
    tmp.write('id,name\n1,Alice\n')
    tmp.close()
    # Pass tmp.name to external tool...
finally:
    os.unlink(tmp.name)

Node.js

import os from 'os';
import fs from 'fs';
import path from 'path';

// Using built-in fs promises with custom cleanup
async function withTempFile(data, suffix = '.tmp') {
    const tmpPath = path.join(os.tmpdir(), `tmp-${Date.now()}-${Math.random().toString(36).slice(2)}${suffix}`);
    try {
        await fs.promises.writeFile(tmpPath, data);
        return tmpPath;
    } catch (err) {
        await fs.promises.unlink(tmpPath).catch(() => {});
        throw err;
    }
}

// Using the tmp package (recommended for production)
import tmp from 'tmp';

// Auto-cleanup on process exit
const tmpObj = tmp.fileSync({ postfix: '.json' });
fs.writeFileSync(tmpObj.name, '{"key": "value"}');
// tmpObj.removeCallback() deletes the file

const tmpDir = tmp.dirSync({ unsafeCleanup: true });
// Recursively removes dir on cleanup

Java

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

// Java 7+ NIO.2 (recommended)
public class TempFileExample {
    public static void main(String[] args) throws IOException {
        // Create temp file in default temp directory
        Path tempFile = Files.createTempFile("prefix-", ".txt");
        System.out.println("Created: " + tempFile);
        Files.writeString(tempFile, "temporary data");

        // Create temp directory
        Path tempDir = Files.createTempDirectory("myapp-");
        Path nested = tempDir.resolve("nested.txt");
        Files.writeString(nested, "nested content");

        // Register for deletion on JVM exit (best effort)
        tempFile.toFile().deleteOnExit();
        tempDir.toFile().deleteOnExit();

        // Explicit cleanup
        Files.deleteIfExists(tempFile);
        Files.walk(tempDir)
            .sorted((a, b) -> -a.compareTo(b))
            .forEach(p -> {
                try { Files.deleteIfExists(p); }
                catch (IOException e) { /* ignore */ }
            });
    }
}

Bash

#!/bin/bash
set -euo pipefail

# Create temp file (portable, POSIX-compliant)
TMPFILE=$(mktemp "${TMPDIR:-/tmp}/XXXXXX.json")
trap 'rm -f "$TMPFILE"' EXIT

echo '{"status": "ok"}' > "$TMPFILE"
# Process file...
echo "Using: $TMPFILE"

# Create temp directory
TMPDIR_PATH=$(mktemp -d "${TMPDIR:-/tmp}/myapp.XXXXXX")
trap 'rm -rf "$TMPDIR_PATH"' EXIT

# Multiple temp resources — use a cleanup function
cleanup() {
    rm -f "$TMPFILE" 2>/dev/null || true
    rm -rf "$TMPDIR_PATH" 2>/dev/null || true
}
trap cleanup EXIT

# Advanced: generate unique temp path without creating file
UNIQUE_PATH="${TMPDIR:-/tmp}/batch_$(date +%s)_$$_$RANDOM.csv"

What Works

  • Always use mktemp or language-native temp APIs. Never build temp paths manually with predictable patterns like /tmp/myapp.pid — they are vulnerable to race conditions and symlink attacks.
  • Set trap in Bash or deleteOnExit in Java for cleanup guarantees, but prefer explicit cleanup in try-finally or try-with-resources.
  • Use descriptive prefixes and suffixes (mktemp prefix.XXXXXX.ext) to identify temp file purpose in logs and filesystem tools.
  • Avoid writing secrets to temp files when possible. If unavoidable, set restrictive permissions (chmod 600) immediately after creation.
  • Respect $TMPDIR environment variable for portability. Do not hardcode /tmp — macOS and some Linux distros use alternate paths.

Common Mistakes

  • Hardcoding /tmp with predictable names. An attacker can create a symlink at the expected path to overwrite arbitrary files.
  • Relying solely on deleteOnExit in long-running processes. Files accumulate until JVM or process exits.
  • Forgetting cleanup in error paths. An exception before cleanup leaves orphaned temp files that fill the disk over time.
  • Using Date.now() as the only randomizer in Node.js. Millisecond collisions are possible under load — combine with crypto-random bytes.
  • Creating temp files in the working directory. Pollutes the project and may be committed accidentally.

Additional Best Practices

  1. Create temp files in the same directory as the target for atomic writes. os.rename() is atomic only within the same filesystem. If the temp file is on a different mount point, the rename becomes a copy, which is not atomic:
import tempfile
import os

# Good: temp file in same dir as target
target = "/var/app/config.json"
fd, tmp = tempfile.mkstemp(dir=os.path.dirname(target), prefix=".config.", suffix=".tmp")
os.close(fd)
# ... write data ...
os.rename(tmp, target)  # Atomic on same filesystem

# Bad: temp file in /tmp, target on /var (different filesystem)
# fd, tmp = tempfile.mkstemp(suffix=".tmp")  # /tmp may be a different mount
# os.rename(tmp, "/var/app/config.json")     # Not atomic if cross-filesystem
  1. Use fsync after writing critical data. The OS may buffer writes. If the process crashes, buffered data is lost. Call fsync to force data to disk before renaming:
import os

fd, tmp_path = tempfile.mkstemp(suffix=".dat")
with os.fdopen(fd, "wb") as f:
    f.write(b"critical data")
    f.flush()
    os.fsync(f.fileno())  # Force to disk
os.rename(tmp_path, "important.dat")
  1. Clean up temp files on signal interruption. In Bash, trap on EXIT does not fire on SIGKILL, but it does on SIGINT and SIGTERM:
#!/bin/bash
set -euo pipefail

TMPDIR_PATH=$(mktemp -d)

cleanup() {
    rm -rf "$TMPDIR_PATH"
    exit 0
}

trap cleanup EXIT INT TERM

# Long-running process
for i in $(seq 1 100); do
    echo "Processing $i..." > "$TMPDIR_PATH/log.txt"
    sleep 1
done

Additional Common Mistakes

  1. Using tempfile.mktemp() (deprecated). It generates a filename without creating the file, creating a race condition. Use mkstemp() instead:
import tempfile
import os

# Bad: mktemp() is deprecated, race condition vulnerable
# path = tempfile.mktemp(suffix=".txt")  # Don't use this

# Good: mkstemp() creates the file atomically
fd, path = tempfile.mkstemp(suffix=".txt")
os.close(fd)
print(f"Safe temp file: {path}")
os.unlink(path)
  1. Not handling temp directory cleanup in recursive structures. If you create nested temp directories, a simple os.rmdir() fails because it only removes empty directories. Use shutil.rmtree():
import tempfile
import shutil
import os

tmpdir = tempfile.mkdtemp()
try:
    nested = os.path.join(tmpdir, "a", "b", "c")
    os.makedirs(nested)
    with open(os.path.join(nested, "file.txt"), "w") as f:
        f.write("data")

    # Bad: os.rmdir fails because dir is not empty
    # os.rmdir(tmpdir)  # OSError: Directory not empty

    # Good: shutil.rmtree removes recursively
    shutil.rmtree(tmpdir)
finally:
    if os.path.exists(tmpdir):
        shutil.rmtree(tmpdir, ignore_errors=True)
  1. Leaking temp file descriptors. On Windows, files with open descriptors cannot be deleted. Always close before unlinking:
import tempfile
import os

# Bad: file still open, unlink fails on Windows
# f = tempfile.NamedTemporaryFile(delete=False)
# f.write("data")
# os.unlink(f.name)  # May fail on Windows

# Good: close first, then unlink
f = tempfile.NamedTemporaryFile(delete=False, suffix=".txt")
try:
    f.write("data".encode())
    f.close()  # Close before unlink
    os.unlink(f.name)
except Exception:
    f.close()
    if os.path.exists(f.name):
        os.unlink(f.name)
    raise

Frequently Asked Questions

Why is mktemp safer than creating files in /tmp manually?
mktemp generates unique filenames with restrictive permissions, preventing race conditions and predictable paths that attackers could exploit.
What happens to temporary files after the script exits?
They remain unless you delete them. Use a trap to clean up on exit, or store files in a directory created with mktemp -d and remove the whole directory.
Can I use temporary files in a CI pipeline?
Yes, but ensure the runner has enough disk space and that sensitive data is never left in artifacts or shared caches.