---
title: "Deploy Thumbor"
description: "Image CDN that crops, resizes and filters photos from a URL"
category: "Other"
url: https://railway.com/deploy/thumbor-imaging
---

# Deploy Thumbor

Image CDN that crops, resizes and filters photos from a URL

**[Deploy Thumbor on Railway](https://railway.com/template/thumbor-imaging)**

Machine-readable deploy manifest (JSON, validated by TemplateCI): https://railway.com/deploy/thumbor-imaging/manifest.json

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

## Template content

### remotecv https://cdn.simpleicons.org/opencv/5C3EE8.svg

- **Source:** https://github.com/gridalpha/thumbor-railway
- **Health check:** /healthcheck

### Redis https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/redis.svg

- **Image:** redis:8.2
- **Start command:** `/bin/sh -c "rm -rf $RAILWAY_VOLUME_MOUNT_PATH/lost+found/ && exec docker-entrypoint.sh redis-server --requirepass $REDIS_PASSWORD --save 60 1 --dir $RAILWAY_VOLUME_MOUNT_PATH"`

### thumbor https://raw.githubusercontent.com/thumbor/thumbor/master/logo-thumbor.png

- **Source:** https://github.com/gridalpha/thumbor-railway
- **Health check:** /healthcheck
- **Public domain:** Yes

## Buckets

- **thumbor-storage**

## Documentation

# Deploy and Host Thumbor on Railway

Thumbor is an open-source imaging service that crops, resizes, filters and optimises images on demand, straight from a URL. Instead of generating every thumbnail ahead of time, you ask for `/300x200/smart/` and Thumbor fetches the original, finds the faces in it, crops around them and returns that image. Wikipedia, Globo.com, Square, Forbes and Vox Media run it in front of user-generated photography, where nobody can hand-crop a million uploads and a centred crop cuts heads off.

Self-host Thumbor on Railway with the production shape already wired up. The `thumbor` service answers requests on a public URL; `remotecv` is a separate OpenCV worker that runs detection off the request path, as upstream's own documentation asks; `Redis` carries the detection queue and the focal points the two share; and a managed bucket holds the fetched originals and the rendered results, so no service needs a volume. Every URL is HMAC-signed, so the deployment is not an open image proxy.

![Thumbor, remotecv and Redis services on Railway](https://res.cloudinary.com/rroe4rtk/image/upload/v1788974166/thumbor-architecture.png)

## Getting Started with Thumbor on Railway

Thumbor has no dashboard and no login — it is an HTTP API whose only credential is the URL-signing key. After deploying, open `https:///healthcheck`; it answers `WORKING` once the service is live. Copy the `SECURITY_KEY` value from the `thumbor` service's variables, since you need it to build any image URL. A request path is `///<source>`, where the signature is a URL-safe base64 HMAC-SHA1 of everything after it. The `libthumbor` package builds these in Python; equivalents exist for Node, Ruby, PHP, Java and Go.

The following Python snippet signs a URL with nothing but the standard library:

```
import base64, hashlib, hmac

KEY = "your-security-key"
path = "300x200/smart/raw.githubusercontent.com/thumbor/thumbor/master/example.jpg"
sig = base64.urlsafe_b64encode(
    hmac.new(KEY.encode(), path.encode(), hashlib.sha1).digest()
).decode()
print(f"https://your-domain/{sig}/{path}")
```

Open that URL and you get a 300x200 JPEG. Ask for `/meta/smart/` instead of a size to see the focal points as JSON, and put `/debug/` before a size to get the source with rectangles drawn on everything detected. Expect one quirk: the first `/smart/` request for an unseen image returns a centred crop and queues detection, so request it again for the smart crop. Unsigned `/unsafe/...` URLs are rejected with 400.

![Detected face and eye rectangles drawn over a portrait](https://res.cloudinary.com/rroe4rtk/image/upload/v1788974168/thumbor-smart-crop-debug.png)
![Grayscale and rounded-corner filters applied to a photo](https://res.cloudinary.com/rroe4rtk/image/upload/v1788974169/thumbor-grayscale-filter.png)
![Thumbor metadata endpoint listing detected focal points](https://res.cloudinary.com/rroe4rtk/image/upload/v1788974174/thumbor-meta-focal-points.png)

## About Hosting Thumbor

Thumbor serves one uploaded photo at a dozen sizes and aspect ratios with no designer in the loop. Self-host it when image URLs are part of the product, when the sources are private, or when a CDN's pricing stops adding up.

- Smart cropping driven by OpenCV face, eye and feature detection
- Resize, fit-in, flip, rotate and manual crop from the URL
- Filters for grayscale, blur, brightness, watermarking, rounded corners and format
- Automatic WebP and AVIF when the browser advertises support
- Pluggable loaders, storages, filters and detectors through a plugin API
- Signed URLs, so only holders of the key can request a transformation

The Railway architecture splits that across three services. `thumbor` validates the signature, fetches the source, transforms it and returns the result. `remotecv` takes detection jobs off a Redis queue, runs OpenCV against the source and writes the focal points back — detection is CPU-heavy, and doing it in the request blocks the server under load. `Redis` is both that queue and the shared store for those focal points. The bucket caches originals under `st/` and renders under `rs/`, so a repeat request is one object read.

## Why Deploy Thumbor on Railway

Railway removes the setup between you and a working imaging service:

- Thumbor, the detection worker, Redis and object storage deploy together in one click
- Managed Redis and a managed bucket, credentials wired in as references
- A public HTTPS domain with a certificate, no reverse proxy to configure
- Health checks on both the server and the worker
- Horizontal scaling for the image tier, since neither role needs a volume

## Common Use Cases

- Responsive images from user uploads, where a face-aware crop beats a centred one
- A self-hosted alternative to an image CDN, in front of S3, a bucket or any public origin
- Social preview and open-graph images at fixed sizes from editorial photography
- Format negotiation, sending WebP or AVIF to browsers that accept them, JPEG to the rest

## Dependencies for Thumbor

- `thumbor` — built from [gridalpha/thumbor-railway](https://github.com/gridalpha/thumbor-railway), layering the Redis, S3 and remotecv extensions onto `ghcr.io/thumbor/thumbor:7-py-3.13`
- `remotecv` — the same image with `ROLE=worker`, running the [remotecv](https://github.com/thumbor/remotecv) OpenCV worker
- `Redis` — `redis:8.2`, the detection queue and shared focal-point store
- Object storage — a managed bucket for source and result caching

### Environment Variables Reference

| Variable | Service | Purpose |
|---|---|---|
| `SECURITY_KEY` | thumbor | HMAC key every image URL is signed with |
| `ALLOW_UNSAFE_URL` | thumbor | `true` serves unsigned URLs; leave `false` |
| `ALLOWED_SOURCES` | thumbor | Hosts or regexes the loader may fetch |
| `DETECTION_MODE` | thumbor | `queued`, `local` or `none` |
| `MAX_WIDTH` / `MAX_HEIGHT` | thumbor | Dimension caps; `0` is unlimited |
| `STORAGE_EXPIRATION_SECONDS` | thumbor | Cache lifetime for originals and results |
| `UPLOAD_ENABLED` | thumbor | Upload endpoint has no auth; leave `false` |
| `DETECTOR_TIMEOUT` | remotecv | Seconds one detection job may run |

### Deployment Dependencies

- Source repository: [gridalpha/thumbor-railway](https://github.com/gridalpha/thumbor-railway)
- Upstream: [thumbor/thumbor](https://github.com/thumbor/thumbor), [thumbor/remotecv](https://github.com/thumbor/remotecv)
- Base image: `ghcr.io/thumbor/thumbor:7-py-3.13` (Python 3.13)
- Docs: [thumbor.readthedocs.io](https://thumbor.readthedocs.io/)

## Hardware Requirements for Self-Hosting Thumbor

| Resource | Minimum | Recommended |
|---|---|---|
| CPU | 1 vCPU | 4+ vCPU (encoding is CPU-bound) |
| RAM | 512 MB | 2 GB per service |
| Storage | None on the container | A bucket for the caches |
| Runtime | Python 3.10+ with OpenCV and Pillow | Python 3.13 |

Thumbor forks one process per core, so throughput scales almost linearly with CPU. Cap `MAX_WIDTH` and `MAX_HEIGHT` if your sources are unbounded.

## Self-Hosting Thumbor with Docker

The official image runs standalone, which is the fastest way to try the URL syntax locally:

```
docker run -p 8888:8888 ghcr.io/thumbor/thumbor:7-py-3.13 \
  thumbor -p 8888 -i 0.0.0.0 -l info
```

That serves an unsigned instance on `http://localhost:8888/unsafe/300x200/<img>`. Beyond a local try, install from PyPI with the extras that pull in OpenCV, Cairo and libcurl:

```
pip install "thumbor[all]" remotecv tc_redis thumbor-aws
thumbor-config &gt; thumbor.conf
thumbor -c thumbor.conf -p 8888 -l info
```

Set `SECURITY_KEY` and `ALLOW_UNSAFE_URL = False` there before exposing it.

## How Much Does Thumbor Cost to Self-Host?

Thumbor is free and open source under the MIT licence, and so are remotecv, the Redis storage adapter and the AWS extensions this template uses. There is no paid tier, no licence key and no per-image charge. On Railway you pay only for the compute, memory, Redis and object storage used — a few dollars a month for a small site, and the cost stops scaling with the number of images served.

## FAQ

**What is Thumbor?**
An open-source imaging service that crops, resizes, filters and optimises images on demand from a URL, using OpenCV to detect faces so automatic crops keep the important part of the frame.

**What does this Railway template deploy?**
Four pieces: the Thumbor HTTP service on a public domain, a remotecv detection worker, managed Redis for the queue and focal points, and a managed bucket for the caches.

**Why does the template include Redis and object storage?**
Redis is how the two share work: Thumbor pushes a detection job onto it and remotecv writes the focal points back under a key Thumbor reads. Object storage holds the originals and renders, so a repeat request skips the download and the re-encode — and being shared rather than a per-container disk, it lets the image tier run several replicas.

**How do I generate a signed Thumbor URL?**
Take everything after the signature in the path, compute an HMAC-SHA1 of it with your `SECURITY_KEY`, and URL-safe base64 the digest. `libthumbor` does it in one call, with equivalents for Node, Ruby, PHP, Java and Go.

**Why did my first smart crop come back centred?**
Detection runs asynchronously. The first request for an unseen image queues the job and serves a centred crop; every later request gets the face-aware crop. Set `DETECTION_MODE=local` to detect inside the request and skip the worker.

**Can I restrict which image sources Thumbor will fetch?**
Yes. Set `ALLOWED_SOURCES` to a comma-separated list of hostnames or regular expressions, and only those origins load. With signed URLs, that keeps the deployment from being used as a general-purpose proxy.


## Similar templates

- [Rocky Linux](https://railway.com/deploy/rocky-linux) — Hosted Rocky Linux 9 workspace with SSH and persistent storage. 🚀
- [Foundry Virtual Tabletop](https://railway.com/deploy/X5tR6G) — A Self-Hosted & Modern Roleplaying Platform
- [Letta Code Remote](https://railway.com/deploy/letta-code-remote) — Run a Letta Code agent 24/7. No inbound ports, just deploy.

Open this page in a browser: https://railway.com/deploy/thumbor-imaging
