imgopt is a high-performance microservice developed in Rust for the Rush CMS ecosystem. Its primary function is to receive images via HTTP, process them (resize, strip metadata), and convert them to WebP or AVIF efficiently.
Image processing in the main backend (Laravel) faced significant bottlenecks:
- Memory Consumption: The PHP stack + Imagick/GD loaded the entire framework into memory for each conversion Job (~100MB+ per process)
- CPU Blocking: Image encoding operations blocked Laravel workers, affecting other tasks
- System Dependencies: Managing libraries like
libmagickorlibgdacross different deployment environments was inconsistent
imgopt Solution:
- Decoupling: Removes the heavy load from the monolith.
- Efficiency: Drastically reduces RAM usage (from ~100MB to ~20-40MB under load).
- Consistency: Static binary or minimalist Docker image with all dependencies (such as
nasm,libwebp).
Opting for Rust was strategic to ensure robustness and performance in a resource-constrained environment (shared VPS):
- Memory Safety without GC: Rust guarantees memory safety at compile-time without the unpredictable pauses of a Garbage Collector (unlike Go or Java), crucial for consistent latency in high-throughput services.
- C/C++ Performance: Utilizes top-tier native libraries (
rav1efor AVIF,libwebpfor WebP) with zero abstraction overhead - Safe Concurrency: Rust's ownership model allows processing multiple images in parallel (via Tokio) without risk of data races
- Modern Ecosystem: Frameworks like Axum (based on Hyper and Tokio) offer market-leading ergonomics and performance for HTTP APIs
The service runs in a Docker container within the same internal network as Laravel (coolify), without public exposure.
graph TD
Laravel[Laravel App] -- "HTTP POST /convert" --> ImgOpt["imgopt (Rust)"]
ImgOpt -- "WebP/AVIF bytes" --> Laravel
Laravel -- "Saves to S3/Disk" --> Storage
- Input: Multipart form data (file + parameters)
- Processing: Decode -> Resize (optional) -> Encode (WebP/AVIF)
- Output: Processed image bytes (stateless)
- Language: Rust 2021
- Web Framework: Axum
- Async Runtime: Tokio
- Image Processing:
image(decode/operations)webp(libwebp bindings)ravif+rav1e(high-efficiency AVIF encoding)
- Memory Allocator:
jemallocator(optimized to avoid fragmentation on Linux)
Converts an uploaded image.
Headers:
Authorization: Bearer <token>Content-Type: multipart/form-data
Body (Multipart):
file: Image file (required)format:webp(default) oravifquality: 1-100 (default: 80)width: Target width (maintains aspect ratio ifheightis omitted)height: Target height (maintains aspect ratio ifwidthis omitted)
Returns service status.
{ "status": "ok", "version": "0.1.0", "uptime_seconds": 42 }- Rust (stable)
nasm(required to compile the AV1 encoderrav1ewith optimizations)
# Configure environment variables
cp .env.example .env
# Run
cargo runThe project includes unit and integration tests.
# Run all tests
# Note: --test-threads=1 is recommended if local memory is limited
export PATH=$PWD/bin_inst/bin:$PATH # If using local nasm
cargo testThe Dockerfile uses multi-stage build to create a lightweight final image (~debian-slim) containing only the compiled binary.
docker build -t imgopt .