
我有段时间在 NAS 上最不想打开的目录就是“游戏备份”。里面塞了 N 个平台的文件命名混乱到只能靠内存认游戏。直到我把这些老游戏统一交给 RomM 管理以后NAS 才真正变成一个能让我舒服翻看的私人游戏库。虽然项目本身和 Steam 没有任何关系但它扫描、刮削、在线启动的整个体验非常接近一个属于自己的“Steam 库”。RomM 是一款自托管的 ROM 管理服务核心功能是把按平台分好类的游戏文件夹变成带封面、简介、版本信息和筛选能力的 Web 库。你不用装桌面模拟器不需要打开某个目录再手动选择核心只要浏览器能访问 NAS就能像逛街一样浏览游戏。这篇内容会从“为什么要用它”讲到具体的 Docker Compose 部署、ROM 文件整理、刮削器配置、在线游玩体验以及备份安全尽量贴合在实际 NAS 上操作的思路。1. 为什么我最终把老游戏库交给了 RomM1.1 当老游戏多到“不想打开”以前我的 NAS 目录大概长这样/volume1/games/GBA下是一堆game1.gba、mario_chn_0812.zip再往里翻还有中文版、英文版、修改版混在一起。某个周末想玩一款当年的战棋游戏文件名完全没有提示我只能一个个拖进模拟器试试了几次就不想玩了。这是很典型的“资源收藏多但整理成本高”的问题。NAS 本身提供了很大的存储空间但它不关心你存的是什么。电影可以交给 Jellyfin 去刮削封面和简介音乐可以交给 Navidrome 去整理游戏这一块在 RomM 出现之前一直是模糊地带。RomM 解决的就是这个它像一个老游戏专用媒体服务器。你给 RomM 一个目录它会扫描后识别平台和游戏再抓取元数据最终展示出海报墙。我几百个 GBA、GBC、PS 游戏文件不用再靠眼睛认。1.2 它和 RetroArch 那类模拟器前端不是一回事很多人会问RetroArch 不一样能做游戏库吗我也用过 RetroArch它在本地设备上确实很强但定位有明显不同。RetroArch 本质是模拟器前端它关心的重点是“怎么运行游戏”不是“怎么长期存放和陈列游戏”。RomM 正好反过来它先把游戏变成可以检索的媒体条目再把运行能力通过 EmulatorJS 接到浏览器上。你可以把 RomM 理解成 Jellyfin把 RetroArch 理解成 VLC——一个管媒体库一个管播放。用表格对比更清楚对比方向RomMRetroArch / ES-DE常驻位置跑在 NAS 上集中管理通常跑在客户端设备上访问方式浏览器局域网内任意设备需要安装客户端或连接电视/主机元数据刮削自动抓封面、简介、评分、厂商偏重运行配置很少做完整媒体墙游戏运行通过 WebAssembly 模拟器在浏览器中运行本机调用核心兼容性和控制更好适合场景多设备、多人访问的 NAS 用户单机、客厅游戏用户如果你只在一台 Windows 电脑上玩游戏那 RetroArch 完全够。可当文件长期躺在 NAS 里并且我想在 MacBook、手机、客厅小主机上随手打开时RomM 的方案明显更顺。1.3 先说清一个前提整理老游戏时我自己处理的都是早年用实体卡带、光盘备份下来的文件以及一些明确允许自由分发的开源和自制游戏。RomM 不会帮你变出游戏它只负责把已有的文件管好。我始终不建议把别人打包分享的内容直接做成公网可访问的资源站这不是技术问题是风险问题。2. RomM 的接线逻辑文件夹、数据库和刮削器如何配合2.1 三个角色文件系统、扫描器、展示界面要稳定使用 RomM必须先理解它的运行逻辑。它不是一个靠人工往网页里录入游戏的工具而是严格按照“扫描文件 → 识别平台 → 查询元数据 → 入库展示”这个链路工作的。第一步发生在文件系统层。你需要给 RomM 提供一个包含roms子目录的库目录里面按平台放不同文件。RomM 不会主动移动或重命名你的文件它只读取目录结构作为索引来源。第二步进入扫描器层。扫描器遍历每个平台目录把文件名转换为候选游戏标题再根据平台字段去匹配元数据。这里的平台很关键因为 RomM 先按目录名确定平台再到数据库里做筛选。第三步是展示层。匹配成功的游戏会获得封面、简介、评分、厂商、发售日期等信息最终展示在 Web 界面里。刮削下来的图片资源会存在专门资源目录文本数据写入数据库下次打开时不需要再重复请求在线数据库。2.2 RomM 默认不重命名你的文件这是很多 NAS 用户关心的问题。过去整理音乐时我习惯让工具把所有文件改成统一格式但 RomM 的设计思路不太一样。RomM 把游戏文件名作为“候选标题”然后把匹配关系记录在数据库里。也就是说即使文件名是mario_chn_0812.zip只要你在界面里手动把它修正成《Super Mario》下次进入时依然能正常显示。数据库里存的是文件和元数据之间的关联不是单纯靠文件名硬挂。当然文件名越规范扫描器解析出来的标题就越准后续需要手动修补的情况就越少。这也是我在后文单独讲文件整理的原因文件命名质量直接决定你是在“一次刮削完成”和“逐一修补两百个条目”之间选择。2.3 为什么需要一个数据库容器RomM 在数据量增长后单纯用目录或 JSON 存索引会很难维护。无论按平台筛选、按名称搜索、按评分排序还是记录每个游戏的匹配状态放在传统关系型数据库里是最顺的。这就解释了为什么推荐使用 Docker Compose 把 RomM 和数据库容器一起启动。只要你的 NAS 支持 Docker这套架构就能跑起来。有人会觉得“就管理几个老游戏用数据库是不是太大动干戈”但等你往库里塞了几千个游戏后会感谢当初这个决定。3. 在 NAS 上用 Docker 跑起来Compose 编排与首次启动3.1 先确认你的 NAS 能撑住什么RomM 本身并不吃资源它不像转码服务那样长期占用 CPU。以一个几百游戏的库为例扫描时的内存占用通常在几百 MB 到 1GB 左右平时空闲时只占很少后台资源。但这不等于任何 NAS 都能跑得舒服。你至少需要一个支持 Docker 的 NAS 系统群晖的 Container Manager、飞牛 NAS 的 Docker 应用、绿联 NAS 的容器管理都行核心还是 Docker Compose。如果 NAS 只有 ARM 芯片且内存只有 1GB建议还是别塞太多游戏网页加载和数据库查询都会明显变慢。我自己的机器是群晖路径习惯放在/volume1/docker/romm下面。目录结构大致是docker/romm/ ├── docker-compose.yml ├── romm-library/ │ └── roms/ ├── romm-resources/ └── romm-db/如果你用飞牛 NAS 或绿联路径换成自己的共享目录即可其余逻辑是一样的。3.2 Compose 配置参考RomM 的项目迭代比较快不同版本的镜像和环境变量偶尔会调整。我这里给一个贴近新版常用写法的配置同时建议你以官方文档的 compose 模板为准。先进入目录并创建docker-compose.ymlservices: romm: image: rommapp/romm:latest container_name: romm restart: unless-stopped ports: - 8080:8080 environment: ROMM_HOST: 0.0.0.0 ROMM_PORT: 8080 ROMM_DB_DSN: mysqlpymysql://romm:rommromm-db:3306/romm?charsetutf8mb4 ROMM_AUTH_SECRET_KEY: 这里改成一段至少32位的随机字符串 TZ: Asia/Shanghai volumes: - ./romm-library:/romm/library - ./romm-resources:/romm/resources depends_on: - romm-db romm-db: image: mariadb:10.11 container_name: romm-db restart: unless-stopped environment: MARIADB_ROOT_PASSWORD: 数据库root密码 MARIADB_DATABASE: romm MARIADB_USER: romm MARIADB_PASSWORD: 数据库romm用户密码 volumes: - ./romm-db:/var/lib/mysql这里关键点有三个第一./romm-library卷映射到容器里的/romm/library。你现有的游戏库要放进romm-library/roms这个子目录。如果你想引用 NAS 上已经存在的目录也可以改成/volume1/games:/romm/library只要里面能识别到roms子目录即可。第二./romm-resources用来保存刮削下来的封面和图片。它很重要之后备份时不能漏掉。第三数据库密码别设成一眼就能猜到的弱口令。如果你不愿让 RomM 容器直连数据库至少也要用独立密码不要用 root 密码去跑业务链接。建议用docker compose up -d启动。第一次启动时 Mariadb 初始化需要一点时间耐心等三十秒左右再访问http://NAS_IP:8080。3.3 首次启动常见的几个坑我在首次部署时遇到过三个问题如果你复现时卡住大概率也是这几类。第一个是数据库连接失败。Log 里会直接提示无法连接数据库原因是容器启动顺序没等数据库初始化好。解决方法是把depends_on加上或者启动后等一会再看页面多数情况不是配置密码错误是数据库还没就绪。第二个是端口冲突。8080 端口在我 NAS 上已经被其他服务占用导致 RomM 页面打不开。处理方式是宿主机端口改成 8081例如8081:8080访问时用 8081 即可。第三个是扫描结果为空。这个最常见通常不是 RomM 坏了而是库目录里忘了建roms子目录或者放进去了但权限不够。确认挂载目录后在容器内部能读到文件比不断重装容器更高效。排查时多用日志查看命令docker compose logs -f romm日志会把扫描错误、数据库连接错误、HTTP 请求异常都打出来定位问题会快很多。4. 让刮削一次成功的文件整理规范4.1 平台目录名决定了识别结果RomM 识别平台靠的是库目录里的第一层子目录。目录名不是随便起的它与平台的短标识符相关。比如nes、snes、gb、gbc、gba、n64、psx、psp这类命名都能被直接识别成对应主机平台。这里建议所有目录名统一小写。你在浏览器里看到的平台显示名由 RomM 根据数据库映射出来不需要你在目录名里写一大段完整名称。一个可参考的结构是romm-library/roms/ ├── gba/ │ ├── Advance Wars (USA, Europe).gba │ └── The Legend of Zelda - The Minish Cap (USA, Europe).gba ├── gbc/ └── snes/如果你的平台没有被识别先查一下项目官方平台列表再对照修改文件夹名。平台不对后面刮削全部会错位因为 RomM 是按平台查找游戏 ID 的。4.2 文件命名越接近 No-Intro 越好用 No-Intro 风格命名的一个巨大好处是它能同时表达“标题、地区、版本、语言”这几个关键信息。刮削器在解析文件名时能把这些信息拆开处理最终在界面里不仅显示游戏标题还能显示是美版还是欧版。我实测下来最稳的格式是游戏英文标题 (地区, 语言).扩展名例如Final Fantasy Tactics Advance (USA, Europe) (Rev 1).gba如果是汉化补丁过的版本我会把中文标注放在结尾例如Mother 3 (Japan) [Chinese].gba中文字符作为辅助信息通常不影响刮削因为 RomM 会根据英文部分去数据库搜索。但如果你全部用中文例如“最终幻想战略版 中文版.gba”刮削器大概率匹配不到因为 RomM 的元数据源以英文标题为主。4.3 多文件镜像和特殊格式怎么入库PS1、SS、PC Engine CD 这类平台的光盘镜像经常是一个游戏拆出多个文件常见的一堆.bin、.cue放同一个目录或压缩包里。RomM 处理起来最省心的方式是提前转成 CHD 或压缩成单个文件。比如一个 PS1 游戏是Party.cue加上三个.bin直接放根目录会让扫描器无从判断哪个是游戏主体。我更推荐用工具转成 CHD这样既能保留原始数据又能把一整个游戏收敛成一个文件扫描入库时干净利落。如果你有一个游戏的多个修改版也建议在文件名里标注清楚版本。比如“原版”“改版”“汉化版”用方括号放在文件名尾部这样在网页里能快速区分。4.4 入库前顺手做一次重复文件清理很多人收藏游戏时会在不同时期下载到同一份文件文件名不一样大小一样。RomM 本身不会在扫描时直接告诉你“这个和那个重复”所以入库前先在文件系统层做一次去重更聪明。我常用做法是先按平台目录统计文件大小把明显相同体积的候选文件用哈希校验工具比对保留命名更规范的那一个。这样刮削出来的库不会一半是重复条目看着也舒服。5. 元数据打磨刷成“商店页面”的经验与缺口5.1 先开 IGDB 数据源RomM 在刮削时依赖外部游戏数据库。早期默认会走 TheGamesDB它不需要密钥覆盖老游戏也还可以。但实际匹配效果参差不齐尤其遇到一些冷门平台时返回结果往往大而全但不精确。IGDB 的匹配质量通常更稳定。接入方式不复杂但需要去注册一个开发者应用拿到 Client ID 和 Secret再填到 RomM 的设置里。这一步虽然要几分钟但对一个几百游戏的库来说非常值得因为自动匹配成功率上去之后后面人工修补时间会大大减少。填入后建议先针对单个平台重新刮削一次作测试不要一上来对全库操作。比如先在gba平台上做一个“重新刮削全部文件”看命中率是否能接受。5.2 为什么有些游戏还是搜不到在国内使用 RomM 时最常遇到的挫败感不是 RomM 不好用而是游戏数据库普遍以欧美标题为基准。我收藏里有一批日版游戏文件名是罗马音直译搜不到准确结果的情况很常见。后来我把文件名改成 No-Intro 的英文标准标题匹配率立刻高了很多。还有一个经验是如果自动搜索返回了很多个候选结果不要急着选把年份、平台、地区三个字段对一下再确认否则封面和简介可能落到另一个同名重制版上。中文游戏名搜不到不要怪刮削器直接在网页里手动编辑标题即可。RomM 允许你修改显示名称所以补救成本不算高。5.3 手动修正和本地封面如果数据库里确实没有封面RomM 网页端是支持手动上传图片的。我会在 NAS 里给游戏做一个本地图片文件夹封面干净无字上传到对应游戏详情页。对于有多个修饰版游戏我会用本地封面统一加角标区分比如原版不标、汉化版右下角加个小字。这个操作听起来麻烦但你在首页扫一眼就能认出版本时会觉得所有整理都是值得的。手动修正过的条目在之后重新扫描时是否会被覆盖不同版本行为会有所不同。我的习惯是核心条目修好后就不轻易对整个平台再次全量刮削避免不小心把精心调好的数据冲掉。6. 即点即玩的在线模拟器体验能到什么程度6.1 它并不是云游戏RomM 在游戏详情页提供一个“开始游戏”或类似按钮点开后会在浏览器里直接启动模拟器。很多人误以为这是把 NAS 当服务器跑游戏然后把画面串流过来实际并不是。RomM 的做法是把游戏文件通过本地网络发送到浏览器端再由浏览器里的 WebAssembly 模拟器运行。也就是说游戏真正的算力消耗发生在你的客户端设备上NAS 只负责存储和传输文件。这意味着你的平板或电脑性能越强游戏体验越流畅。这同时也带来一个好处就算 RomM 容器所在 NAS 的 CPU 很弱只要网速和文件传输正常老游戏也能跑得起来。而那些追求极致延迟和兼容性的玩家仍然会把文件下载到本地用独立模拟器跑RomM 更适合快速体验和整理收藏。6.2 平台支持范围有边界RomM 内置的模拟器能力取决于它打包的 EmulatorJS 核心不同平台的模拟成熟度差异非常大。GB、GBC、GBA、NES、SNES 这类 8 位和 16 位平台通常表现稳定几乎点开就能玩。PS1 及其同类光盘平台的体验要看核心支持程度部分游戏花屏、卡顿都正常。N64、PSP 及更高规格平台的浏览器模拟更要降低预期能进游戏不代表能完整体验。我给自己定的使用边界是GBA、GBC、NES、SNES 在 RomM 里直接玩PS1 游戏先用 RomM 查看资料和封面真要通关时还是把文件拷到专门模拟器。把“展示库”和“主力游玩”分开反而更省心。6.3 手柄、存档和沉浸感在浏览器里玩游戏手柄支持是通过浏览器的 Gamepad API