Pular para o conteúdo

técnico

Deploy do RomM

  • self-hosting
  • docker
  • cloudflare

O RomM (Retro ROMs Manager) é um projeto de código aberto para organizar, gerenciar e rodar coleções de jogos antigos direto pelo navegador. Funciona como uma plataforma de streaming particular: a aplicação gerencia a biblioteca no servidor, busca metadados automaticamente por API — arte de capa, sinopse, data de lançamento — e permite jogar os clássicos no próprio cliente web pelo módulo In-Browser Play, sem emulador local.

O projeto integra com o RetroAchievements para habilitar conquistas nos jogos e tem sugestões aleatórias para quando bate a dúvida sobre o que jogar. A arquitetura é baseada em containers Docker, com MariaDB como banco principal e Redis (ou Valkey) para as tarefas em segundo plano.

Instalação

A instalação padrão é via Docker Compose.

Preparação

Crie um diretório no host para hospedar o projeto e defina onde ficarão os arquivos da biblioteca. Gere também a chave secreta, que vai na variável ROMM_AUTH_SECRET_KEY:

openssl rand -hex 32

Arquivo de configuração

O docker-compose.yml abaixo é o modelo padrão já ajustado para uso atrás de um reverse proxy:

volumes:
  mysql_data:
  romm_resources:
  romm_redis_data:

services:
  romm:
    image: rommapp/romm:latest
    container_name: romm
    restart: unless-stopped
    environment:
      - DB_HOST=romm-db
      - DB_NAME=romm
      - DB_USER=romm-user
      - DB_PASSWD=SENHA_DO_BANCO # Deve ser idêntica ao MARIADB_PASSWORD
      - ROMM_AUTH_SECRET_KEY=CHAVE_GERADA_NO_OPENSSL
      - TZ=America/Sao_Paulo
    volumes:
      - romm_resources:/romm/resources
      - romm_redis_data:/redis-data
      - /caminho/para/sua/library:/romm/library
      - ./assets:/romm/assets
      - ./config:/romm/config
    depends_on:
      romm-db:
        condition: service_healthy
        restart: true
    networks:
      - proxy_network
      - default

  romm-db:
    image: mariadb:latest
    container_name: romm-db
    restart: unless-stopped
    environment:
      - MARIADB_ROOT_PASSWORD=SENHA_ROOT
      - MARIADB_DATABASE=romm
      - MARIADB_USER=romm-user
      - MARIADB_PASSWORD=SENHA_DO_BANCO
    volumes:
      - mysql_data:/var/lib/mysql
    healthcheck:
      test: [CMD, healthcheck.sh, --connect, --innodb_initialized]
      start_period: 30s
      start_interval: 10s
      interval: 10s
      timeout: 5s
      retries: 5

networks:
  proxy_network:
    external: true

Execução

Suba os containers em segundo plano:

docker compose up -d

No primeiro acesso à URL da aplicação, um assistente de configuração pede a criação da conta de administrador.

Decisões de arquitetura

  • Storage distribuído: para não sobrecarregar o I/O do SSD principal do host, onde o sistema operacional roda, apontei o ponto de montagem da biblioteca de jogos para uma unidade externa (/media/ssd_usb/romm/library). As pastas de configuração e os assets da aplicação continuam no diretório de contexto do Docker.
  • Reverse proxy: uso o Nginx Proxy Manager. Em vez de expor a porta de acesso para fora, os containers compartilham a rede virtual proxy_network e o tráfego flui direto pelo nome do container (romm) na porta interna 8080.
  • Segurança na borda: o RomM fica atrás de um subdomínio protegido pelo Cloudflare Zero Trust, com uma Access Policy de negação por padrão. Todo tráfego externo é interceptado e bloqueado na borda; o acesso só é liberado depois que um provedor de identidade valida o meu e-mail por One-Time PIN.

Problemas resolvidos no caminho

  • Falha de conexão com o banco. O RomM não conseguia falar com o container do MariaDB. A causa era a variável DB_PASSWD, no bloco da aplicação, vazia ou diferente da MARIADB_PASSWORD do banco. As duas precisam ser idênticas.
  • Conflito de portas. Como já rodo outros serviços na rede do Docker, declarar ports: - 8080:8080 retornava port already allocated do daemon. A solução foi remover o mapeamento de portas do docker-compose.yml e deixar a comunicação puramente dentro da rede interna.
  • Scan travando com erro 400. A interface escondia o painel e os jogos não apareciam depois do upload. Nos logs havia vários HTTP 400 recusando conexões em GET /ws/socket.io/. A aplicação precisa de WebSockets tanto para a comunicação em tempo real do emulador quanto para atualizar a barra de progresso do scanner. Resolvido ativando a opção Websockets Support nas configurações do Nginx Proxy Manager.

Fontes: