Deploy Openinary | (Just Updated) Cloudinary Alternative, Image and Video API with Admin Login, Data Survives Redeploys

Openinary: self-hosted image and video transform API, admin login preset

Deploy Openinary | (Just Updated) Cloudinary Alternative, Image and Video API with Admin Login, Data Survives Redeploys

Just deployed

/data

Deploy and Host Openinary on Railway

Built for an LLM agent to deploy and use. There is no setup form and no /setup page to open: the admin account is created from generated variables on first boot, and the contract below lists every address, variable name and one script that signs in, mints an API key, uploads an image and fetches a transformed copy.

Openinary is an open source, self-hosted alternative to Cloudinary. You upload an image or video once and request any size, crop, format or quality by editing the URL (/t/w_800,f_webp/photo.jpg); results are cached, and videos are transcoded through a background queue with ffmpeg.

About Hosting Openinary

This template runs one service: the official openinary/openinary v0.7.0 full-stack image, pinned by digest, with a volume on /data. The volume holds the SQLite database (users, sessions, API keys), the uploaded originals and the transformation cache, so all three survive redeploys. The image's nginx listens on a fixed port 3000; the start command moves it to Railway's injected PORT so the healthcheck and the public domain reach it.

The dashboard is behind a login. The admin user is admin@openinary.example.com and the password is OPENINARY_ADMIN_PASSWORD, generated per deploy. The account is created on the first boot only, while the user table is empty, so changing the variable later does not change the password; change it in the dashboard instead. A first API key is also printed once in the deploy logs, but you do not need to read logs: the script below signs in and mints a key over HTTP.

Files are stored on the volume. To use S3-compatible storage instead, add the STORAGE_* variables from the Openinary documentation to the service.

Agent contract

  • Public URL: OPENINARY_PUBLIC_URL (https:// plus the service's RAILWAY_PUBLIC_DOMAIN). The dashboard is at /, the API under /api.
  • Private URL: http://openinary.railway.internal:8080, exposed as OPENINARY_PRIVATE_URL.
  • Credentials: OPENINARY_ADMIN_PASSWORD (password of the admin user admin@openinary.example.com), BETTER_AUTH_SECRET and API_SECRET (server-side signing secrets; clients never send them).
  • Auth: sign in with POST /api/auth/sign-in/email (JSON email, password, header Origin equal to the public URL) to get a session cookie; with that cookie POST /api/api-keys/create returns an API key once. Every upload call then sends Authorization: Bearer API_KEY. Reads of transformed files (/t/...) are public.
  • Health: GET /health returns 200 without credentials.
  • API: POST /api/upload (multipart files, optional transformations), GET /t/TRANSFORMS/PATH, GET /api/storage, GET /api/api-keys/list, POST /api/invalidate.
# run with OPENINARY_PUBLIC_URL and OPENINARY_ADMIN_PASSWORD set
U="$OPENINARY_PUBLIC_URL"
curl -s -o /dev/null -w "health: %{http_code}\n" "$U/health"
curl -s -c /tmp/oi.jar -o /dev/null -w "sign-in: %{http_code}\n" -H "Origin: $U" -H "Content-Type: application/json" \
  -d "{\"email\":\"admin@openinary.example.com\",\"password\":\"$OPENINARY_ADMIN_PASSWORD\"}" "$U/api/auth/sign-in/email"
KEY=$(curl -s -b /tmp/oi.jar -H "Origin: $U" -H "Content-Type: application/json" -d '{"name":"agent"}' "$U/api/api-keys/create" | sed -n 's/.*"key":"\([^"]*\)".*/\1/p')
printf '%s' 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==' | base64 -d | tee /tmp/oi.png | wc -c
curl -s -H "Authorization: Bearer $KEY" -F "files=@/tmp/oi.png;filename=probe.png" "$U/api/upload"; echo
curl -s -o /dev/null -w "upload without key: %{http_code}\n" -F "files=@/tmp/oi.png;filename=probe.png" "$U/api/upload"
curl -s -o /tmp/oi.webp -w "transform: %{http_code} %{content_type}\n" "$U/t/w_64,f_webp/probe.png"

A fresh deploy prints health: 200, sign-in: 200, 70 (the probe image size), an upload response with "success":true, upload without key: 401 and transform: 200 image/webp.

Why Deploy Openinary on Railway?

Railway is a singular platform to deploy your infrastructure stack. Railway will host your infrastructure so you don't have to deal with configuration, while allowing you to vertically and horizontally scale it.

By deploying Openinary on Railway, you are one step closer to supporting a complete full-stack application with minimal burden. Host your servers, databases, AI agents, and more on Railway.

  • Agent-ready - generated admin password, contract block and one working script; no setup page.
  • Login on from the first request - the dashboard and the upload API are never public and unauthenticated.
  • State survives redeploys - users, API keys, originals and the cache live on one volume.
  • A working port - the image's fixed port 3000 is moved to Railway's injected port, so the healthcheck passes.
  • No per-image bill - transformations run on your own service instead of a metered media API.

Common Use Cases

  • Image CDN for an app - upload once, request resized and reformatted copies by URL.
  • Video thumbnails and transcodes - queue ffmpeg variants for uploaded videos.
  • Cloudinary migration - keep the URL-parameter model on infrastructure you own.

Dependencies for Openinary Hosting

  • One Railway volume on /data, created by the template.

Deployment Dependencies

Implementation Details

VariablePurpose
OPENINARY_ADMIN_PASSWORDPassword of the admin user, read on the first boot only. Generated per deploy.
BETTER_AUTH_SECRETSigns sessions and tokens. Generated per deploy.
API_SECRETSigns signed URLs and presigned uploads. Generated per deploy.
BETTER_AUTH_URLPublic base URL used for auth callbacks and CORS.
OPENINARY_PUBLIC_URLHTTPS address for clients outside Railway.
OPENINARY_PRIVATE_URLAddress on the private network, for services in the same project.

The start command folds /app/data, /app/apps/api/public and /app/apps/api/cache onto the volume with symlinks, moves nginx to PORT, sets the admin email and asks for an initial API key, then runs the image's own start sequence.


Template Content

More templates in this category

View Template
RustFS | (Just Updated) S3 Object Store, Public URL and Keys Set
S3-compatible object store, keys and public URL preset, volume kept

SuperSlowSloth
0
View Template
TimescaleDB
A PostgreSQL-based time-series database for analytics on Railway.

Rakit
1
View Template
Garage S3 Storage
Ultra-light S3 server: fast, open-source, plug-and-play.

PROJETOS
8