imaginary
Self-hosted microservice for on-demand image processing
imaginary is a fast, self-hosted HTTP microservice for high-level image processing, built on libvips. It performs resizing, cropping, format conversion, and more, and is commonly used as an image backend for self-hosted photo and media apps.
Key features
- On-demand image transformation
- libvips-based performance
- HTTP API
- Stateless microservice
Pros & cons
Strengths
- Very fast processing
- Easy to scale
Trade-offs
- Backend service only
- No UI
imaginary replaces
Last reviewed Sep 13, 2026 · 775 words
imaginary is a stateless HTTP endpoint that takes an image and a set of query parameters and returns a resized, cropped, or converted image. That is the whole product. There is no gallery, no login, no storage, and no UI; if you came here looking for a Google Photos replacement, Immich is the page you want. If you run a self-hosted app that needs thumbnails generated on the fly and would rather not embed libvips into it, imaginary is a 6,000-star, MIT-licensed Go binary that does exactly that job in about 256 MB of RAM.
It is a resize endpoint, not a gallery
The API is a handful of routes: /resize, /crop, /smartcrop, /thumbnail, /convert, /watermark, /pipeline, plus /info for metadata. Each takes the image as a POST body or, with -enable-url-source, fetches it from a url= parameter. Output format follows the type= parameter, so type=webp gives you WebP from a JPEG source without touching disk. Because libvips streams and works on tiles rather than loading the whole bitmap, a 20-megapixel JPEG resizes in tens of milliseconds on modest hardware and memory stays flat under concurrency. That performance is the reason the project exists; ImageMagick-based services fall over at a fraction of the load.
Where it sits in a stack
A self-hoster uses imaginary in one of three ways. First, as the image backend an app already knows how to call: Nextcloud has a preview_imaginary_url config option that hands preview generation to it, which cuts the PHP preview cost sharply for large photo libraries. Second, as a transformation layer in front of object storage: your reverse proxy routes /img/* to imaginary, which fetches the original from MinIO or a plain directory and serves resized variants. Third, as an internal utility for scripts: curl -F "[email protected]" "http://imaginary:9000/resize?width=800&type=webp" -o out.webp.
services:
imaginary:
image: h2non/imaginary:latest
command: -enable-url-source -concurrency 20 -mount /images
environment:
- PORT=9000
volumes:
- ./originals:/images:ro
restart: unless-stopped
Port 9000 is the default. Do not publish it to the internet; put it on an internal Docker network and let the reverse proxy or the calling app talk to it.
The URL-fetch flag is the gotcha
-enable-url-source is what makes the "resize anything by URL" pattern work, and it also turns your server into an open fetcher that will happily request http://10.0.0.5/admin on someone else's behalf. Restrict it with -allowed-origins https://cdn.example.com,https://files.example.com, or leave URL sourcing off and use -mount so only files under a fixed directory are reachable. Add an API key with -key if the calling app can send an API-Key header. imaginary has no rate limiting and no built-in cache, so a reverse proxy cache with a long Cache-Control on responses is what keeps a burst of thumbnail requests from becoming a burst of libvips work.
No cache means you bring one
Every request is recomputed. For a Nextcloud preview backend that is fine, because Nextcloud stores the result. For a public image CDN pattern it is not: put Caddy or nginx in front with a proxy cache keyed on the full query string, or you will resize the same hero image 10,000 times a day. Budget CPU accordingly; libvips is efficient but 50 concurrent 4K conversions still saturate two cores.
imgproxy is the safer pick for a new deployment
imaginary works and the maintainer keeps it building, but development has slowed, and it predates modern formats in places: AVIF support depends on the libvips build inside the image, and HEIC input needs a custom build. imgproxy covers the same job with signed URLs by default, more active development, and better format coverage. Thumbor is the Python-world equivalent with a plugin ecosystem. If you are wiring up a new app today and have no existing imaginary integration, start with imgproxy. If your app already speaks imaginary's API, as Nextcloud does, there is no reason to switch.
What I'd do
For a Nextcloud instance with more than about 50 GB of photos, run imaginary on the same Docker network, set preview_imaginary_url, and enjoy previews that stop pegging PHP workers. For anything public-facing, use imgproxy with signed URLs behind a caching proxy instead. Either way, treat the URL-source flag as a firewall decision, keep the service off the public internet, and remember that this is a component you bolt onto a photo stack, not the stack itself.
Compare imaginary
1 head-to-head comparisons.
Similar photo management apps
Immich
Photo ManagementHigh-performance self-hosted photo and video backup
Replaces Google Photos, iCloud Photos
PhotoPrism
Photo ManagementAI-powered photo app for the decentralized web
Replaces Google Photos
Ente Photos
Photo ManagementEnd-to-end encrypted photo storage and backup
Replaces Google Photos, iCloud Photos
DeepFace
Photo ManagementFace recognition and facial attribute analysis framework
Replaces AWS Rekognition, Azure Face API
gallery-dl
Photo ManagementCommand-line tool to download image galleries from websites
Replaces Manual downloads
Color Thief
Photo ManagementLibrary to extract color palettes from photos
Replaces Commercial color analysis APIs