---
title: "Deploy Imagor"
description: "Image processing server that resizes and converts images from a URL"
category: "Storage"
url: https://railway.com/deploy/imagor-image-server
---

# Deploy Imagor

Image processing server that resizes and converts images from a URL

**[Deploy Imagor on Railway](https://railway.com/template/imagor-image-server)**

Machine-readable deploy manifest (JSON, validated by TemplateCI): https://railway.com/deploy/imagor-image-server/manifest.json

- **Creator:** A3A
- **Category:** Storage
- **Total deploys:** 1

## Template content

### imagor https://cdn.jsdelivr.net/gh/cshum/imagor@v1.9.6/docs/static/img/icon.png

- **Image:** shumc/imagor:latest
- **Health check:** /healthcheck
- **Public domain:** Yes

## Buckets

- **imagor-result**
- **imagor-source**

## Documentation

# Deploy and Host Imagor on Railway

Imagor is an open-source image processing server written in Go on top of libvips, the engine behind many commercial image CDNs. Every transformation lives in the URL — `fit-in/800x600/filters:format(webp):quality(85)/photo.jpg` — so your front-end never pre-generates thumbnails or stores a dozen variants of one asset. It speaks the thumbor URL syntax, making it a drop-in replacement for a thumbor install, and teams self-host Imagor for Cloudinary-style on-the-fly resizing without per-image pricing.

This template runs Imagor as a public HTTP service backed by two Railway object storage buckets. `imagor` is the only service with a domain: it takes a signed request, loads the original from the `imagor-source` bucket (or fetches it over HTTP when the path is a remote URL), renders the variant with libvips, and writes the result to the `imagor-result` bucket so the next identical request is served from cache. Remote originals are saved into `imagor-source` too, so each source is fetched once. The app tier keeps nothing on disk, which is what lets you raise the replica count later.

![Railway diagram of the single public imagor service](https://res.cloudinary.com/rroe4rtk/image/upload/f_auto,q_auto/v1789635083/imagor-architecture.webp)

## Getting Started with Imagor on Railway

There is no login screen and no admin account. Open the deployed URL and you should see the Imagor landing page with the running version, confirming the server is up; `/healthcheck` returns 200 for uptime monitoring. Because `IMAGOR_SECRET` is generated at deploy time, every image path must carry an HMAC-SHA1 signature, so an unsigned URL gets `403 url signature mismatch` rather than serving anyone who guesses a path. Copy `IMAGOR_SECRET` from the service variables and sign the part of the path after the signature segment:

```
IMAGOR_SECRET=""
P='fit-in/800x600/filters:format(webp):quality(85)/photo.jpg'
SIG=$(printf %s "$P" | openssl dgst -sha1 -hmac "$IMAGOR_SECRET" -binary | base64 | tr '+/' '-_')
echo "https:///$SIG/$P"
```

For your own images, upload them to `imagor-source` under the `source/` prefix with any S3 client and reference them by filename; or put a full `https://` image URL at the end of the path and Imagor fetches and caches it. Signing helpers exist for Go, Node, Python, PHP and Ruby, so a real application builds these URLs in its backend.

![Imagor landing page showing the running server version](https://res.cloudinary.com/rroe4rtk/image/upload/f_auto,q_auto/v1789635086/imagor-landing-page.webp)
![Imagor smart-cropped square thumbnail of a spacewalk photo](https://res.cloudinary.com/rroe4rtk/image/upload/f_auto,q_auto/v1789635087/imagor-smart-crop-thumbnail.webp)
![Imagor watermark filter composited over a lunar map](https://res.cloudinary.com/rroe4rtk/image/upload/f_auto,q_auto/v1789635089/imagor-watermark-composition.webp)

## About Hosting Imagor

Self-hosting Imagor makes sense as soon as image delivery becomes a real line item. A commercial image CDN charges per transformation and owns the URL your pages point at; Imagor puts that layer in your own infrastructure, in front of storage you control, for the cost of the container rendering the images.

- Resize, crop, fit-in, stretch, pad, rotate and flip from URL parameters
- Content-aware smart cropping that keeps a photo's subject in frame
- Filters for quality, format, fill, blur, sharpen, grayscale, hue, rounded corners and watermarks
- JPEG, PNG, WebP, AVIF, GIF and TIFF output with automatic WebP/AVIF negotiation, animation included
- HMAC URL signing (SHA1, SHA256 or SHA512), and storage backends for S3, Google Cloud Storage, local disk or plain HTTP

The topology is three pieces. `imagor` is the stateless renderer. `imagor-source` holds originals and caches remote fetches. `imagor-result` holds rendered variants keyed by a digest of the request and is safe to empty at any time, so clearing the derived cache never touches your originals.

## Why Deploy Imagor on Railway

Railway removes the parts of running an image server that are not about images:

- Both object storage buckets are provisioned and wired in for you
- A public HTTPS domain and certificate with no configuration
- The signing secret is generated at deploy time, so nothing ships unprotected
- Health checks and log aggregation as standard
- Raise replicas as traffic grows; the service holds no local state

## Common Use Cases

- Serving responsive `srcset` variants from one uploaded original
- Generating product thumbnails and gallery crops for an e-commerce catalogue
- Replacing a paid image CDN's transformation tier while keeping your own storage
- Watermarking user-submitted media at request time

## Imagor vs Thumbor vs imgproxy

| | Imagor | thumbor | imgproxy |
|---|---|---|---|
| Engine | Go + libvips | Python + PIL | Go + libvips |
| Licence | Apache-2.0 | MIT | Open core, paid Pro tier |
| URL syntax | thumbor-compatible | original | its own |
| Paid features | none | none | advanced filters, internal cache |

Imagor's published benchmarks put it ahead of thumbor on JPEG, PNG, WebP and AVIF, and trading places with imgproxy by format. Against imgproxy the practical difference is licensing: no Pro edition holds back filters or caching.

## Dependencies for Imagor

- `shumc/imagor:latest` — the Imagor server, Go and libvips in one image
- `imagor-source` — bucket holding originals under `source/`
- `imagor-result` — bucket holding rendered variants under `result/`

### Environment Variables Reference

| Variable | Purpose |
|---|---|
| `IMAGOR_SECRET` | HMAC key for URL signing; generated at deploy time |
| `PORT` | HTTP listening port, `8000` |
| `IMAGOR_AUTO_WEBP` / `IMAGOR_AUTO_AVIF` | Serve WebP or AVIF when the browser allows it |
| `IMAGOR_PROCESS_CONCURRENCY` | Ceiling on simultaneous libvips operations |
| `IMAGOR_CACHE_HEADER_TTL` | `Cache-Control` max-age on successful responses |
| `S3_LOADER_BUCKET` / `S3_STORAGE_BUCKET` | Bucket for reading and writing originals |
| `S3_RESULT_STORAGE_BUCKET` | Bucket for rendered variants |
| `HTTP_LOADER_BLOCK_PRIVATE_NETWORKS` | Refuses origin fetches aimed at internal addresses |
| `VIPS_STRIP_METADATA` | Drops EXIF, including GPS, from output |

### Deployment Dependencies

- Source: [github.com/cshum/imagor](https://github.com/cshum/imagor)
- Image: [shumc/imagor on Docker Hub](https://hub.docker.com/r/shumc/imagor)
- Docs: [docs.imagor.net](https://docs.imagor.net)

## Hardware Requirements for Self-Hosting Imagor

| | Minimum | Recommended |
|---|---|---|
| CPU | 1 vCPU | 2–4 vCPU |
| RAM | 512 MB | 2 GB |
| Storage | None on the container | Object storage sized to your media |
| Runtime | Docker | Docker |

libvips streams rather than buffering whole images, so memory stays modest and CPU decides throughput. AVIF encoding is the expensive case — raise `VIPS_AVIF_SPEED` or stay on WebP if requests queue up.

## Is Imagor Free to Self-Host?

Imagor is free and open source under Apache-2.0, with no paid edition and no feature held back — every filter, format and storage backend is in the public image. On Railway you pay for the compute the renderer uses and the storage your media occupies, so cost tracks traffic rather than image count.

## Self-Hosting Imagor

To try Imagor locally first, this starts an unsigned development server:

```
docker run -p 8000:8000 shumc/imagor -imagor-unsafe -imagor-auto-webp
```

Then open `http://localhost:8000/unsafe/400x400/raw.githubusercontent.com/cshum/imagor/master/testdata/gopher.png`. To run it as this template does — signed URLs against S3-compatible storage — pass the settings as variables:

```
docker run -p 8000:8000 \
  -e IMAGOR_SECRET=your-signing-key -e S3_FORCE_PATH_STYLE=1 \
  -e AWS_ACCESS_KEY_ID=... -e AWS_SECRET_ACCESS_KEY=... -e AWS_REGION=auto \
  -e S3_ENDPOINT=https://your-s3-endpoint \
  -e S3_LOADER_BUCKET=originals -e S3_RESULT_STORAGE_BUCKET=variants \
  shumc/imagor
```

Never set `-imagor-unsafe` on a public instance: it turns signature checking off and lets anyone run transformations through your server.

## FAQ

**What is Imagor?**
An open-source image processing server built on libvips. It resizes, crops, converts and filters images on demand, with the operations written into the request URL, in thumbor's syntax.

**What does this Railway template deploy?**
One public `imagor` service on the official `shumc/imagor` image, plus the `imagor-source` and `imagor-result` object storage buckets.

**Why does the template include two object storage buckets?**
Separating them lets you clear the derived cache without risking your originals: `imagor-result` holds only regenerable images, so emptying it costs a little CPU and nothing else.

**How do I generate a signed Imagor URL in my application?**
Compute an HMAC of the path following the signature segment, keyed with `IMAGOR_SECRET`, and encode it as URL-safe base64. The default algorithm is SHA1; `IMAGOR_SIGNER_TYPE` switches it to SHA256 or SHA512.

**Can Imagor process images from a URL instead of a bucket?**
Yes. Put a full image URL at the end of the path and the HTTP loader fetches it, caches the original in `imagor-source` and renders from there. Restrict the origins it may reach with `HTTP_LOADER_ALLOWED_SOURCES`.

**Does self-hosted Imagor support smart cropping and watermarks?**
Both, with no paid tier. Add `smart` to the path for content-aware cropping, and use the `watermark()` filter to composite another image.

**How do I scale Imagor for heavy traffic?**
The service stores nothing locally, so raise the replica count and every instance shares one result cache. Put a CDN in front too, since responses already carry a long `Cache-Control` max-age.


## Similar templates

- [Garage S3 Storage](https://railway.com/deploy/garage-s3-storage) — Ultra-light S3 server: fast, open-source, plug-and-play.
- [Redis](https://railway.com/deploy/redis-1) — Self Host Latest Redis with Railway
- [EasyImg](https://railway.com/deploy/easyimg) — Simple self-hostable Nuxt.js personal image hosting system.

Open this page in a browser: https://railway.com/deploy/imagor-image-server
