Guides
How to Self-Host a Web Retro Gaming Server with Docker (RomM Setup Guide)
Step-by-step RomM deployment guide using Docker Compose. Mount game directories, configure metadata scrapers, and stream retro ROMs in your browser.
- Topic
- Browser emulation
- Difficulty
- advanced
- Spoilers
- None
To self-host a web-accessible retro gaming library, deploy RomM via Docker Compose paired with a MariaDB database and a Redis caching layer. RomM scans local directory hierarchies, pulls cover art and metadata from IGDB, and embeds a WebAssembly emulator frontend so users can play retro consoles directly inside any desktop or mobile browser without installing client-side emulators.
Directory Layout & File Permissions
Create the host folder structure before launching the container stack. Using consistent paths prevents permission lockouts during the initial file indexing pass:
mkdir -p ~/romm/{config,resources,library/roms,mysql_data,redis_data}
chmod -R 755 ~/romm
Organize your game files into system subdirectories matching RomM’s platform conventions:
~/romm/library/roms/gba/(Game Boy Advance)~/romm/library/roms/snes/(Super Nintendo)~/romm/library/roms/genesis/(Sega Genesis / Mega Drive)~/romm/library/roms/psx/(Sony PlayStation 1.chdor.bin/.cue)
Production docker-compose.yml Configuration
Save the following configuration inside ~/romm/docker-compose.yml. Replace placeholder passwords with dedicated 32-character random strings:
services:
romm:
image: ghcr.io/rommapp/romm:latest
container_name: romm
restart: unless-stopped
environment:
- DB_HOST=romm-db
- DB_NAME=romm
- DB_USER=romm_user
- DB_PASSWD=ReplaceWithStrongDatabasePassword_8921#
- ROMM_AUTH_SECRET_KEY=GenerateRandomHexKey32CharsLongHere99!
- REDIS_HOST=romm-redis
- IGDB_CLIENT_ID=${IGDB_CLIENT_ID:-}
- IGDB_CLIENT_SECRET=${IGDB_CLIENT_SECRET:-}
- DISABLE_USER_REGISTRATION=false
volumes:
- ./resources:/romm/resources
- ./config:/romm/config
- ./library/roms:/romm/library/roms:ro
ports:
- "8080:8080"
depends_on:
romm-db:
condition: service_healthy
romm-redis:
condition: service_started
romm-db:
image: mariadb:11.2
container_name: romm-db
restart: unless-stopped
environment:
- MYSQL_ROOT_PASSWORD=ReplaceWithRootPasswordSecure_990!
- MYSQL_DATABASE=romm
- MYSQL_USER=romm_user
- MYSQL_PASSWORD=ReplaceWithStrongDatabasePassword_8921#
volumes:
- ./mysql_data:/var/lib/mysql
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 10s
timeout: 5s
retries: 5
romm-redis:
image: redis:7.2-alpine
container_name: romm-redis
restart: unless-stopped
volumes:
- ./redis_data:/data
Running and Indexing
Launch the multi-container stack in detached mode:
cd ~/romm
docker compose up -d
Verify that all three containers are healthy:
docker compose ps
Open http://<your-server-ip>:8080 in your browser. Register your initial administrator profile, then navigate to Settings -> Scan Library to trigger automated ROM hashing and IGDB metadata scraping.
Reverse Proxy: Solving Browser Audio Stutter (SharedArrayBuffer)
Modern browser-based emulators depend on SharedArrayBuffer for multi-threaded audio synchronization. Modern browsers disable this feature unless the origin is secure (HTTPS) and sends specific cross-origin isolation headers. Without these headers, WebAssembly audio drops into a single-threaded fallback loop that results in severe sound crackling and input latency.
Nginx Configuration
Add these headers to your server block:
server {
listen 443 ssl http2;
server_name retro.yourdomain.com;
# SSL certificates here...
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Mandatory Cross-Origin Isolation Headers
add_header Cross-Origin-Opener-Policy "same-origin" always;
add_header Cross-Origin-Embedder-Policy "require-corp" always;
}
}
Caddy Configuration
retro.yourdomain.com {
reverse_proxy 127.0.0.1:8080 {
header_up Host {host}
header_up X-Real-IP {remote_host}
}
header {
Cross-Origin-Opener-Policy "same-origin"
Cross-Origin-Embedder-Policy "require-corp"
}
}
Storage & Compression Best Practices
- PlayStation 1 (.chd): Convert all multi-bin PS1 games to single CHD files using
chdman createcd -i game.cue -o game.chd. This reduces file sizes by ~50% and removes multi-track indexing errors in browser emulators. - Save State Syncing: RomM stores emulator save states inside the
resources/saves/volume. Back up this specific directory using Borg or Restic to preserve in-game progress across container recreations.