--- type: reference description: "RomM (ROM Manager) is a self-hosted ROM library manager and player." tags: - dankserver - romm timestamp: 2026-09-03 --- # RomM RomM (ROM Manager) is a self-hosted ROM library manager and player. It scans a ROM collection, enriches it with metadata and cover art from multiple providers, and serves it through a clean web interface. RomM also supports browser-based emulation via EmulatorJS and native emulator streaming. **Host:** Dankserver — **Access:** `romm.franksdank.cloud` guarded by the Anubis → Authentik chain ([[Caddy]] routes it post-auth to `192.168.0.235:8085`) — **Docs:** https://docs.romm.app ## Facts | Item | Value | |------|-------| | Running image | `ghcr.io/rommapp/romm:latest` (rolling tag — no pin; the version claims on this page were true at research time; verify against the live container before relying on them) | | Port | 8085 (host) → 8080 (container) | | Database | MariaDB (default); PostgreSQL optional | | Cache / Queue | Redis or Valkey (required for task queue) | | Frontend | React SPA | | Backend | FastAPI / Node | | Metadata providers | IGDB, Screenscraper, MobyGames, Hasheous, Playmatch, LaunchBox, Flashpoint, HowLongToBeat, SteamGridDB, RetroAchievements, TGDB, ES-DE gamelist | | Emulator player | EmulatorJS (WebAssembly RetroArch cores) | | Emulator streaming | Native containers (PCSX2, Dolphin, Xemu) → browser | ## Architecture Current RomM releases require MariaDB plus Redis (or Valkey) for the task queue; this deployment runs **redis** (`REDIS_HOST=redis` in the live unit) not valkey. ```mermaid graph LR U[User] --> C[Caddy :80/:443] C --> R[RomM :8085] R --> DB[(MariaDB)] R --> Cache[(Redis)] R --> Lib[/romm/library] R --> Res[/romm/resources] ``` ## Configuration ### Environment Variables | Variable | Required | Description | |----------|----------|-------------| | `ROMM_USERNAME` | Yes | Initial admin username | | `ROMM_PASSWORD` | Yes | Initial admin password (set on first boot) | | `ROMM_AUTH_SECRET_KEY` | Yes | JWT signing key. Generate with `openssl rand -hex 32`. Replaces the old `ROMM_SECRET_KEY`. | | `DB_HOST` | Yes | MariaDB hostname (e.g. `mariadb`) | | `DB_NAME` | Yes | Database name (e.g. `romm`) | | `DB_USER` | Yes | Database user | | `DB_PASSWD` | Yes | Database password | | `REDIS_HOST` | Yes | Redis hostname (e.g. `redis`) | | `DISABLE_SETUP_WIZARD` | No | Set to `true` to skip the first-run wizard | | `OIDC_ENABLED` | No | Enable OpenID Connect authentication | | `OIDC_CLIENT_ID` | No | OIDC client ID | | `OIDC_CLIENT_SECRET` | No | OIDC client secret | | `OIDC_REDIRECT_URI` | No | OIDC redirect URI | | `STREAMING_BROKER_SECRET` | No | Secret for native emulator streaming | | `STREAMING_SAVE_TIMEOUT` | No | Timeout for save states during streaming | | `KIOSK_MODE` | No | Enable kiosk mode | | `ROMM_SESSION_SECURE_COOKIE` | No | Secure cookie flag for sessions | ### Metadata Provider Credentials These are set via environment variables or in `config.yml`: | Variable | Provider | Notes | |----------|----------|-------| | `IGDB_CLIENT_ID` / `IGDB_CLIENT_SECRET` | IGDB (Twitch OAuth2) | Create app at Twitch Developer Portal. Free tier: ~10 req/sec. | | `SCREENSCRAPER_USER` / `SCREENSCRAPER_PASSWORD` | Screenscraper | Account credentials (not API key). Free, community-driven. | | `MOBYGAMES_API_KEY` | MobyGames | Paid hobbyist API. Note: RomM recommends Screenscraper as free alternative. | | `RETROACHIEVEMENTS_API_KEY` | RetroAchievements | Hash-based achievements via bundled RAHasher binary. | | `STEAMGRIDDB_API_KEY` | SteamGridDB | Custom cover art via the search cover button. | | `HASHEOUS_API_ENABLED` | Hasheous | Set to `true` in the live unit env file. Free, hash-based, proxies IGDB data. Fast scans. | | `PLAYMATCH_API_ENABLED` | Playmatch | Set to `true`. Multi-provider hash matching (IGDB, SS, SteamGridDB, RA). | ### Config File RomM uses `config.yml` for scan settings, available through the admin UI (Scan Settings). Place the file at `/home/dank/Containers/romm/config/config.yml` on the host. Key scan config options: ```yaml scan: priority: metadata: - igdb - ss # Screenscraper artwork: - igdb - ss region: - us - eu - jp region_mode: prefer_rom_tags # or prefer_config media: - box2d - screenshot - manual gamelist: export: true # Export ES-DE compatible gamelist.xml ``` ## Deployment (Quadlet) Since 2026-09-01 the trio runs as repo-tracked quadlet units, deployed with `./scripts/deploy.sh dankserver --service romm` (any of `romm` `romm-mariadb`, `romm-redis` triggers the full ordered cutover): - Units: `dankserver/romm/quadlet/{romm,romm-mariadb,romm-redis}.container` → generated `romm.service` etc., enabled via the quadlet generator's `default.target.wants` (`is-enabled` reports `generated`). - Secrets: host-local `/home/dank/Containers/romm/env` (0600, 15 app vars — compose-provided keys only; image-baked vars like `HOME`/`PATH` must never enter it, they poison the podman client's service environment). - Network: `dankserver_appnet` with aliases `mariadb`/`redis` (replaces compose service discovery; `DB_HOST`/`REDIS_HOST` still resolve). - Publish: `8085:8080` (`PublishPort=`, see the seam test on published ports). ## Volumes The live unit mounts one root bind — `Volume=/home/dank/Containers/romm:/romm` — so `/romm/config`, `/romm/library`, `/romm/resources`, and `/romm/assets` all resolve inside a single host-side data root: | Container path | Host path | |----------------|-----------| | `/romm/*` (single root bind) | `/home/dank/Containers/romm/*` | | `/var/lib/mysql` (romm-mariadb) | `/home/dank/Containers/romm/mariadb` (mapped subuid owner — never chown) | | `/data` (romm-redis) | `/home/dank/Containers/romm/redis` (mapped subuid owner) | The cutover rescued RomM's anonymous `/romm` volume (assets, plus empty mountpoint stubs that shadowed nothing) into the data root before first unit start — the mechanism behind the original "RomM data wiped" incident is now structurally impossible: the units declare bind mounts only. ## Docker Compose (reference — retired 2026-09-01, superseded by the quadlet units above) ```yaml services: romm: image: ghcr.io/rommapp/romm:latest container_name: romm restart: unless-stopped environment: ROMM_USERNAME: "${ROMM_USERNAME}" ROMM_PASSWORD: "${ROMM_PASSWORD}" ROMM_AUTH_SECRET_KEY: "${ROMM_AUTH_SECRET_KEY}" DB_HOST: mariadb DB_NAME: romm DB_USER: "${ROMM_DB_USER}" DB_PASSWD: "${ROMM_DB_PASSWD}" REDIS_HOST: redis ports: - "8085:8080" volumes: - /opt/romm/library:/romm/library - /opt/romm/resources:/romm/resources - /opt/romm/config:/romm/config depends_on: mariadb: condition: service_healthy redis: condition: service_healthy mariadb: image: docker.io/mariadb:11 container_name: romm-mariadb restart: unless-stopped environment: MARIADB_ROOT_PASSWORD: "${MARIADB_ROOT_PASSWORD}" MARIADB_DATABASE: romm MARIADB_USER: "${ROMM_DB_USER}" MARIADB_PASSWORD: "${ROMM_DB_PASSWD}" volumes: - /opt/romm/mariadb:/var/lib/mysql healthcheck: test: ["CMD", "mariadb-admin", "ping", "-h", "localhost"] interval: 10s timeout: 5s retries: 3 redis: image: docker.io/redis:7-alpine container_name: romm-redis restart: unless-stopped volumes: - /opt/romm/redis:/data healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 10s timeout: 5s retries: 3 ``` ## Healthcheck ```yaml healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8080/health"] interval: 30s timeout: 10s retries: 3 ``` ## Gotchas - **First login overwrites env credentials.** The `ROMM_PASSWORD` env var sets the initial password. After first login, the admin password is controlled in the UI. The env var only applies on first boot. - **MariaDB and Redis are required.** RomM 5.2.0 no longer supports SQLite-only deployments. Both MariaDB and Redis must be running. - **5.3.0 breaking change (2026-09-21).** `filesystem.roms_folder` / `firmware_folder` are removed; the scan root must be expressed via `filesystem.structure` in `config.yml` (see the Config File section above). New env vars: `SCAN_WORKERS`, `WEB_SERVER_CONCURRENCY`, `DB_POOL_RECYCLE_SECONDS`. The rolling `:latest` tag will pick this up on the next pull — migrate `config.yml` before redeploying, or the scan root silently goes empty. - **Reverse proxy cache stale index.html.** Purge Caddy proxy cache after RomM version bumps to avoid serving a stale frontend after an upgrade. - **RAHasher build time.** Enabling RetroAchievements hashing increases the first image build. Skip unless you need achievement data. - **MobyGames is paid.** The hobbyist API has strict rate limits. Screenscraper is the recommended free alternative. - **OIDC requires additional services.** Authentik, Keycloak, or another OIDC provider must be running before enabling `OIDC_ENABLED`. ## See Also - [[Dankserver Stack]] - [[RomM-Scraping]] — metadata providers, cover art, naming conventions, emulator integration - [[Calibre-Web]] — sibling library web UI