---
title: "Deploy imgproxy"
description: "Image server that resizes and converts images straight from a URL"
category: "Other"
url: https://railway.com/deploy/imgproxy-cache
---

# Deploy imgproxy

Image server that resizes and converts images straight from a URL

**[Deploy imgproxy on Railway](https://railway.com/template/imgproxy-cache)**

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

- **Creator:** A3A
- **Category:** Other

## Template content

### imgproxy https://raw.githubusercontent.com/imgproxy/imgproxy/master/logo.svg

- **Image:** darthsim/imgproxy:latest
- **Health check:** /health

### cache https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/nginx.svg

- **Source:** https://github.com/gridalpha/imgproxy-cache-railway
- **Health check:** /healthz
- **Public domain:** Yes

## Buckets

- **imgproxy-images**

## Documentation

# Deploy and Host imgproxy on Railway

imgproxy is a standalone image server that resizes, crops, converts and optimises images the moment they are requested. You keep one original — in object storage or on any host reachable over HTTP — and ask for a derivative by putting the transformation in the URL: a 400×400 smart crop, a blurred placeholder, an AVIF for browsers that accept one. Nothing is pre-generated, nothing is written back, and the URL is signed so only your application can request a transformation.

Deploy imgproxy on Railway and this template gives you two services rather than one. **imgproxy** does the processing on the private network with no public address of its own. **cache** is an nginx reverse-proxy cache in front of it, holding rendered images on a Railway volume and serving repeats without touching imgproxy again — imgproxy's internal cache is a paid feature, so an external cache is what upstream recommends for the open-source build. A managed object-storage bucket is wired in as an `s3://` source, and only the cache service is published, so every request enters through it and is answered from disk from the second hit onward.

![Diagram of the nginx cache and imgproxy services on Railway](https://res.cloudinary.com/rroe4rtk/image/upload/v1788490991/imgproxy-architecture.png)

## Getting Started with imgproxy on Railway

There is no sign-up, no admin panel and no first user to create — imgproxy is an HTTP API, so the deploy is finished the moment the public URL answers. Open it and you should see imgproxy's own landing page, which confirms traffic reaches the processor through the cache. `IMGPROXY_KEY` and `IMGPROXY_SALT` are generated at deploy time, so every processing URL must carry a valid HMAC signature and an unsigned request is answered `403` — that is what stops the deployment being an open image proxy for strangers. Copy both values out of the imgproxy service's variables and sign URLs in your application. The signed path is everything after the signature: the options, then `/plain/` and the source URL. In Python:

```
import hmac, hashlib, base64

KEY  = bytes.fromhex("")
SALT = bytes.fromhex("")

path = "/rs:fill:400:400:ce/g:sm/plain/https://example.com/photo.jpg@webp"
sig  = hmac.new(KEY, SALT + path.encode(), hashlib.sha256).digest()
url  = "/" + base64.urlsafe_b64encode(sig).rstrip(b"=").decode() + path
```

Request that path against your public domain and you get a 400×400 WebP cropped around the most interesting part of the photo. To read from the bucket, upload an object and swap the source for `s3:///`. Watch the `X-Cache-Status` header while testing: the first request reads `MISS`, the second `HIT`.

![The imgproxy landing page served over the public cache domain](https://res.cloudinary.com/rroe4rtk/image/upload/v1788490993/imgproxy-landing.png)
![Four object-storage photos smart-cropped to squares by imgproxy](https://res.cloudinary.com/rroe4rtk/image/upload/v1788490995/imgproxy-bucket-gallery.png)
![One photo through six imgproxy resize, blur and rotate pipelines](https://res.cloudinary.com/rroe4rtk/image/upload/v1788490996/imgproxy-processing-options.png)

## About Hosting imgproxy

imgproxy makes every derivative a pure function of a URL and encodes to the best format the browser advertises. It is written in Go on top of libvips, which streams and shrinks on load rather than decoding whole images into memory, so a 2000×1400 JPEG becomes a thumbnail in tens of milliseconds on a small container.

- Resize, fit, fill, crop, pad, zoom, rotate, trim, blur and sharpen, all expressed in the URL path
- Smart gravity, which crops around the most salient region instead of the centre
- WebP, AVIF and JPEG XL negotiation from `Accept`, with `Vary: Accept` set correctly
- Sources over HTTP(S), S3-compatible storage, Google Cloud Storage, Azure Blob Storage and Swift
- URL signing, source allow-lists, resolution and file-size ceilings, metadata stripping by default
- Prometheus, OpenTelemetry, Datadog, New Relic and Sentry instrumentation built in

**imgproxy** holds the signing key and storage credentials and never receives traffic directly. **cache** terminates public requests, keys on the URL plus the `Accept` and client-hint headers, serves stale content while imgproxy redeploys, and collapses a burst of requests for one cold image into a single upstream call.

## Why Deploy imgproxy on Railway

Railway removes the fiddly parts of running an image tier:

- Private networking between the cache and imgproxy, with no public port on the processor
- A managed S3-compatible bucket wired in as a source, credentials injected as references
- A persistent volume for the rendered-image cache, so a redeploy does not start cold
- Health checks, restart policies and HTTPS on the public domain
- Scale by raising CPU: imgproxy sizes its worker pool from the container's quota

## Common Use Cases for Self-Hosted imgproxy

- **Product and user-generated media** — store one upload, serve every thumbnail and retina variant from it
- **Responsive front ends** — emit `srcset` entries differing only in the width, with AVIF where it is accepted
- **Editorial and CMS sites** — art-direct crops per breakpoint without re-cutting images by hand
- **Replacing an image CDN bill** — run the transformation API in your own region, on your own storage

## Dependencies for imgproxy

- `darthsim/imgproxy:latest` — the processing server, from [imgproxy/imgproxy](https://github.com/imgproxy/imgproxy)
- `gridalpha/imgproxy-cache-railway` — an `nginx:alpine` image carrying the cache configuration
- A Railway managed object-storage bucket, exposed to imgproxy as the `s3://` source scheme

### Environment Variables Reference

| Variable | Service | Purpose |
|---|---|---|
| `IMGPROXY_KEY` | imgproxy | Hex HMAC key; required for signed URLs |
| `IMGPROXY_SALT` | imgproxy | Hex HMAC salt; must be set alongside the key |
| `IMGPROXY_USE_S3` | imgproxy | Enables the `s3://` source scheme |
| `IMGPROXY_ALLOWED_SOURCES` | imgproxy | Optional allow-list of source URL prefixes |
| `IMGPROXY_UPSTREAM` | cache | `host:port` of the processing service |
| `CACHE_MAX_SIZE` | cache | Disk budget for cached images |
| `CACHE_TTL` | cache | How long a cached image stays fresh |

### Deployment Dependencies

- Source repository: [github.com/imgproxy/imgproxy](https://github.com/imgproxy/imgproxy) (Apache-2.0)
- Documentation: [docs.imgproxy.net](https://docs.imgproxy.net)
- Image: [darthsim/imgproxy on Docker Hub](https://hub.docker.com/r/darthsim/imgproxy)
- Cache image: [gridalpha/imgproxy-cache-railway](https://github.com/gridalpha/imgproxy-cache-railway)

## Hardware Requirements for Self-Hosting imgproxy

| Resource | Minimum | Recommended |
|---|---|---|
| CPU | 1 vCPU | 4–8 vCPU |
| RAM | 512 MB | 2–4 GB |
| Storage | none for imgproxy | 5–50 GB volume for the cache |
| Runtime | Docker | Docker |

imgproxy is CPU-bound. Its worker pool defaults to twice the cores it is allowed, read from the container's quota, so adding CPU raises throughput.

## Self-Hosting imgproxy with Docker

The published image needs no build step. To run it locally with signing on:

```
docker run -p 8080:8080 \
  -e IMGPROXY_KEY=$(openssl rand -hex 32) \
  -e IMGPROXY_SALT=$(openssl rand -hex 32) \
  darthsim/imgproxy:latest
```

To read from S3-compatible storage, add credentials and the source scheme:

```
docker run -p 8080:8080 \
  -e IMGPROXY_USE_S3=true \
  -e IMGPROXY_S3_ENDPOINT=https://your-endpoint.example \
  -e IMGPROXY_S3_REGION=auto \
  -e AWS_ACCESS_KEY_ID=... -e AWS_SECRET_ACCESS_KEY=... \
  darthsim/imgproxy:latest
```

Put a CDN or caching reverse proxy in front in production; without one, every request re-renders the image.

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

imgproxy is open source under Apache-2.0 and free to run, with no limit on images, requests or instances. A commercial imgproxy Pro tier adds advanced compression, dynamic watermarks, video thumbnails and an internal cache, from $49/month or $499/year for up to 16 workers. Self-hosting the open-source build on Railway costs infrastructure only.

## FAQ

**What is imgproxy?**
imgproxy is a fast, secure standalone server that processes images on the fly. It fetches a source image, applies the transformations encoded in the request URL, and returns the result without storing derivatives.

**What does this Railway template deploy?**
Two services and a bucket: the imgproxy processing server on the private network, an nginx reverse-proxy cache with a volume as the public entry point, and managed object storage wired in as an `s3://` image source.

**Why does the template include an nginx cache in front of imgproxy?**
imgproxy renders every request from scratch and its built-in cache is a Pro feature, so upstream recommends a CDN or caching proxy in front. The cache service is that layer, on a volume so redeploys start warm.

**Do I need the object-storage bucket?**
No. imgproxy fetches plain `http(s)://` sources, so you can point it at an existing origin and ignore the bucket. Official signing helper libraries exist for Ruby, PHP, Go, Node.js, Python, Java and .NET.

**Can I disable URL signing in self-hosted imgproxy?**
You can, by clearing `IMGPROXY_KEY` and `IMGPROXY_SALT`, but not on a public deployment: anyone could then make your server fetch and re-encode arbitrary images. Keep signing on, and add `IMGPROXY_ALLOWED_SOURCES` to restrict which origins it reads from.

**How do I know the cache is working?**
Request the same signed URL twice and read `X-Cache-Status`: the first response reads `MISS`, the second `HIT`. `CACHE_MAX_SIZE`, `CACHE_TTL` and `CACHE_INACTIVE` control how much is kept and for how long.


## 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/imgproxy-cache
