RomM: a self-hosted ROM manager that scans, enriches and plays your collection
A beautiful, powerful, self-hosted rom manager and player. Overview RomM (ROM Manager) allows you to scan, enrich, browse and play your game collection with a clean and responsive interface.
At a glance
- What is it?
- RomM indexes a ROM folder tree, pulls metadata from IGDB, Screenscraper, MobyGames and SteamGridDB, and plays games in the browser through EmulatorJS. It is AGPL-3.0, Python on the backend, and the README points at the docs for installation rather than shipping a one-liner.
- Who is it for?
- Adopt RomM if you already keep ROMs in a folder tree and want a browser front end with metadata, tags and in-browser play, and if you can run Docker and a MariaDB or Postgres instance alongside it. Skip it if you need a native desktop library manager, or if you want a single binary with no database.
- Can I use it commercially?
- Yes, with strict conditions. AGPL-3.0 is a network copyleft licence: if people use a modified version over a network, for example as a hosted service, you must offer them its source code under the same licence.
- Is it still maintained?
- Yes. The repository last received commits 3 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 27, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What RomM solves for people with a ROM folder and no front end
A ROM collection on disk is a directory tree and nothing else. There is no cover art, no release year, no way to tell a European revision from a US one without opening a filename, and no way to hand a friend a link to a specific game. Emulator frontends solve this on a single machine, tied to that machine's emulator configuration. RomM takes the other position: the library lives on a server, the interface is a web page, and any device with a browser can browse it.
The README describes the scope as scanning, enriching, browsing and playing a game collection. The target reader is someone running emulators across more than one device, or someone who wants a shared library rather than a per-machine setup. The feature list names 400+ platforms with metadata, multi-disk games, DLCs, mods, hacks, patches and manuals, and filename tag parsing. That is a library-management scope, not an emulator scope. RomM does not emulate on the server; the browser player runs the ROM client-side through EmulatorJS and RuffleRS.
The repository is AGPL-3.0-only, and the last push was on 2026-08-20, with 5.2.0 released the same day. Nothing in the repository metadata says archived.
How the scan, enrich and play pipeline is wired
The backend is a FastAPI application, and pyproject.toml lists SQLAlchemy with MariaDB, MySQL and PostgreSQL connectors, Alembic for migrations, Redis for the queue, and rq as the worker. That combination tells you the shape of the thing: a web API, a relational database holding the library index and metadata, and a background worker doing the slow work of scanning and fetching.
Scanning is the entry point. RomM walks the ROM directory you mount into the container, matches filenames against known naming schemes, and creates library entries. Enrichment is a separate step that calls out to external metadata providers: IGDB, Screenscraper and MobyGames for game data, SteamGridDB for artwork, Retroachievements for achievement data. Tag parsing happens at scan time from the filename, which is why the docs have a dedicated folder-structure and tag-support page rather than treating naming as a free-for-all.
Playback is the part that surprises people. EmulatorJS and RuffleRS are browser runtimes shipped as static assets. The Dockerfile pins them explicitly, with EMULATOR_ASSETS_DIR set to /opt/romm/emulators and an EMULATORJS_VERSION build argument. A comment in the Dockerfile explains why they live outside /app/frontend: the bind mount would hide them, so entrypoint.sh links them into the assets tree at startup. That is a real deployment detail, and it means the browser player is tied to how the container is assembled, not to a runtime download.
Installing RomM with Docker and running a first scan
The README does not give install commands. It says to check the Quick Start Guide in the docs, and points at a troubleshooting page for scanning issues. The repository does ship a docker-compose.yml, but read its header first: it says to see the full example under examples/docker-compose.example.yml. The compose file in the repository root is a development stack, not a production one. It builds the image from the local Dockerfile, mounts ./backend and ./frontend as bind mounts, and runs /bin/bash as its command with stdin and tty open. That is a developer shell, not a service.
So the honest first step is to open examples/docker-compose.example.yml and the Quick Start Guide. What the root compose file does show is the dependency set you will need in any deployment: a database and a Redis-compatible cache. The dev stack uses these two images.
romm-db-dev:
image: mariadb:12.3.3
environment:
- MARIADB_ROOT_PASSWORD=${DB_ROOT_PASSWD:-rootpassword}
- MARIADB_DATABASE=${DB_NAME:-romm}
- MARIADB_USER=${DB_USER:-romm}
- MARIADB_PASSWORD=${DB_PASSWD:-romm}
romm-valkey-dev:
image: valkey/valkey:9.0.6The application container reads its configuration from a .env file, and env.template exists at the repository root as the starting point. The dev service sets REDIS_HOST, DB_HOST and ROMM_BASE_PATH explicitly, and publishes the backend on port 5000 by default via ${DEV_PORT:-5000}. Ports 3000 and 5173 are the Vite dev servers, 8443 is an HTTPS dev server, and 5678 is debugpy. None of those belong in a production deployment.
A first real use, once your instance is up, is to point it at a ROM directory, let the scan finish, and then check the tag filters. The README states that RomM parses and filters by tags in filenames, and links a tag-support page in the folder-structure docs. If your filenames do not follow a scheme the scanner recognizes, the library will come back thin, and the troubleshooting page for scanning issues is the place the README sends you.
Where RomM gets awkward: metadata providers, storage and the browser player
The metadata is not self-contained. IGDB, Screenscraper, MobyGames, SteamGridDB and Retroachievements are all external services, and the README links each to a metadata-provider page in the docs. That means enrichment depends on credentials you have to obtain, and on rate limits and availability you do not control. A library that scans cleanly can still come back with placeholder art because a provider rejected a request. The README does not describe offline enrichment or a bundled metadata dump, so plan for the network dependency.
Resource use is the second constraint. You are running a web API, a relational database, a Redis-compatible cache and at least one background worker. On a small NAS or a low-power home server, that is a heavier footprint than a desktop frontend that reads a local config file. The dev compose file also mounts an empty directory over /app/backend/romm_mock and another over /app/frontend/node_modules, which is normal for development but a reminder that the container expects specific mount points.
The browser player is the third. EmulatorJS runs the emulated system inside the browser tab, so performance depends on the client device, not the server. A phone browser will not play a demanding system the way a desktop will. And because the emulator assets are pinned at build time in the Dockerfile, upgrading the player means rebuilding or pulling a new image, not flipping a setting. The README does not document rollback for a version upgrade.
RomM compared with Retrom, which the README lists as a friend project
The README's friends section names Retrom as "a centralized game library/collection management service" and Gaseous as "another ROM manager with web-based emulator." Those two labels describe the split cleanly.
Retrom is the closer comparison. Both centralize a library, but Retrom's description stops at management, while RomM's README leads with playing games in the browser through EmulatorJS and RuffleRS. If you want a service that catalogs and serves your collection to native clients, the two overlap. If in-browser play is the feature you actually want, RomM is the one whose README claims it. Gaseous is the other direction: also a web-based emulator, but the README presents it as a separate project rather than something RomM wraps.
There is also a client ecosystem worth knowing about. The README lists official apps for Playnite, Android (Argosy) and CFWs (Grout), plus community projects including an iOS app, RetroArch Sync, DeckyRommSync for SteamOS, a Switch homebrew NRO, and a Syncthing sync tool. The README is explicit that the team does not regularly review community project source code, so treat those as third-party integrations. If your workflow is RetroArch on a handheld, the RetroArch Sync entry is the relevant one, and it is community-maintained.
Licence and the cost of keeping RomM current
RomM is AGPL-3.0-only, per pyproject.toml and the LICENSE file. The practical implication for most self-hosters is nil: you run it, you do not distribute it. If you plan to modify RomM and expose it as a network service to other people, the AGPL's network clause is the part to read, and that is a question for a lawyer rather than for this article.
The upgrade cost is the more concrete concern. Releases listed in the repository are 5.2.0 on 2026-08-20, with 5.1.1-beta.2 on 2026-08-16 and 5.1.1-beta.1 on 2026-08-02. Beta tags are being published alongside stable ones, so if you track a moving tag you can land on a beta. Pin an explicit version.
Because Alembic is a dependency, schema migrations are part of the upgrade path, and they run against your database on startup. That makes a database backup the thing to take before pulling a new image, not after. The README does not document rollback, so a downgrade after a migration is not something the project promises to support. The Dockerfile pins EmulatorJS to a specific version with a SHA256 check and a comment telling maintainers to keep that pin in sync with the emulator stage of docker/Dockerfile, which is a sign the browser player and the application image are versioned together.
Editorial conclusion
Adopt RomM if you already keep ROMs in a folder tree and want a browser front end with metadata, tags and in-browser play, and if you can run Docker and a MariaDB or Postgres instance alongside it. Skip it if you need a native desktop library manager, or if you want a single binary with no database. Before committing, verify that your folder naming matches the tag and folder-structure rules in the docs, check which metadata providers you can actually authenticate against, and read the quick start guide rather than improvising a compose file.
Frequently asked questions
Can I use RomM with RetroArch?
Not directly through RetroArch itself. The README lists RetroArch Sync as a community project that syncs a RetroArch library with RomM, and notes that the RomM team does not regularly review community project source code.
What are the differences between Retrom and RomM?
The README describes Retrom as a centralized game library and collection management service, while RomM's own feature list includes playing games directly from the browser using EmulatorJS and RuffleRS. The README lists Retrom under its friends section rather than as a direct equivalent.
How do I set up the RomM app?
The README does not include install steps. It directs readers to the Quick Start Guide in the docs, and the repository ships examples/docker-compose.example.yml, which the root docker-compose.yml header points to for the full example.
How do I update RomM?
The README does not document an update procedure or a rollback path. Because Alembic is a dependency, migrations are part of the application, so it is worth taking a database backup before changing versions and pinning an explicit release rather than a moving tag.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/rommapp-romm)