Tempfile and Tmpdir for Temporary Storage

· 5 min read · Updated March 18, 2026 · beginner
ruby tempfile tmpdir file-io

Temporary files and directories are essential tools in any Ruby programmer’s toolkit. Whether you’re buffering large datasets, handling file uploads, or creating scratch workspaces for batch operations, Ruby’s standard library has you covered. This guide covers Tempfile, Dir.tmpdir, and Dir.mktmpdir — the three main utilities for temporary storage.

Why Use Temporary Files?

You need temporary storage when working with data that shouldn’t persist permanently. Common scenarios include:

  • Processing files too large to fit in memory
  • Storing intermediate results during multi-step transformations
  • Handling uploaded files before moving them to permanent storage
  • Creating scratch workspaces for parallel operations

Ruby’s temporary file utilities solve several problems automatically: they generate unique filenames to prevent collisions, create files with restricted permissions for security, and support automatic cleanup so you don’t leave behind orphan files.

Creating Temporary Files with Tempfile

The Tempfile class from Ruby’s standard library creates temporary files with predictable behavior. Two main methods exist: Tempfile.create (recommended) and Tempfile.new (legacy).

Tempfile.create is the modern approach. It returns a regular File object rather than a Tempfile object, which means better performance and cleaner semantics:

require 'tempfile'

Tempfile.create('myapp') do |file|
  file.puts "Hello, World!"
  file.rewind
  puts file.read
end
# => Hello, World!
# File is automatically deleted after the block

The block form guarantees cleanup — the file is deleted when the block exits, even if an exception occurs. Without a block, you’re responsible for cleanup:

file = Tempfile.create('data')
file.write "Some content"
file.close
File.unlink(file.path)

Security: File Permissions

Tempfile creates files with restrictive permissions (0600) by default, meaning only the owner can read or write:

require 'tempfile'

file = Tempfile.create('secure')
puts file.stat.mode.to_s(8)
# => 100600 (owner read/write only)

This is important when handling sensitive data. The 0600 permission ensures other users on the system cannot read your temporary files.

The Anonymous Option

For highly sensitive data that should never be visible to other processes, use anonymous: true:

require 'tempfile'

Tempfile.create(anonymous: true) do |file|
  file.write "secret data"
  file.rewind
  puts file.read
end
# => secret data
# File is unlinked immediately after creation - never visible on filesystem

On Linux, this uses O_TMPFILE for efficient kernel-level support. The file exists only as an open file descriptor.

Dir.tmpdir: Finding the System Temp Directory

Dir.tmpdir returns the path to the system’s temporary directory. It checks multiple environment variables and falls back to platform-specific defaults:

require 'tmpdir'

puts Dir.tmpdir
# => "/tmp" on most Linux systems
# => "C:\\Users\\Username\\AppData\\Local\\Temp" on Windows

The search order is: TMPDIR, TMP, TEMP, then platform-specific defaults (/tmp on Unix, user temp folder on Windows), and finally the current directory as a last resort.

You can use this to create temp files in a specific location:

require 'tempfile'

file = Tempfile.new('custom', Dir.tmpdir)
puts file.path
# => "/tmp/custom20260318-12345-67890"

Creating Temporary Directories with Dir.mktmpdir

When you need a temporary directory rather than a file, Dir.mktmpdir creates one with secure permissions (0700):

require 'tmpdir'

Dir.mktmpdir do |dir|
  puts "Created: #{dir}"
  # => Created: "/tmp/20260318-12345-67890"
  File.write("#{dir}/data.txt", "temporary content")
  puts File.read("#{dir}/data.txt")
  # => temporary content
end
# Directory is automatically removed after block exits

The block form ensures the directory is cleaned up. Without a block, you must handle cleanup yourself:

require 'tmpdir'
require 'fileutils'

dir = Dir.mktmpdir('myapp')
begin
  File.write("#{dir}/output.txt", "results")
ensure
  FileUtils.remove_entry dir
end

You can specify prefix and suffix for the directory name:

Dir.mktmpdir(['session_', '.cache']) do |dir|
  puts dir
  # => "/tmp/session_20260318-xxxxxx.cache"
end

Understanding Cleanup Behavior

Both Tempfile.create and Dir.mktmpdir support block forms that guarantee cleanup:

Tempfile.create do |file|
  # File exists here
end
# File is deleted here, guaranteed

The block form is safest because cleanup happens when the block exits — predictable timing, no reliance on garbage collection.

Manual Cleanup with Tempfile.new

Tempfile.new and Tempfile.open are older methods that return Tempfile objects. They rely on garbage collection finalizers for cleanup, which is unreliable:

file = Tempfile.new('data')
file.write "content"
file.close
file.unlink  # Must call explicitly!

The finalizer may not run if the process crashes or is killed. Always explicitly close and unlink when not using blocks:

file = Tempfile.new('data')
begin
  file.write stuff
ensure
  file.close
  file.unlink if file.path
end

The unlink method removes the file from the filesystem while keeping the file handle open:

file = Tempfile.create('test')
puts file.path
# => "/tmp/test20260318-xxxxxx"
file.unlink
puts file.path
# => nil (path returns nil after unlink)
puts file.closed?
# => false (file handle still open)
file.close
# Now you can still read/write through the handle

This is useful when you want the file gone from disk but still need to work with its contents in memory.

Common Gotchas

GC Timing is Unpredictable

Tempfile objects register finalizers with Ruby’s garbage collector. Files may persist until GC runs — or not at all if references remain:

# May leave orphan files
file = Tempfile.new('data')
file.write stuff
file.close
# File might stay until GC runs

# Explicit cleanup - reliable
file = Tempfile.new('data')
begin
  file.write stuff
ensure
  file.close
  file.unlink
end

Tempfile is Not Thread-Safe

If multiple threads access the same Tempfile, wrap operations in a mutex:

require 'tempfile'

mutex = Mutex.new
file = Tempfile.create('shared')

threads = 5.times.map do |i|
  Thread.new do
    mutex.synchronize { file.write "Thread #{i}\n" }
  end
end

threads.each(&:join)
file.rewind
puts file.read

Calling unlink before closing works on POSIX systems but silently fails on Windows. The file remains until the handle closes:

file = Tempfile.create('test')
file.unlink  # Works on Linux/macOS, may not on Windows
file.close

Summary

MethodReturn TypeCleanupBest For
Tempfile.createFileBlock form or explicitMost cases
Tempfile.newTempfileManual or GCLegacy compatibility
Dir.tmpdirStringN/AFinding temp directory
Dir.mktmpdirStringBlock form or explicitTemporary workspaces

Best practices:

  • Prefer block forms for automatic, predictable cleanup
  • Use Tempfile.create over Tempfile.new
  • Explicitly close and unlink when not using blocks
  • Use anonymous: true for sensitive data

See Also