compression.zstd: Compress Data with Zstandard

Published on: September 18, 2026
Reading time: 5 minutes
Code and file structure for Python Zstandard compression

The compression.zstd module brings the Zstandard format to Python’s standard library and provides a fast, modern way to reduce the size of files and byte sequences. It is a strong choice for backups, log pipelines, caches, data exports, network payloads, and systems that need better compression than gzip without accepting extremely slow processing.

This guide covers byte compression, streaming, compression levels, dictionaries, integrity checks, and defensive decompression. For related workflows, review the Academify guides to safe ZIP archives with zipfile, file handling with pathlib, reading huge files without exhausting memory, and copying and moving files with shutil.

What Zstandard is

Zstandard, commonly called Zstd, is a lossless compression algorithm designed to balance speed and compression ratio. On many text and structured-data workloads, it can create smaller output than gzip while compressing and decompressing quickly. It also supports adjustable levels, reusable dictionaries, independent frames, and streaming.

The official Python compression.zstd documentation is the primary source for version-specific signatures. The underlying frame format is standardized in RFC 8878.

Compressing bytes

For small payloads, work directly with bytes. Encode text, compress it, and decompress it when needed.

from compression import zstd

text = "Python and Zstandard compression" * 100
data = text.encode("utf-8")

compressed = zstd.compress(data)
restored = zstd.decompress(compressed)

print(len(data), len(compressed))
print(restored.decode("utf-8") == text)

This approach is convenient, but both the source and result may coexist in memory. For large inputs, use a stream so memory consumption stays predictable.

Choosing a compression level

Higher levels usually spend more CPU to produce smaller output. A low or medium level is often best for APIs, caches, and frequently generated files. Higher levels may be worthwhile for archival data written once and downloaded many times.

compressed = zstd.compress(data, level=6)

Benchmark your own workload. JSON lines, source code, database dumps, and plain text behave differently from images or data that is already compressed. Measure compressed size, compression time, decompression time, memory, and total infrastructure cost.

Streaming large files

A streaming workflow reads and writes bounded chunks instead of loading an entire file.

from pathlib import Path
from compression import zstd

source = Path("events.jsonl")
target = Path("events.jsonl.zst")

with source.open("rb") as input_file, target.open("wb") as output_file:
    with zstd.open(output_file, mode="wb", level=5) as compressor:
        while chunk := input_file.read(1024 * 1024):
            compressor.write(chunk)

A one-megabyte chunk is a reasonable starting point, not a universal rule. Smaller chunks reduce temporary memory but increase function calls. Larger chunks may improve throughput until disk or CPU becomes the bottleneck.

Safe decompression

Compressed data from an external source is untrusted input. A tiny compressed file may expand into gigabytes. Enforce an output limit, write only to controlled paths, check available disk space, and remove partial files after errors.

from pathlib import Path
from compression import zstd

source = Path("events.jsonl.zst")
target = Path("events-restored.jsonl")
max_output = 2 * 1024 * 1024 * 1024
written = 0

try:
    with zstd.open(source, mode="rb") as reader, target.open("wb") as writer:
        while chunk := reader.read(1024 * 1024):
            written += len(chunk)
            if written > max_output:
                raise ValueError("Decompressed output exceeds the limit")
            writer.write(chunk)
except Exception:
    target.unlink(missing_ok=True)
    raise

Services should also apply request-size limits, timeouts, quotas, and cancellation. Never let a user-provided archive choose an arbitrary output path.

Compression dictionaries

Dictionaries improve compression when you process many small, structurally similar values, such as JSON events, telemetry records, or messages with repeated field names. Training captures common byte patterns that would otherwise be too short for the compressor to learn within each item.

Collect representative samples, train a dictionary, assign it a version, and use the same dictionary for compression and decompression. The receiver must possess the matching version. Store a dictionary identifier next to each payload and retain old dictionaries for as long as dependent data exists.

Checksums and integrity

Compression is not a complete integrity strategy. Calculate a cryptographic hash such as SHA-256 and keep it in a manifest with the original size, compressed size, creation time, algorithm, and dictionary version.

import hashlib

def sha256_file(path):
    digest = hashlib.sha256()
    with open(path, "rb") as file:
        while chunk := file.read(1024 * 1024):
            digest.update(chunk)
    return digest.hexdigest()

After restoring a backup, calculate the hash again and compare it before replacing production data.

Zstd versus gzip and ZIP

Choose Zstd when throughput and compression ratio matter and you control the reader. Choose gzip for maximum compatibility with older tooling and web infrastructure. Choose ZIP when you need a container that preserves multiple filenames, directory entries, and per-file metadata. For directory backups, a common design is to create a tar stream and compress that stream with Zstandard.

Atomic output files

Write compressed output to a temporary file in the same filesystem, flush it, validate it, and then rename it over the final destination. Filesystem rename operations are typically atomic within one filesystem, so consumers never observe a half-written result.

from pathlib import Path
import os

final = Path("events.jsonl.zst")
temporary = final.with_suffix(final.suffix + ".tmp")

# Write and validate temporary here
os.replace(temporary, final)

This pattern is valuable for scheduled exports, backup jobs, and caches read concurrently by other processes.

Production practices

Record compression ratio, duration, failures, input bytes, and output bytes. Alert when ratios change sharply because that may reveal a schema change, accidental double compression, or unexpected binary content. Add retention rules and periodically perform restore tests instead of assuming backups are valid.

Avoid recompressing JPEG, PNG, MP4, ZIP, and other formats that already use compression. The output may become slightly larger while consuming CPU. Do not automatically choose the maximum level either. A middle level frequently gives a better cost-to-size balance for continuous pipelines.

Reusable helper

from pathlib import Path
from compression import zstd

def compress_file(source, target, level=5, chunk_size=1024 * 1024):
    source = Path(source)
    target = Path(target)
    temporary = target.with_suffix(target.suffix + ".tmp")

    try:
        with source.open("rb") as reader, temporary.open("wb") as raw_writer:
            with zstd.open(raw_writer, mode="wb", level=level) as writer:
                while chunk := reader.read(chunk_size):
                    writer.write(chunk)
        temporary.replace(target)
    except Exception:
        temporary.unlink(missing_ok=True)
        raise

The helper keeps memory bounded and avoids leaving a final file in a partially written state. Add logging, hashes, permissions, and free-space checks according to your environment.

Conclusion

compression.zstd gives Python applications an efficient lossless compression option with simple APIs and advanced features. Use direct byte functions for small values, streaming for large files, dictionaries for repeated small records, and strict output limits for untrusted data. Combined with hashes, metrics, restore tests, and atomic writes, Zstandard can reliably support backups, logs, data pipelines, and network storage.

Share:

Facebook
WhatsApp
Twitter
LinkedIn

Article content

    Related articles

    Programmer managing an asynchronous queue with Python asyncio.Queue.shutdown
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    asyncio.Queue.shutdown: Shut Down Queues Safely

    Learn Python asyncio.Queue.shutdown to close queues, release workers, drain tasks, and prevent deadlocks in asynchronous pipelines.

    Ler mais

    Tempo de leitura: 5 minutos
    17/09/2026
    Debugging a running Python process with pdb -p
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python pdb -p: Debug Running Processes

    Learn how to use Python pdb -p to attach to running processes, inspect stacks, diagnose hangs, and debug production incidents

    Ler mais

    Tempo de leitura: 6 minutos
    17/09/2026
    Python code processed in batches with itertools.batched
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    itertools.batched strict: Validate Complete Batches

    Learn Python itertools.batched with strict mode to build batches, validate complete groups, and process streams safely.

    Ler mais

    Tempo de leitura: 5 minutos
    16/09/2026
    Data analysis and calculations with Python math.sumprod
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    math.sumprod: Dot Products and Weighted Averages

    Learn Python math.sumprod for dot products, weighted averages, costs, scores, and clear numeric calculations.

    Ler mais

    Tempo de leitura: 4 minutos
    16/09/2026
    CSV data analysis with Python csv.QUOTE_STRINGS
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    csv.QUOTE_STRINGS: Preserve Types in CSV Files

    Learn Python csv.QUOTE_STRINGS to quote text, preserve types, and build more predictable and secure CSV files.

    Ler mais

    Tempo de leitura: 4 minutos
    15/09/2026
    Code and file paths for Python PurePath.full_match
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    PurePath.full_match: Validate Paths with Glob Patterns

    Learn Python PurePath.full_match to validate complete paths with glob patterns, control case sensitivity, and build precise file filters.

    Ler mais

    Tempo de leitura: 4 minutos
    15/09/2026