The USB stick my cousin handed me at a family gathering had a folder called ROMS on it, and inside was my entire childhood filed the way chaos files things, names like Super Mario Bros (J) [!].zip sitting next to a file called final (2) (1).gba, four hundred games across nine consoles with no covers and no order and no way to find the one game I wanted to show him. I had the hardware to play all of it, emulators on every device I own, and what I did not have was a library, because a library has shelves and labels, and what I had was a shoebox of cartridges with the labels torn off.
RomM is the shelf, a self-hosted ROM manager and player, twelve thousand stars and a Hacker News front page to its name, that scans a folder of game files and turns it into something beautiful, covers and screenshots and descriptions pulled from IGDB and Screenscraper, platforms organized like console shelves, filters by genre and region and tag, and then the part that makes everyone gasp when they see it, you click a game and it plays right there in the browser, because EmulatorJS is embedded in the app, no install, no emulator configuration, my cousin was playing on the sofa with his phone thirty seconds after I sent him a link.
The legal paragraph first, because this corner of self-hosting deserves honesty, game files sit in a gray zone that depends entirely on where you live and what you own, and the position I take is the one the community generally respects, dump your own cartridges, keep the library to yourself, and treat the public ROM sites as the risk they are, and I am not your lawyer, so the laws where you live apply to you. What I can say is that the hardware I dumped from was mine in 1997 and is still mine now, a cheap cartridge dumper turned a shoebox of physical games into files one rainy weekend, and the collection that resulted is legally mine to put on my own shelf.
The compose file is the app and its database, and the env file carries the metadata credentials.
volumes:
mysql_data:
romm_resources:
romm_redis_data:
services:
romm:
image: rommapp/romm:latest
restart: unless-stopped
ports:
- "8080:8080"
environment:
DB_HOST: romm-db
DB_NAME: romm
DB_USER: romm-user
DB_PASSWD: use-a-real-password
ROMM_AUTH_SECRET_KEY: generate-with-openssl-rand-hex-32
SCAN_WORKERS: 2
volumes:
- romm_resources:/romm/resources
- romm_redis_data:/redis-data
- ./library:/romm/library
- ./assets:/romm/assets
- ./config:/romm/config
depends_on:
romm-db:
condition: service_healthy
romm-db:
image: mariadb:latest
restart: unless-stopped
environment:
MARIADB_ROOT_PASSWORD: use-a-real-password
MARIADB_DATABASE: romm
MARIADB_USER: romm-user
MARIADB_PASSWORD: use-a-real-password
volumes:
- mysql_data:/var/lib/mysql
The library volume is your game folder mounted read-write, resources is where the fetched covers and screenshots land, and assets holds browser saves and states, which means a game started on the laptop in the evening resumes from the same save on the phone the next morning, a trick that took the big console companies a decade to ship. The folder structure inside library is one folder per platform named the way RomM expects, n64 and gba and psx and so on, and the scanner reads the files, matches them against the metadata providers, and renames nothing, so your original files stay untouched while the shelf assembles itself.
Metadata needs free accounts, Screenscraper for the richest retro coverage and RetroAchievements if you want the achievement layer that turns childhood games into something with progress bars, and the first full scan of four hundred games took about ten minutes with covers streaming in behind it. Multi-user support means my cousin has a limited account that can play but not delete, and there are official apps for Playnite and Android handhelds, so the Anbernic sitting in a drawer now pulls its library from my server instead of hoarding its own copies.
The gotchas were small, the auth secret key generates with openssl rand -hex 32 and matters because it signs the login sessions, browser emulation wants a real GPU for anything past the 16-bit era so the N64 and PlayStation games play better on devices with actual emulators, and the scanner occasionally mismatches a badly named file, which the interface lets you fix by searching the metadata providers manually.
The shoebox became a shelf, my cousin spent the evening working through games older than his parents’ marriage, and the save states sync between my devices like magic that has no right to be this easy. The console of my childhood is a URL now, and it lives in the same rack as everything else I decided not to rent.
If the games of your childhood deserve a shelf, subscribe to the newsletter.