TinyJPG

May 18, 2026 ยท View on GitHub

TinyJPG logo

CI status Releases Install TinyJPG C++23 CMake vcpkg License

TinyJPG

TinyJPG is a C++23 image optimizer built for fast, repeatable asset pipelines. It compresses JPEG, PNG, WebP, AVIF, and JPEG XL files in-process, generates responsive variants, scans folders, watches upload directories, and reports results as text, tables, or JSON.

The executable is available as both tinyjpg and the short alias tj.

Contents

Preview

BeforeAfter
Original portrait before compressionPortrait after TinyJPG compression
1.5 MB JPEG243 KB JPEG

This sample keeps the original 2236 x 1792 dimensions and reduces the file by about 84%.

Why TinyJPG

  • Native C++23 command line tool with no shell-outs for image conversion.
  • Codec coverage for JPEG, PNG, WebP, AVIF, and JPEG XL workflows.
  • One-shot run, recursive scan, and continuous watch modes.
  • Built-in responsive presets plus TOML configuration for custom variants.
  • Atomic writes, dry-run planning, skip-if-not-smaller behavior, and structured output for CI and automation.
  • Cross-platform CMake and vcpkg build on Linux, macOS, and Windows.

Install

Linux and macOS:

curl -fsSL https://tj.eorlov.org/install.sh | sh

Windows PowerShell:

irm https://tj.eorlov.org/install.ps1 | iex

Homebrew on Apple Silicon macOS:

brew tap OrlovEvgeny/tinyjpg
brew install tinyjpg

Install a specific version:

curl -fsSL https://tj.eorlov.org/install.sh | sh -s -- --version 1.0.1
irm https://tj.eorlov.org/install.ps1 -OutFile install.ps1
./install.ps1 -Version 1.0.1

The installers read the release manifest, pick the correct archive for the current platform, verify SHA256, and install tinyjpg plus the tj alias.

Quick Start

Run a safe dry-run first:

tj scan ./images --preset web --dry-run --format table

Generate optimized variants:

tj scan ./images --preset web --format table

Create a config file for repeatable jobs:

tj config print --defaults > tinyjpg.toml
tj config validate tinyjpg.toml
tj scan ./images --config tinyjpg.toml

Watch a directory continuously:

tj watch ./uploads --config tinyjpg.toml

Commands

CommandPurposeExample
runOptimize explicit files.tj run hero.jpg banner.png --preset web
scanOptimize supported images under files or directories.tj scan ./public/images --config tinyjpg.toml
watchRepeatedly scan paths and process changed images.tj watch ./uploads --config tinyjpg.toml
presets listShow built-in responsive presets.tj presets list --format table
config printPrint a default TOML config.tj config print --defaults
config validateValidate a TOML config before automation.tj config validate tinyjpg.toml
doctorCheck local runtime basics.tj doctor --format table
completionPrint shell completion for bash, zsh, or fish.tj completion zsh > _tj

Common options for run, scan, and watch:

OptionMeaning
--config, -cTOML configuration file.
--presetReplace configured variants with web, ecommerce, or avatar.
--dry-runPlan work without writing files.
--quiet, -qSuppress per-file text output.
--formatOutput as text, table, or json.

Output formats are intended for different uses: text for humans, table for inspection, and json for CI or other automation.

Configuration

Generate and validate a config:

tj config print --defaults > tinyjpg.toml
tj config validate tinyjpg.toml

TinyJPG starts from built-in defaults and then applies values from your TOML file. CLI flags such as --preset and --dry-run override the loaded config for that invocation.

Practical Config

This config is suitable for a service-style image pipeline. It watches an input directory, writes generated files into a separate output directory, and creates three web delivery variants.

[general]
workers = 0
log_level = "info"
queue_capacity = 512
stable_wait_ms = 400
dry_run = false

[watch]
paths = ["/var/lib/tinyjpg/inbox"]
recursive = true
include = ["*.jpg", "*.jpeg", "*.png", "*.webp"]
exclude = ["**/.cache/**", "*.tmp"]
prefix = []

[compress]
mode = "visually_lossless"
effort = "balanced"
keep_metadata = false
skip_if_not_smaller = true
preserve_original = true

[[variant]]
name = "large"
codec = "auto"
max_width = 1920
suffix = "-large"

[[variant]]
name = "card"
codec = "webp"
mode = "lossy"
max_width = 960
quality = 82
suffix = "-card"

[[variant]]
name = "thumb"
codec = "auto"
max_width = 320
max_height = 320
quality = 76
fit = "cover"
suffix = "-thumb"

[output]
directory = "/var/lib/tinyjpg/output"
pattern = "{stem}{suffix}.{ext}"
on_exist = "version"

For /var/lib/tinyjpg/inbox/hero.jpg, this can produce:

/var/lib/tinyjpg/output/hero-large.jpg
/var/lib/tinyjpg/output/hero-card.webp
/var/lib/tinyjpg/output/hero-thumb.jpg

Config Reference

[general]

SettingValuesMeaning
workers0 or positive integerWorker threads. 0 uses hardware concurrency.
log_leveltrace, debug, info, warn, errorRuntime verbosity.
queue_capacitypositive integerMaximum queued work items before producers wait.
stable_wait_ms0 or positive integerWatch-mode delay before reading a changed file, so uploads can finish.
dry_runtrue, falsePlan work without writing files. Can also be set with --dry-run.

[watch]

SettingValuesMeaning
pathsarray of pathsDefault paths for tj watch when no CLI path is passed.
recursivetrue, falseParsed from TOML; directory scanning currently walks recursively.
includeglob arrayParsed from TOML; current processing still selects files by supported image codec.
excludeglob arrayParsed from TOML; generated TinyJPG variants are skipped automatically.
prefixstring arrayParsed from TOML and reserved for path-prefix filtering.

[compress]

SettingValuesMeaning
modelossless, visually_lossless, lossyDefault fidelity target for variants.
effortfast, balanced, maxEncoder effort/speed preference.
keep_metadatatrue, falseKeep image metadata when possible.
skip_if_not_smallertrue, falseSkip outputs that would be larger than the source.
preserve_originaltrue, falseAvoid overwriting the source path; an empty suffix becomes -optimized.

[[variant]]

Each variant describes one output image. At least one variant is required. Variant names may contain letters, digits, _, and -.

SettingValuesMeaning
namestringVariant name shown in output and available as {name}.
codecauto, jpeg, png, webp, avif, jxlOutput codec. auto keeps the source codec.
modesame as [compress].modeOptional per-variant fidelity override.
quality1 to 100Encoder quality. If omitted, TinyJPG uses 82.
max_widthpositive integerMaximum output width.
max_heightpositive integerMaximum output height.
fitcontain, cover, fillResize behavior when both dimensions are set.
suffixstringAdded to output file names, for example -thumb.

Every non-original variant must set max_width, max_height, or both. The special variant name original is allowed without size constraints.

[output]

SettingValuesMeaning
directorypath or empty stringOutput directory. Empty means next to the input file.
patternstring with tokensOutput path template.
on_existskip, overwrite, versionBehavior when the output path already exists.

Supported pattern tokens:

TokenValue
{dir}Input directory or configured output directory.
{stem}Input file name without extension.
{suffix}Variant suffix.
{ext}Output extension for the chosen codec.
{name}Variant name.
{codec}Output codec name.
{width}Planned output width.
{height}Planned output height.

Useful patterns:

pattern = "{dir}/{stem}{suffix}.{ext}"
pattern = "{stem}-{width}w.{ext}"
pattern = "{codec}/{stem}-{name}.{ext}"

Presets

Use presets when you do not need custom variants:

tj scan ./images --preset web
tj scan ./products --preset ecommerce
tj scan ./avatars --preset avatar
PresetVariants
weboriginal, large 1920w, medium 1024w, thumb 320x320 cover
ecommerceoriginal, hero 1600w, listing 900w, thumb 320w
avataroriginal, full 512x512 cover, thumb 128x128 cover

Services

Release archives include service helpers for long-running optimization:

  • systemd unit, sysusers, and tmpfiles snippets for Linux.
  • launchd plist template for macOS.
  • PowerShell install and uninstall scripts for Windows services.

systemd Example

The generated Linux unit runs:

tinyjpg watch --config /etc/tinyjpg/tinyjpg.toml

It uses a dedicated tinyjpg user, protects the host filesystem, and only grants write access to /var/lib/tinyjpg and /var/log/tinyjpg. Keep watched input and output directories under /var/lib/tinyjpg, or add a systemd override with extra ReadWritePaths=.

Example setup:

sudo systemd-sysusers packaging/systemd/tinyjpg.sysusers
sudo systemd-tmpfiles --create packaging/systemd/tinyjpg.tmpfiles

sudo install -d -m 0750 -o root -g root /etc/tinyjpg
sudo install -d -m 0750 -o tinyjpg -g tinyjpg /var/lib/tinyjpg/inbox
sudo install -d -m 0750 -o tinyjpg -g tinyjpg /var/lib/tinyjpg/output
sudo install -m 0640 -o root -g tinyjpg tinyjpg.toml /etc/tinyjpg/tinyjpg.toml
sudo install -m 0644 build/packaging/systemd/tinyjpg.service /etc/systemd/system/tinyjpg.service
sudo systemctl daemon-reload
sudo systemctl enable --now tinyjpg.service

Check service health and logs:

systemctl status tinyjpg.service
journalctl -u tinyjpg.service -f

If your images live outside /var/lib/tinyjpg, add an override:

sudo systemctl edit tinyjpg.service
[Service]
ReadWritePaths=/srv/uploads /srv/images /var/lib/tinyjpg /var/log/tinyjpg

Then reload and restart:

sudo systemctl daemon-reload
sudo systemctl restart tinyjpg.service

Release Assets

Tagged releases publish platform archives to Cloudflare R2 and create a GitHub Release with changelog and install instructions.

PlatformArchive
Linux x86_64tinyjpg-{version}-linux-x86_64.tar.gz
Linux aarch64tinyjpg-{version}-linux-aarch64.tar.gz
macOS arm64tinyjpg-{version}-macos-arm64.tar.gz
Windows x86_64tinyjpg-{version}-windows-x86_64.zip

The public manifest for installers and future self-update support is:

https://tinyjpg.eorlov.org/tinyjpg/manifest.json

Build From Source

Requirements:

  • CMake 3.28 or newer.
  • Ninja.
  • A C++23 compiler.
  • vcpkg.
  • nasm on Linux for codec dependencies.

Configure, build, test, and install:

cmake -S . -B build -G Ninja \
  -DCMAKE_BUILD_TYPE=Release \
  -DCMAKE_TOOLCHAIN_FILE="$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" \
  -DBUILD_TESTING=ON

cmake --build build
ctest --test-dir build --output-on-failure
cmake --install build --prefix ~/.local

Create local CPack archives:

cpack --config build/CPackConfig.cmake

License

MIT