Docker and Docker Compose deployment assets for RAChecker, the upstream RetroAchievements ROM scanner. This is an independent packaging repository, not the RAChecker source project. The image fetches the selected upstream revision during its build. This repository contains no RAChecker checkout, ROM library, credentials, or published image.
- Docker Engine with Docker Compose v2
- An x86-64 Linux Docker host for the bundled RAHasher executable
- Network access during image build to fetch the selected upstream revision
- An operator-controlled ROM directory only when optional ROM access is enabled
The service listens on container port 8088. The default host binding is
127.0.0.1:8088; change it explicitly when wider access is required.
Clone this repository, create the local environment file, and set the host ROM directory you want RAChecker to scan:
cp .env.example .envAt minimum, keep the supported RACHECKER_REF=v0.17.0 value and set
ROM_LIBRARY_PATH to an absolute host path. Add RA_USERNAME and
RA_API_KEY if authenticated RetroAchievements operations are required. Then
resolve and start the development deployment with the optional read-only ROM
mount:
docker compose -f compose.development.yml -f compose.roms.yml config
docker compose -f compose.development.yml -f compose.roms.yml up -d --build
docker compose -f compose.development.yml -f compose.roms.yml ps
docker compose -f compose.development.yml -f compose.roms.yml logs -f racheckerOpen http://127.0.0.1:8088. Stop the deployment without deleting persisted configuration:
docker compose -f compose.development.yml -f compose.roms.yml downThe development deployment persists RAChecker state under ./config. The ROM
directory is mounted read-only at /roms. Do not use down -v when operating
the production deployment if its named configuration volume must be retained.
Create a local environment file from the non-secret template:
cp .env.example .envSet RACHECKER_REF to the supported v0.17.0 tag or its matching commit
daa29b232313760353b857840808e920e9f77977. Floating branches, latest, and
other revisions are not supported. Optional runtime settings are:
RA_USERNAMEandRA_API_KEYfor RetroAchievements accessFILE__RA_API_KEYto loadRA_API_KEYfrom a readable runtime secret filePUID,PGID,TZ, andUMASKfor identity and process defaultsROM_LIBRARY_PATHonly when an optional ROM mount is explicitly enabled
Credentials and host-specific paths belong in the ignored .env file or an
external secret mechanism. They are not image build arguments, labels, or
committed values. The default Compose configuration has no ROM mount.
Build from the repository root with the selected immutable revision:
docker build --build-arg RACHECKER_REF=v0.17.0 -t rachecker-docker:v0.17.0 .The build records the resolved upstream commit in image metadata and fails if
the revision cannot be fetched or the application cannot be built. It also
installs the checksum-pinned official RAHasher 1.8.4 Linux x86-64 executable
behind a compatibility wrapper at /usr/local/bin/RAHasher and sets
RA_RAHASHER to that path. Archive extraction tooling remains confined to a
build stage and is absent from the final image.
The official Linux RAHasher can print a valid hash and still exit with status
1. RAChecker interprets that non-zero status as a hashing error and discards
the otherwise valid result. The bundled wrapper executes the untouched
official binary, passes through its stdout and stderr, and changes the exit
status to 0 only when stdout contains a standalone 32-character hexadecimal
hash. If no valid hash appears, the wrapper preserves the real exit status and
diagnostics. Signals are forwarded and the temporary stdout buffer is removed
on success, failure, or interruption. The fix is limited to the observed
process-contract mismatch; it does not manufacture hashes or hide genuine
RAHasher failures.
Validate and start the default development stack:
docker compose config
docker compose up -d --buildThe default compose.yml imports the development service model. Select the
production model explicitly:
docker compose -f compose.production.yml config
docker compose -f compose.production.yml up -d --buildInspect state and diagnostics with the same Compose project and file:
docker compose ps
docker compose logs -f racheckerThe production health check is the availability signal. If HTTP availability
is not reached within the health-check window, the scoped failure is
runtime.unhealthy: the service remains unhealthy, with state and logs
available. The service remains unhealthy with logs available, and no healthy
result should be assumed. Inspect the health output and service logs before
remediation. A healthy service is available at
http://127.0.0.1:8088 with the default host binding.
Stop without deleting persistent state:
docker compose stop
docker compose downAfter changing configuration or RACHECKER_REF, rebuild and recreate:
docker compose up -d --build --force-recreateKeep the build output, health state, and service logs when diagnosing failed fetch, build, startup, health, or shutdown operations.
/config is the stable persistence boundary for mutable RAChecker
configuration, SQLite data, metadata, and hash caches. Development Compose
uses explicit repository-local bind directories. Production Compose uses
explicit Compose-managed named volumes. Both are outside the image layer and
survive ordinary container recreation and image rebuilds.
In production, a bounded one-shot initializer applies the configured non-root
PUID and PGID to the named /config volume, defaulting to 1000:1000.
The long-running service then starts directly as that numeric non-root identity.
Changing PUID or PGID changes the effective writer identity. Initialization
changes only the declared writable /config path and receives only the CHOWN
capability required for that ownership handoff. The named /config volume is
written by the container's configured non-root application identity after
initialization.
The volume is not a promise that arbitrary host users can read or write its
contents.
Production /config ownership is the configured non-root PUID/PGID writer.
The selected Compose file is authoritative for the actual volume name and
service wiring. Do not delete development directories or production volumes
when their state is needed. docker compose down -v, docker volume rm, and
broad docker volume prune can destroy RAChecker state and require deliberate
operator confirmation.
/roms is an optional external ROM library. It is never copied into the image
or /config, and any configured bind mount is read-only. The host source must
be readable by the configured non-root identity; initialization does not change
its ownership or permissions. Enabling the mount without ROM_LIBRARY_PATH
fails Compose interpolation before container creation. An unreadable mount
fails visibly as runtime.roms_unreadable. Startup or scanning fails visibly;
permissions are not broadened automatically. Correct host permissions or
choose compatible PUID and PGID values.
The image includes Linux RAHasher for supported disc-image identification. RAHasher is executed from the read-only image and reads the operator-mounted ROM library without changing it. The packaged official artifact is x86-64, so other container architectures fail the image build instead of installing an incompatible executable.
Enable the read-only ROM mount explicitly by layering the override onto either Compose model:
ROM_LIBRARY_PATH=path/to/roms docker compose -f compose.development.yml -f compose.roms.yml up -d --build
ROM_LIBRARY_PATH=path/to/roms docker compose -f compose.production.yml -f compose.roms.yml up -d --buildThe override fails Compose interpolation when ROM_LIBRARY_PATH is missing.
The default and production models do not mount /roms until the override is
selected.
Persistent storage is not backup support. Automated backup is unsupported, and automated restore is unsupported. This repository supplies no retention, snapshot, or migration procedure and makes no claim that an external copy is restorable. If an operator uses external storage tooling, stop the service and wait for quiescence first, preserve the original, and validate the restored application independently. Never copy live SQLite or cache files, restore over the active volume, or delete the original as part of an unverified operation.
Containers do not update upstream source automatically. To upgrade, update the
supported pinned release and its resolved commit in the image contract, select
that release through RACHECKER_REF, then rebuild and recreate the service.
Keep the existing /config storage. The selected upstream revision defines
state-format compatibility and any required migration; this repository does
not migrate or rewrite application state.
If the new revision is unhealthy or cannot read existing state, preserve the
storage, inspect docker compose ps and docker compose logs rachecker, and
resolve the issue before any destructive storage operation. The container ROM
path remains /roms regardless of the host source.
scripts/rachecker-mismatch-report.py
is a dependency-free Python 3 CLI for reviewing RAChecker records whose status
is no_match. It never reports records already marked match or error, but
reads matched game IDs so a no-match duplicate is suppressed when the collection
already contains a supported ROM for the same RA game.
For each no-match file it searches RAChecker's local game API, requires the
same console and an achievement-bearing game, rejects weak or ambiguous title
guesses, and retrieves RAChecker's authoritative supported ROM names, MD5
hashes, labels, and patch URLs.
Run it on a host that can reach RAChecker and can see the same ROM tree. The
--rachecker-rom-mapping value is the local directory corresponding to the
container's /roms; --roms may be that directory or one of its
subdirectories:
python3 scripts/rachecker-mismatch-report.py \
--rachecker http://127.0.0.1:8088 \
--roms /srv/roms \
--rachecker-rom-mapping /srv/romsHuman-readable text is the default. It is sorted by console, lists each local
incompatible ROM, identifies the likely RA game, and shows every exact
supported ROM filename and MD5. Patch links are included when RAChecker
provides them. Colour is automatic for terminals and can be controlled with
--color auto, --color always, or --color never.
Write machine-readable JSON or CSV:
python3 scripts/rachecker-mismatch-report.py \
--rachecker http://127.0.0.1:8088 \
--roms /srv/roms \
--rachecker-rom-mapping /srv/roms \
--format json --output mismatch-report.jsonPrint aggregate accepted/fixable, low/uncertain, patch-version mismatch, likely region mismatch, and unsupported-version-or-bad-dump counts:
python3 scripts/rachecker-mismatch-report.py \
--rachecker http://127.0.0.1:8088 \
--roms /srv/roms \
--rachecker-rom-mapping /srv/roms \
--summaryUse --include-rejected to audit weak and ambiguous guesses with rejection
reasons. By default the utility refuses to produce a partial report while a
scan is active; --allow-active-scan explicitly permits that incomplete view.
Candidate titles must also meet --minimum-title-similarity, a normalized edit
similarity from 0 to 1 that defaults to 0.90. Exact RA title words contained
at a token boundary in a longer local title are accepted so meaningful version
or variant suffixes remain eligible. Summary and structured output count
already-supported duplicates separately from uncertain rejections.
Files detected as hacks, including files beneath a Hacks directory, can only
match RA titles explicitly marked as a hack, bootleg, or homebrew. This prevents
modified ROM names from being reported as fixes for the unmodified base game.
Filename matching is heuristic. A candidate means an exact supported RA ROM
exists for the likely game, not that the local file's identity was proven from
its filename. Each accepted candidate is classified as a patch/version mismatch
when a detected modified ROM has a supported patch, a likely region mismatch
when supported RA filenames identify one or more regions, or an unsupported
version or bad dump otherwise. This unsupported version or bad dump diagnosis
is the fallback. Region diagnosis is intentionally heuristic: an
unsupported local hash may instead be another revision or dump variation. Text,
JSON, and CSV include the likely issue and supported regions.
Use --patch-candidate-mirror to optionally build a local mirror while
producing any text, JSON, CSV, or summary report:
python3 scripts/rachecker-mismatch-report.py \
--rachecker http://127.0.0.1:8088 \
--roms /srv/roms \
--rachecker-rom-mapping /srv/roms \
--patch-candidate-mirror /srv/ra-patchesOnly accepted candidates with patch links enter the mirror. Each incompatible
ROM is copied, never symlinked, beneath a normalized
<console>/<RA game title>/ directory, alongside all advertised patches under
their original decoded filenames. Slashes in directory names become -.
ROM copies and patches are append-only by filename. Existing regular files are retained without opening, hashing, comparing, downloading, or replacing them; new filenames are copied or downloaded atomically. Delete an existing mirror file manually if a same-name ROM or patch must be refreshed. Files are not pruned when an upstream patch link disappears.
Unsafe paths, symlinks, and local copy errors stop immediately. Patch HTTP or network failures are collected while later downloads continue. The complete report is still written, every failed URL is diagnosed, and the command exits non-zero if any patch failed. A rerun retains successful files and only attempts missing filenames. See the patch candidate mirror guide for the complete behavior.
This project packages and operates upstream RAChecker. It does not modify or reimplement RAChecker, guarantee upstream ROM hashing or achievement behavior, commit personal ROMs or host paths, or publish container images. The optional mismatch utility reads operator-selected local filenames only when explicitly invoked; repository verification uses synthetic data and never accesses a real ROM library.
This repository's original deployment assets and utilities are available under the MIT License. RAChecker, RAHasher, and other fetched third-party components remain covered by their respective upstream licenses; this license does not claim or relicense those projects.