Tempfile and Tmpdir for Temporary Storage
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 (Recommended)
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
Block Forms (Recommended)
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
Unlink Patterns
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
Unlink-Before-Close on Windows
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
| Method | Return Type | Cleanup | Best For |
|---|---|---|---|
Tempfile.create | File | Block form or explicit | Most cases |
Tempfile.new | Tempfile | Manual or GC | Legacy compatibility |
Dir.tmpdir | String | N/A | Finding temp directory |
Dir.mktmpdir | String | Block form or explicit | Temporary workspaces |
Best practices:
- Prefer block forms for automatic, predictable cleanup
- Use
Tempfile.createoverTempfile.new - Explicitly close and unlink when not using blocks
- Use
anonymous: truefor sensitive data
See Also
- Ruby Core Classes — Overview of Ruby’s built-in classes including File and IO
- Ruby String Methods — Working with string data in Ruby
- Ruby Kernel Methods — Essential kernel methods like
requireandputs