The Python tarfile module reads, writes, and extracts TAR archives, including gzip, bzip2, XZ, and, in Python 3.14, Zstandard compression when available. Unlike GZIP, which represents one stream, TAR is a container that preserves paths, directories, permissions, timestamps, links, and other filesystem metadata.
Those features also create risk. A hostile archive may attempt to write outside the destination, create dangerous links or device files, or exhaust storage with many members. Since Python 3.14, the default extraction filter is data, which is safer than the previous fully trusted behavior. It still does not prevent every denial-of-service scenario.
When to use tarfile
Use TAR to package directories, backups, source trees, and Unix artifacts. ZIP is often easier for desktop users; see the Python ZIP guide. For a single compressed stream, use Python gzip.
Create an uncompressed TAR
import tarfile
with tarfile.open("project.tar", "x") as tar:
tar.add("src", arcname="src")
tar.add("README.md", arcname="README.md")Exclusive mode fails if the destination exists. arcname controls the stored path and prevents absolute local directory names from leaking into the archive.
Create compressed TAR files
import tarfile
with tarfile.open("project.tar.gz", "x:gz", compresslevel=6) as tar:
tar.add("src", arcname="src")
with tarfile.open("data.tar.xz", "x:xz", preset=6) as tar:
tar.add("data", arcname="data")Use modes such as w:gz, w:bz2, w:xz, and w:zst. For reading, r:* detects compression automatically. Compressed archives cannot be appended with the ordinary append mode; create a replacement archive instead.
Inspect members first
import tarfile
with tarfile.open("project.tar.gz", "r:*") as tar:
for member in tar:
print(member.name, member.size, member.type)Each entry is a TarInfo. Before extraction, inspect names, types, sizes, link targets, and duplicates. Helpers such as isfile(), isdir(), issym(), islnk(), and isdev() simplify classification.
Safe extraction in Python 3.14
The default is now the data filter, but specifying it explicitly also protects code running on versions where the old default may still apply:
import tarfile
from pathlib import Path
source = Path("upload.tar.gz")
destination = Path("extracted").resolve()
destination.mkdir(parents=True, exist_ok=False)
with tarfile.open(source, "r:*") as tar:
tar.extractall(destination, filter="data")The filter rejects absolute paths, paths escaping the destination, external links, and special files. It also reduces permissions and ignores stored owner information.
The data filter is not a complete sandbox
An archive may still contain millions of members, huge files, long names, duplicates, case-insensitive collisions, or payloads that consume disk and CPU. Extract into a new temporary directory, enforce OS-level quotas, and delete the entire directory after a failure.
Limit count and declared size
import tarfile
MAX_FILES = 5_000
MAX_TOTAL = 2 * 1024**3
def accepted_members(tar):
total = 0
for index, member in enumerate(tar, start=1):
if index > MAX_FILES:
raise ValueError("too many members")
if member.size < 0:
raise ValueError("invalid size")
total += member.size
if total > MAX_TOTAL:
raise ValueError("expanded size exceeded the limit")
if member.isdev() or member.isfifo():
continue
yield member
with tarfile.open("upload.tar", "r:*") as tar:
tar.extractall("destination", members=accepted_members(tar), filter="data")Header sizes can also be malicious. Combine this check with filesystem quotas and process isolation.
Reject links when unnecessary
import tarfile
def regular_data(member, path):
member = tarfile.data_filter(member, path)
if member is None:
return None
if member.issym() or member.islnk():
return None
return member
with tarfile.open("upload.tar.gz", "r:*") as tar:
tar.extractall("destination", filter=regular_data)A custom filter may return a modified TarInfo, return None, or raise an exception.
Read one member without extraction
import tarfile
import json
with tarfile.open("package.tar.gz", "r:*") as tar:
member = tar.getmember("manifest.json")
if not member.isfile() or member.size > 1_000_000:
raise ValueError("invalid manifest")
with tar.extractfile(member) as file:
manifest = json.load(file)This is preferable when only a manifest, configuration, or signature is needed.
Control names while archiving
tar.add() otherwise stores the supplied local path. Always use arcname to produce a portable layout and avoid disclosing server directories.
Filter and normalize during creation
import tarfile
def prepare(info):
if info.name.endswith((".env", ".key")):
return None
return info.replace(
uid=0, gid=0, uname="root", gname="root", mtime=0
)
with tarfile.open("source.tar.gz", "x:gz", compresslevel=6) as tar:
tar.add("project", arcname="project", filter=prepare)Normalizing ownership and timestamps helps reproducible builds. Exclude secrets, caches, virtual environments, and temporary files.
Archive formats
USTAR_FORMAT: old and widely supported, with name and size limits.GNU_FORMAT: GNU extensions for long names and large files.PAX_FORMAT: current default, flexible, and UTF-8 friendly.
PAX is usually best for new archives.
Streaming modes
Modes such as r|gz and w|gz process blocks sequentially without random access, which works with stdin, stdout, sockets, and pipes:
import sys
import tarfile
with tarfile.open(fileobj=sys.stdout.buffer, mode="w|gz", compresslevel=6) as tar:
tar.add("result", arcname="result")In stream mode, process each member when it arrives because earlier entries cannot be revisited.
Partial extraction after errors
extractall() does not roll back files already written. Extract to an isolated temporary directory, validate the completed tree, and move it into place only after success.
Important exceptions
Handle ReadError, CompressionError, StreamError, and FilterError subclasses. Avoid setting errorlevel=0 for untrusted archives because refused members may be logged and skipped while extraction continues.
Testing
Include absolute paths, ../, links outside the destination, devices, FIFOs, duplicates, excessive member counts, huge files, Unicode names, truncated archives, and every supported compression. Test on Windows and Linux when portability matters.
Best practices
- Read with
r:*. - Create with exclusive mode.
- Set
arcname. - Extract with
filter="data". - Reject links unless required.
- Limit members, size, names, disk, and CPU.
- Use a new temporary destination.
- Normalize metadata when creating.
- Never assume a TAR is trusted.
Conclusion
Python tarfile is a complete tool for packaging filesystem trees and combining TAR with gzip, bzip2, XZ, or Zstandard. Python 3.14 improved the default, but applications still need explicit limits and isolation.
Read the official tarfile documentation and PEP 706. To monitor large extractions, see Python tracemalloc and Python trace.







