Tutorial  ·  June 2026

Deploy a Self-Hosted Screenshot API on Railway

What you'll have: A live Openkova screenshot API endpoint on Railway with automatic HTTPS, a custom domain, and persistent storage — deployed from the official Docker image in under 10 minutes. No API key required.

Prerequisites

Option A: Deploy from the Railway dashboard (recommended)

Step 1: Create a new project

In the Railway dashboard, click New Project → Deploy a Docker image. Enter the Openkova image:

ghcr.io/scnix-git/openkova:latest

Railway pulls the image and creates a service. It will show as failing until you add the required environment variables in the next step.

Step 2: Set environment variables

In your service settings, open the Variables tab and add:

CHROMIUM_PATH=/usr/bin/chromium
OPENKOVA_STORAGE_PATH=/data
PORT=3000

CHROMIUM_PATH tells Openkova where the Chromium binary lives inside the Docker image. OPENKOVA_STORAGE_PATH sets the directory for generated screenshots. PORT tells Railway which port to expose.

Step 3: Add a persistent volume

In the service settings, open the Volumes tab and click Add Volume. Set the mount path to /data. This persists generated screenshots across restarts and redeployments.

Without a volume, screenshots are stored in the container's ephemeral filesystem and lost on each restart.

Step 4: Deploy and get your URL

Click Deploy. Railway builds the service, assigns a public URL (https://your-service.up.railway.app), and provisions automatic HTTPS. Once the health check passes, your screenshot API is live.

Test it:

curl -X POST https://your-service.up.railway.app/api/convert/snippet \
  -H "Content-Type: application/json" \
  -d '{"html": "<h1 style=\"font-family:sans-serif;padding:40px\">Hello Railway</h1>", "format": "png"}' \
  --output test.png

Option B: Deploy via Railway CLI

If you prefer the CLI:

# Log in to Railway
railway login

# Create a new project
railway init

# Deploy the Docker image
railway up --image ghcr.io/scnix-git/openkova:latest

# Set environment variables
railway variables set CHROMIUM_PATH=/usr/bin/chromium
railway variables set OPENKOVA_STORAGE_PATH=/data
railway variables set PORT=3000

# Open the deployed service URL
railway open

Option C: Deploy from your own GitHub fork

Fork the Openkova GitHub repository, then connect it to Railway via New Project → Deploy from GitHub repo. Railway detects the Dockerfile and builds the image on every push to your default branch. This is the best option if you want to run a customised fork.

Add a custom domain

In service settings, open the Networking tab and click Add Custom Domain. Railway generates a CNAME record — add it to your DNS provider and Railway provisions the SSL certificate automatically.

# Example: point api.yourdomain.com to Railway
CNAME api your-service.up.railway.app

Memory and scaling

Headless Chromium uses approximately 150–300 MB per concurrent screenshot render. On the Railway Hobby plan ($5/mo), your services share a pool of 8 GB RAM — enough for a moderate-traffic screenshot API.

For high-concurrency workloads, Railway supports horizontal scaling (multiple replicas) and vertical scaling (reserved memory). A single Openkova instance handles approximately 5–15 concurrent screenshots comfortably on 512 MB reserved memory.

VolumeRecommended setupEstimated Railway cost
Low (<1K/day)Single instance, 256 MB~$5/mo
Medium (1K–10K/day)Single instance, 512 MB~$10–15/mo
High (>10K/day)VPS (DigitalOcean/Hetzner) is more cost-effective

Environment variable reference

VariableValueRequired
CHROMIUM_PATH/usr/bin/chromiumYes
OPENKOVA_STORAGE_PATH/dataYes (with volume)
PORT3000Yes
NEXT_PUBLIC_SITE_URLYour Railway URL or custom domainNo

Verify your deployment

Once deployed, confirm all three input modes are working:

BASE=https://your-service.up.railway.app

# HTML snippet → PNG
curl -X POST $BASE/api/convert/snippet \
  -H "Content-Type: application/json" \
  -d '{"html":"<h1>Test</h1>","format":"png"}' --output snippet.png

# URL → PNG
curl -X POST $BASE/api/convert/url \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com","format":"png"}' --output url.png

# HTML file → PDF
curl -X POST $BASE/api/convert/file \
  -F "files=@report.html" -F "format=pdf" --output report.pdf

Frequently asked questions

Can I deploy a screenshot API on Railway?

Yes. Openkova ships an official Docker image that deploys on Railway in under 10 minutes via New Project → Deploy a Docker image. Set CHROMIUM_PATH, OPENKOVA_STORAGE_PATH, and PORT as environment variables.

How much RAM does Openkova need on Railway?

Headless Chromium uses approximately 150–300 MB per concurrent screenshot. The Hobby plan ($5/mo) provides ample shared RAM for low-to-moderate traffic. Reserve 512 MB for consistent performance.

Does Railway persist files between deploys?

Not by default — containers have ephemeral filesystems. Add a Railway volume mounted at /data and set OPENKOVA_STORAGE_PATH=/data to persist screenshots.

Is Railway a good host for Openkova?

Railway is excellent for low-to-medium traffic — zero-ops, automatic HTTPS, easy custom domains, and usage-based billing. For high-volume screenshot generation (>10K/day), a dedicated VPS is more cost-effective.

More deployment options

Prefer a different platform? Openkova runs anywhere Docker runs.

Deploy on Fly.io →Deploy on Render →Docker Compose guide →