Skip to content

constantgillet/pixtimize

Repository files navigation

Pixtimize ⚡🖼️

Pixtimize is an open source image transform API compatible with the ImageKit API. Pixtimize is compatible with any S3 bucket service.

This is a Rust rewrite built on Axum for a fast, memory-safe web server.

Tools used

  • Rust and Axum as the web framework and runtime
  • AWS SDK for S3 to read source images and store transformed ones (any S3-compatible bucket)
  • Redis to store the cached image keys
  • libvips (via libvips-rs) for fast, low-memory image decoding, resizing, and encoding

How it works

For every request the server:

  1. Parses the transform string (from the path or the tr query param).
  2. Computes a cache key: sha256(image_path + transformations).
  3. Looks up the transformed image in the cache. On a GET hit the object is fetched from S3; on a HEAD hit the body is never downloaded — Redis stores {s3_key, size, content_type} and S3 HeadObject is used only for legacy markers that lack size.
  4. Otherwise it fetches the source from S3, applies the transform, stores the result in S3, records metadata in Redis, and returns it.

A cron job (CACHE_DELETE_CRON) periodically clears the cache (Redis markers and the matching S3 objects).

Transforms compatibility

Property name compatible comment
w fixed pixel values, and fractions in (0, 1) are treated as a percentage
h fixed pixel values, and fractions in (0, 1) are treated as a percentage
q quality of the image, default value is DEFAULT_QUALITY
f output format, default is DEFAULT_FORMAT; values: jpeg, jpg, png, webp

Limits (ImageKit-compatible)

Aligned with ImageKit transformation limits (free-plan values where adjustable):

Limit Value Behavior
Max image file size for processing 20 MB request rejected
Max image megapixels for processing 25 MP request rejected
Max transform dimensions (w / h) 65 535 px larger absolute values are ignored
Max WebP transform / output dimensions 16 383 px request rejected

Usage

You can transform an image by calling your URL like this (remember to encode the , as %2C in the query param):

https://yourdomain.com/image-example.png?tr=w-606,h-450,f-jpeg

or

https://yourdomain.com/tr:w-606,h-450,f-jpeg/image-example.png

Configuration

Required environment variables:

  • S3_ENDPOINT: URL of the S3 bucket (default https://ams3.digitaloceanspaces.com)
  • S3_BUCKET: name of the S3 bucket
  • S3_ACCESS_KEY: access key id of the S3 bucket
  • S3_SECRET_KEY: secret key of the S3 bucket
  • REDIS_URL: Redis connection URL

Optional environment variables:

  • PORT: port to listen on (default 3000)
  • S3_REGION: S3 region (default ams3)
  • DEFAULT_QUALITY: default quality applied to optimize images (default 80)
  • DEFAULT_FORMAT: default output format (default webp)
  • CACHE_DELETE_CRON: cron schedule for cache cleanup (default 0 1 * * 1, every Monday at 1am). Standard 5-field expressions are supported; a seconds field is optional.
  • CACHED_TIME: max-age (in seconds) advertised on served images (default 604800)

See .env.example for a template.

Commands

Prerequisites

Image processing is powered by native libvips, so it must be installed before building or running locally:

# macOS
brew install vips

# Debian / Ubuntu
sudo apt-get install -y libvips-dev

On Homebrew (or any non-standard prefix), point pkg-config at libvips so the build script can find it:

export PKG_CONFIG_PATH="$(brew --prefix vips)/lib/pkgconfig:$(brew --prefix)/lib/pkgconfig:$PKG_CONFIG_PATH"

Install the toolchain from rustup.rs, then:

# run in development
cargo run

# run the tests
cargo test

# build an optimized release binary
cargo build --release

Docker

docker build -t pixtimize .
docker run --rm -p 3000:3000 --env-file .env pixtimize

Deploy (Nixpacks)

The project ships a nixpacks.toml so it can be deployed on any Nixpacks-based platform (Railway, Coolify, Dokploy, Easypanel, ...).

It configures a few things the default Rust provider does not handle:

  • Nix build packages: cmake + gcc to compile aws-lc-rs (TLS), and pkg-config to locate native libraries at build time.
  • Apt packages: libvips-dev for the build, plus libvips42 and libglib2.0-0 for runtime (the Nixpacks base image is Ubuntu).
  • PKG_CONFIG_PATH pointing at the Ubuntu multiarch pkgconfig dirs so the nix pkg-config can see the apt-installed libvips.
  • NIXPACKS_NO_MUSL=1, because aws-lc-rs and libvips do not build/link cleanly against the default static musl target.
  • After cargo build, scripts/patch-native-binary.sh runs patchelf so the binary uses the system dynamic linker and searches apt’s /usr/lib/... for libvips/glib. Without this, Nixpacks links with a nix ld-linux that cannot see apt libraries — which surfaces as libglib-2.0.so.0: cannot open shared object file even when the packages are installed.

Do not set LD_LIBRARY_PATH or add vips/glib to nixLibs: both mix glibc variants and crash with __vdso_gettimeofday: invalid mode for dlopen().

The server binds to 0.0.0.0:$PORT, so the platform-provided PORT is used automatically. Set the required environment variables (see Configuration) in your platform's dashboard rather than committing a .env file.

Coolify / production: set the build pack to Dockerfile. The multi-stage Debian image installs libvips42 in the runtime stage cleanly and avoids the nix linker issue entirely.

License

Licensed under the Apache License 2.0.

About

Pixtimize is an opensource image transform api compatible with imagekit API. Pixtimize is compatible with any S3 bucket service.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages