First batch of real Wave 1 content for the v5.0 overhaul. These pages establish the tone and cross-linking pattern for the rest of the wave. - index.md: routing landing with intent-based cards - getting-started/quick-start.md: 15-min docker-compose happy path, reusing the existing compose snippet and asciinema cast - install/docker-compose.md: canonical production reference with volume table, env var table, and hardening checklist - install/image-variants.md: slim vs full matrix + what's actually different between them - install/reverse-proxy.md: Caddy / nginx / Traefik / NPM recipes with ROMM_BASE_URL guidance - administration/users-and-roles.md: full 19-scope matrix across Viewer/Editor/Admin, invite flow, OIDC hooks - developers/api-authentication.md: session / Basic / OAuth2 / Client API Token / OIDC flows with error semantics Builds clean with --strict.
5.2 KiB
| title | description |
|---|---|
| Reverse Proxy | Put RomM behind Caddy, nginx, Traefik, or Nginx Proxy Manager with TLS. |
Reverse Proxy
The RomM container listens on plain HTTP on port 8080. For anything beyond localhost you should put it behind a reverse proxy that terminates TLS and forwards to the container.
!!! tip "WebSockets are required"
RomM uses Socket.IO (both the general /ws/socket.io endpoint and the /netplay/socket.io endpoint) for live updates, scan progress, and Netplay. Every reverse-proxy recipe below keeps WebSocket support on — don't strip it out.
The examples here assume your RomM container is reachable at romm:8080 (by container name on a Docker network) or 192.168.1.100:8080 (by IP on the LAN). Swap to whatever's right for your setup.
Caddy
Dead-simple, auto-HTTPS via Let's Encrypt.
romm.mysite.com {
encode zstd gzip
header {
Strict-Transport-Security "max-age=31536000;"
X-XSS-Protection "1; mode=block"
X-Frame-Options "SAMEORIGIN"
X-Robots-Tag "noindex, nofollow"
-Server
-X-Powered-By
}
reverse_proxy romm:8080
}
If you just want HTTP on the LAN:
http://romm.mysite.com {
reverse_proxy romm:8080
}
Nginx
HTTP only
server {
listen 80 default_server;
server_name romm.mysite.com;
client_max_body_size 0;
location / {
proxy_pass http://romm:8080;
proxy_http_version 1.1;
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;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
HTTPS with HSTS
server {
listen 80 default_server;
server_name _;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name romm.mysite.com;
ssl_certificate /etc/ssl/romm/fullchain.pem;
ssl_certificate_key /etc/ssl/romm/privkey.pem;
client_max_body_size 0;
location / {
proxy_pass http://romm:8080;
proxy_http_version 1.1;
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;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
server_tokens off;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
}
}
!!! note "client_max_body_size 0"
Required so large ROM uploads aren't rejected by nginx before they reach RomM.
Traefik
Dynamic configuration file
http:
routers:
romm:
entryPoints:
- websecure
rule: "Host(`romm.mysite.com`)"
middlewares:
- default-headers
- https-redirectscheme
tls:
certResolver: letsencrypt
service: romm
services:
romm:
loadBalancer:
servers:
- url: "http://192.168.1.100:8080"
passHostHeader: true
Docker Compose labels
Add these to the romm service in your docker-compose.yml:
labels:
- "traefik.enable=true"
- "traefik.http.services.romm.loadbalancer.server.port=8080"
- "traefik.http.routers.romm.rule=Host(`romm.mysite.com`)"
- "traefik.http.routers.romm.entrypoints=websecure"
- "traefik.http.routers.romm.tls=true"
- "traefik.http.routers.romm.tls.certresolver=letsencrypt"
Nginx Proxy Manager
Items marked ❗ are important — RomM won't work right without them.
Details
- Domain Names:
romm.mysite.com - Scheme:
http - Forward Hostname/IP: container hostname or LAN IP (e.g.
192.168.1.100) - Forward Port:
8080 - Cache Assets:
off - Block Common Exploits:
on - Websockets Support:
on❗ - Access List: as needed
SSL
- SSL Certificate: Request a new SSL Certificate
- Force SSL:
on - HTTP/2 Support:
on - HSTS Enabled:
on(after you've confirmed TLS works) - Email Address for Let's Encrypt: your address
- I Agree to the TOS:
on
Advanced — custom nginx configuration ❗
proxy_max_temp_file_size 0;
Without that line, large downloads (bulk ROM zips, multi-disc games) will fail on NPM because nginx tries to buffer them to disk.
| Details | SSL | Advanced |
|---|---|---|
Set ROMM_BASE_URL behind HTTPS
Once you're proxying through HTTPS, set ROMM_BASE_URL in the RomM container's environment so generated links (QR codes, invite links, OIDC redirects) use the public URL:
environment:
- ROMM_BASE_URL=https://romm.mysite.com
If you're also using OIDC, update OIDC_REDIRECT_URI to match — see OIDC Setup.