---
title: "Deploy EmulatorJS"
description: "Browser-based emulator for retro consoles, powered by RetroArch"
category: "Other"
url: https://railway.com/deploy/emulatorjs
---

# Deploy EmulatorJS

Browser-based emulator for retro consoles, powered by RetroArch

**[Deploy EmulatorJS on Railway](https://railway.com/template/emulatorjs)**

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

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

## Template content

### emulatorjs https://cdn.jsdelivr.net/gh/EmulatorJS/EmulatorJS@main/docs/Logo-light.png

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

## Documentation

# Deploy and Host EmulatorJS on Railway

EmulatorJS is a browser-based frontend for RetroArch: dozens of libretro emulator cores compiled to WebAssembly, wrapped in a touch-friendly player with save states, shaders, cheats and gamepad support. It covers roughly forty systems — NES, SNES, Nintendo 64, Game Boy through GBA, Nintendo DS, PlayStation, Sega through Saturn, Atari, Commodore, arcade boards and DOS — and every core runs on the client. Developers embed it to demo retro builds, archivists use it for interactive exhibits, and hobbyists use it to play their own cartridge dumps without installing anything. Upstream ships it as a library rather than a website, so no official container has ever existed.

This template gives you both halves. Deploy EmulatorJS on Railway and one `emulatorjs` service serves the complete library at `/data/` — every core, the minified bundle, the compression helpers and the localisations — with the CORS and `Cross-Origin-Resource-Policy` headers a page on another domain needs to load it. The same service serves a player at `/` where you pick a file from your own device and it starts. Nothing is uploaded and nothing is stored: no database, queue, object storage or volume, because save states and the core cache live in your browser's IndexedDB. Self-host EmulatorJS and your pages stop depending on the public CDN.

![Diagram of the single EmulatorJS service on Railway](https://res.cloudinary.com/rroe4rtk/image/upload/f_auto,q_auto/v1789607046/emulatorjs-architecture.webp)

## Getting Started with EmulatorJS on Railway

Click Deploy and wait for the build; it pulls the official release archive, so expect a few minutes and a roughly 330 MB image, almost all of it emulator cores. When it goes green, open the generated Railway URL. There are no accounts, no setup wizard and no default credentials — the page you land on is the product. Drag a game file onto the drop zone, or click it for a file picker. The system is guessed from the extension, so a `.nes`, `.sfc`, `.gba` or `.z64` file selects itself; anything ambiguous you pick from the System dropdown. Consoles needing firmware — PlayStation, Saturn, PC-FX — take a BIOS file in the second picker, again from your own device. Press Start, the core downloads from this deployment, and the game boots.

Move the mouse to reveal the control bar: restart, pause, save and load state, control settings, cheats, cache manager, screen capture and fullscreen. Open Settings to confirm the deployment is healthy — it names the core in use and shows **Threads: Enabled**, meaning the page is cross-origin isolated and the multithreaded cores such as PlayStation Portable and DOSBox Pure will run. To use this instance as a CDN elsewhere, point `EJS_pathtodata` at `https://your-app.up.railway.app/data/`.

![EmulatorJS player with a ROM chosen and the NES system detected](https://res.cloudinary.com/rroe4rtk/image/upload/f_auto,q_auto/v1789607048/emulatorjs-player-landing.webp)

![The 240p Test Suite menu running in the EmulatorJS NES core](https://res.cloudinary.com/rroe4rtk/image/upload/f_auto,q_auto/v1789607053/emulatorjs-nes-240p-suite.webp)

![EmulatorJS settings menu showing the fceumm core with threads enabled](https://res.cloudinary.com/rroe4rtk/image/upload/f_auto,q_auto/v1789607055/emulatorjs-emulator-settings.webp)

## About Hosting EmulatorJS

EmulatorJS solves a narrow problem well: running a real emulator in a web page without asking the visitor to install anything. Every core is WebAssembly executing on the client, so the server does no emulation work — it only hands out static files. Self-hosting is worth it for three reasons: you stop depending on a third party's CDN, you control the headers that unlock the multithreaded cores, and you serve the library from your own domain.

Key features:

- Around forty systems, each backed by a mainstream libretro core
- Save states, battery saves, rewind, fast-forward and slow motion
- Configurable keyboard and gamepad maps, plus an on-screen pad for mobile
- Shaders, screen capture and screen recording from the control bar
- Cheat entry, a cache manager for downloaded cores, and localisations

The architecture is deliberately flat. The `emulatorjs` service runs Caddy over the extracted release and splits traffic three ways: `/data/*` is the library, sent with `Access-Control-Allow-Origin: *` and `Cross-Origin-Resource-Policy: cross-origin`; `/healthz` is an anonymous liveness route; everything else is the player page, sent with `Cross-Origin-Opener-Policy` and `Cross-Origin-Embedder-Policy`.

## Why Deploy EmulatorJS on Railway

Railway suits a static, header-sensitive workload like this one:

- Its edge passes the cross-origin isolation headers through unchanged, so the threaded cores work with no extra configuration
- One service, no database and no volume to manage or back up
- HTTPS and a public domain are issued automatically
- Push to the source repository and the image rebuilds
- Caddy sizes its worker pool from the container's CPU quota on its own

## Common Use Cases

- Publishing playable builds of a homebrew or indie retro game from your own site
- A self-hosted EmulatorJS CDN so several projects share one library origin
- Museum, classroom and conference kiosks where visitors cannot install software
- Personal browser play of cartridge dumps you made from hardware you own

## Dependencies for EmulatorJS

- `caddy:2-alpine` — the web server, for its per-path header blocks and cgroup-aware worker sizing
- The official [EmulatorJS](https://github.com/EmulatorJS/EmulatorJS) release archive, currently 4.2.3, carrying the minified bundle and every prebuilt core
- Image source: [gridalpha/emulatorjs-railway](https://github.com/gridalpha/emulatorjs-railway)

### Environment Variables Reference

| Variable | Default | What it does |
|---|---|---|
| `PORT` | `8080` | Port Caddy listens on; Railway sets it |
| `AUTH_USER` | unset | Username for optional HTTP basic auth |
| `AUTH_PASSWORD` | unset | Password for optional basic auth; the hash is derived at boot |
| `EJS_VERSION` | unset | Pin a specific EmulatorJS release; unset tracks the newest stable one |

Basic auth protects the player page **and** `/data/`, so other origins can no longer load the library. Leave both unset if this deployment is meant to serve other sites.

### Deployment Dependencies

- Core list: 
- Upstream repository: 
- The public CDN this replaces: 
- Caddy image: 

## Hardware Requirements for Self-Hosting EmulatorJS

Emulation runs on the visitor's machine, so the server only moves bytes.

| Resource | Minimum | Recommended |
|---|---|---|
| CPU | 0.5 vCPU | 1 vCPU |
| RAM | 256 MB | 512 MB |
| Storage | 400 MB image | 400 MB image, no volume |
| Runtime | Caddy 2 on Alpine | Caddy 2 on Alpine |

The client side is the demanding half: Nintendo 64, Saturn, PSP and DOS cores want a modern desktop browser, and the last two need the cross-origin isolation headers this template sends.

## Self-Hosting EmulatorJS

To run the same image locally, clone the source repository and build it:

```
git clone https://github.com/gridalpha/emulatorjs-railway
cd emulatorjs-railway
docker build -t emulatorjs .
docker run --rm -p 8080:8080 -e PORT=8080 emulatorjs
```

To embed the library in a page of your own instead of using the bundled player, point EmulatorJS at this deployment's `/data/` path. The minimal HTML is:

```
<div id="game"></div>


```

## Is EmulatorJS Free to Self-Host?

EmulatorJS is free and open source under GPL-3.0, with no paid tier, licence key or usage cap, and takes optional support through Patreon. The bundled libretro cores carry their own open-source licences. Self-hosting costs only the Railway resources used: because the server does no emulation, that is a small always-on container plus bandwidth for core downloads, which browsers cache after the first launch.

## FAQ

**What is EmulatorJS?**
A JavaScript and WebAssembly frontend for RetroArch that runs retro console emulators inside a web browser, with a ready-made player UI, save states and gamepad support.

**What does this Railway template deploy?**
One service, `emulatorjs`, running Caddy over the official EmulatorJS release: the complete library at `/data/` for other sites to consume, and a browser player at `/`.

**Does this template include any games?**
No. No ROMs and no console BIOS files are included, downloaded or stored, and the deployment has no upload endpoint and no storage. You supply files from your own device, and only files you are entitled to use.

**Why is there no database or volume in this template?**
EmulatorJS keeps everything client-side. Save states, battery saves and the core cache live in the browser's IndexedDB, so the server is stateless and a redeploy loses nothing.

**How do I run the PlayStation Portable or DOS cores in self-hosted EmulatorJS?**
They are compiled with threads and need `SharedArrayBuffer`, which requires the page to be cross-origin isolated. This template already sends the two headers that do it; the Settings menu shows *Threads: Enabled* when it is working.

**Can I use this deployment as a CDN for EmulatorJS on my own website?**
Yes — that is what `/data/` is for. Set `EJS_pathtodata` to `https://your-app.up.railway.app/data/` and load `loader.js` from the same host. Leave `AUTH_USER` unset, since basic auth also closes those paths.


## 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/emulatorjs
