High-performance image optimizer — modern successor to img-optimize, with native nginx content negotiation support.
💡 Inspiration: This project was born as an evolution of img-optimize by VirtuBox, maintaining the philosophy of simplicity and efficiency, but with modern tools, native parallelism, and production-ready features.
| Feature | Description |
|---|---|
| 🚀 High Performance | Parallel processing on all cores (xargs -P) |
| 📦 Modern Formats | WebP, AVIF, JPEG XL with double extensions (.jpg.webp, .png.avif) |
| 🎯 Content Negotiation | Compatible naming with nginx map $http_accept |
| 🔒 Owner Preservation | Maintains www-data or other owner in multi-user environments |
| 🛡️ Smart Validation | Detects real file type (magic numbers), not just extension |
| 📊 Complete Report | Per-file savings, total space saved, and execution time |
| 🔧 CPU Limiting | --cpu-limit flag to prevent server overload |
| Format | img-optimize (original) | img-turbo |
|---|---|---|
| JPEG | jpegoptim |
mozjpeg — up to 20% smaller at same quality |
| PNG | optipng (single-thread) |
oxipng (Rust, multithread) — up to 10× faster |
| WebP | basic cwebp |
cwebp + native .webp optimization |
| AVIF | go-avif (abandoned) |
avifenc (official libavif, active) |
| JPEG XL | ❌ | cjxl — lossless JPEG recompression |
| Parallelism | ❌ serial | ✅ All available cores |
| Double Extension | ❌ | ✅ .jpg.webp, .png.avif for nginx |
| Preserve Owner | ❌ | ✅ --preserve-owner for cronjobs |
| CPU Limit | ❌ | ✅ --cpu-limit for production servers |
brew install oxipng mozjpeg webp libavif jpeg-xl# Ubuntu/Debian (some packages may need compilation)
# Check repository availability or use brew for Linux
# Script installation
git clone https://github.com/overdigo/img-turbo.git \$HOME/.img-turbo
sudo install -m 755 \$HOME/.img-turbo/img-turbo.sh /usr/local/bin/img-turboimg-turbo [options] [path]| Flag | Description |
|---|---|
--jpg, --jpeg |
Optimize JPG/JPEG with mozjpeg (jpegtran) |
--png |
Optimize PNG with oxipng (multithread) |
--webp |
Convert to WebP (generates .jpg.webp, .png.webp) |
--webp-opt |
Optimize existing WebP images (in-place) |
--avif |
Convert to AVIF (generates .jpg.avif, .png.avif) |
--jxl |
Convert to JPEG XL (generates .jpg.jxl, .png.jxl) |
--std |
Optimize JPG + PNG + WebP (recommended for existing assets) |
--next |
Generate WebP + AVIF (modern formats) |
--all |
Everything: optimize originals + generate modern formats (excludes JXL) |
| Flag | Description | Default |
|---|---|---|
--path <dir> |
Images directory | . (current) |
--jobs <n> |
Parallelism | auto (detected cores) |
--jpg-quality <n> |
JPEG quality | 82 |
--png-level <n> |
oxipng level (0-6) | 4 |
--webp-quality <n> |
WebP quality | 82 |
--avif-quality <n> |
AVIF quality (0-63) | 30 |
--jxl-distance <n> |
JXL distance (0-15) | 1.0 |
--no-strip |
Keep EXIF/XMP metadata | removes |
--backup |
Create .orig before optimizing |
disabled |
--preserve-owner |
Keep original file owner/group | disabled |
--cpu-limit <n> |
Limit CPU to N% (e.g., 90) | — |
--no-recursive |
Don't search subdirectories | enabled |
--cmin [+|-]<n> |
Filter: modified N minutes ago | — |
-q, --quiet |
Silent mode | — |
-h, --help |
Show help | — |
# Optimize JPG + PNG in current directory
img-turbo --std
# Optimize specific directory
img-turbo --std --path /var/www/html/images
# Maximum PNG compression (slower)
img-turbo --png --png-level 6 --path ./icons# Generate .jpg.webp, .png.webp, .jpg.avif, .png.avif
img-turbo --next --path /var/www/images
# Complete workflow: optimize + generate all formats
img-turbo --all --path /var/www/html
# WebP only
img-turbo --webp --path ./assets
# AVIF with maximum quality
img-turbo --avif --avif-quality 20 --path ./photos# Limit CPU to 90% to avoid affecting production server
img-turbo --all --cpu-limit 90 --preserve-owner --path /var/www/html/images
# Preserve owner (useful when script runs as different user)
img-turbo --all --preserve-owner --path /var/www/html/images
# Optimize only files modified in the last hour, with backup
img-turbo --jpg --backup --cmin -60 --preserve-owner --path /srv/uploads
# Silent processing for cronjobs
img-turbo --std --cmin -1440 --quiet --preserve-owner --path /var/www/uploads# /etc/cron.d/img-turbo
# 90% CPU limit to avoid impacting production server
0 2 * * * www-data /usr/local/bin/img-turbo --std --cpu-limit 90 --cmin -1440 --quiet --preserve-owner --path /var/www/html/wp-content/uploads
0 3 * * * www-data /usr/local/bin/img-turbo --next --cpu-limit 90 --cmin -1440 --quiet --preserve-owner --path /var/www/html/wp-content/uploadsimg-turbo generates files with double extensions (.jpg.webp, .png.avif) to facilitate content negotiation in nginx:
# /etc/nginx/conf.d/webp-avif.conf
map \$http_accept \$webp_suffix {
default "";
"~*webp" ".webp";
}
map \$http_accept \$avif_suffix {
default "";
"~*avif" ".avif";
}
server {
location ~* \\.(jpg|jpeg|png)\$ {
add_header Vary "Accept";
try_files \$uri\$avif_suffix \$uri\$webp_suffix \$uri =404;
}
}How it works:
- Client requests
/image.jpgwithAccept: image/avif - Nginx tries:
/image.jpg.avif→ exists? Serve AVIF - If not: tries
/image.jpg.webp→ exists? Serve WebP - Fallback: serve original JPEG
| File | jpegoptim | mozjpeg | Savings |
|---|---|---|---|
| photo.jpg (2.1 MB) | 1.85 MB | 1.52 MB | ~18% smaller |
| Scenario | optipng | oxipng | Speed |
|---|---|---|---|
| 100 PNG files | ~45s | ~8s | ~5.6× faster |
| Format | vs JPEG | vs PNG |
|---|---|---|
| WebP | -25% to -35% | -15% to -30% |
| AVIF | -40% to -55% | -30% to -45% |
| JPEG XL | -20% to -30% (lossless) | -15% to -25% |
img-turbo verifies the real file type using magic numbers, not just the extension:
✅ Incorrectly renamed files are detected
✅ PNGs with .jpg extension are ignored in JPEG worker
✅ Protection against accidental corruption
In environments where the script runs as a different user than the web server:
# Script runs as 'deploy', but files need to be 'www-data'
sudo -u deploy img-turbo --all --preserve-owner --path /var/www/images
# Or via sudoers for automation
www-data ALL=(deploy) NOPASSWD: /usr/local/bin/img-turboThe --preserve-owner flag copies the UID/GID from the original file to the generated files.
- ✨ Explicit
.jpegsupport (alias for--jpg) - 🚀 High-scalability architecture: handles thousands of files via temporary lists (avoiding
ARG_MAXlimits) - 🛡️ Enhanced robustness: workers no longer abort the entire script on single-file failures
- 🔧 Improved temporary file handling
- ✨ Native WebP optimization (
--webp-opt) - 🔧 Included WebP in standard mode (
--std) - 🔧 Removed JXL from automatic mode (
--all) - now opt-in only
- ✨ Double extension for content negotiation (
.jpg.webp,.png.avif) - ✨
--preserve-ownerflag for multi-user cronjobs - ✨ File type validation by magic numbers
- ✨
--cpu-limitflag for production server protection - 🔧 Automatic Homebrew path support
- 🚀 Parallel processing with xargs
- 📊 Space savings report
- 🛡️ Automatic backup with
--backup
This project is an evolution of img-optimize created by VirtuBox.
We thank VirtuBox for the excellent foundation and philosophy of simplicity that inspired img-turbo. img-optimize remains an excellent choice for those seeking a lightweight and straightforward solution.
MIT License — free to use in personal and commercial projects.
Pull requests are welcome! Areas of interest:
- Support for new formats (HEIC, AVIF-10bit)
- CI/CD pipeline integration
- Content-based parameter optimization (photo vs. illustration)
Made with ⚡ for webmasters who care about performance.