Skip to content

Self-Hosting Romm: Your Retro Game Library

Posted on:April 10, 2025

Self-Hosting Romm: Your Retro Game Library with Docker and HTTPS

Romm is a fantastic web-based application for managing and playing your retro game library. While running it locally is great, self-hosting it on a server allows access from anywhere. This guide will walk you through setting up Romm using Docker and Docker Compose, complete with MariaDB for the database and Nginx as a reverse proxy to enable secure HTTPS access, even if you only have an IP address for your server.

Prerequisites

Step 1: Initial Docker Compose Setup

First, we need a docker-compose.yml file to define our services: romm itself and its database romm-db (MariaDB).

Create a file named docker-compose.yml:

version: "3" # Note: Version tag is optional in newer Docker Compose

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 # Should match MARIADB_DATABASE in mariadb
            - DB_USER=romm-user # Should match MARIADB_USER in mariadb
            # IMPORTANT: Replace with a strong, unique password!
            - DB_PASSWD=your_secure_db_password 
            # IMPORTANT: Generate with `openssl rand -hex 32` and keep secret!
            - ROMM_AUTH_SECRET_KEY=your_generated_secret_key 
            # Optional: Add API keys for enhanced metadata fetching
            - IGDB_CLIENT_ID= 
            - IGDB_CLIENT_SECRET= 
            - MOBYGAMES_API_KEY= 
            - STEAMGRIDDB_API_KEY= 
        volumes:
            - romm_resources:/romm/resources 
            - romm_redis_data:/redis-data 
            # Map your host library directory to the container
            - ./data/library:/romm/library 
            - ./data/assets:/romm/assets 
            - ./data/config:/romm/config 
        depends_on:
            romm-db:
                condition: service_healthy

    romm-db:
        image: mariadb:latest
        container_name: romm-db
        restart: unless-stopped
        environment:
            # IMPORTANT: Use the same secure password as DB_PASSWD above!
            - MARIADB_ROOT_PASSWORD=your_secure_db_password 
            - MARIADB_DATABASE=romm
            - MARIADB_USER=romm-user
            - MARIADB_PASSWORD=your_secure_db_password
        volumes:
            - mysql_data:/var/lib/mysql
        healthcheck:
            test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
            interval: 10s
            timeout: 5s
            retries: 5

Before proceeding:

  1. Replace your_secure_db_password with a strong, unique password in both the romm and romm-db sections.
  2. Generate a secret key using openssl rand -hex 32 in your terminal and replace your_generated_secret_key.
  3. Create the host directories for your library, assets, and config: mkdir -p data/library data/assets data/config.
  4. (Optional) Add your API keys if you have them.

At this point, you could run docker compose up -d and access Romm via http://<your_server_ip>:8080 (if you temporarily added a ports: - 8080:8080 section to the romm service). But our goal is secure HTTPS on the standard port 443.

Step 2: Introducing Nginx for HTTPS

To handle HTTPS, we’ll use Nginx as a reverse proxy. Nginx will listen on ports 80 (HTTP) and 443 (HTTPS). It will redirect HTTP traffic to HTTPS and handle the SSL/TLS encryption for HTTPS traffic, forwarding the decrypted requests to the romm container on its internal port 8080.

Step 3: Updating docker-compose.yml

Modify your docker-compose.yml to add the Nginx service and remove the direct port mapping from the romm service (if you added one).

version: "3" 

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=your_secure_db_password 
            - ROMM_AUTH_SECRET_KEY=your_generated_secret_key
            - IGDB_CLIENT_ID= 
            - IGDB_CLIENT_SECRET= 
            - MOBYGAMES_API_KEY= 
            - STEAMGRIDDB_API_KEY= 
        volumes:
            - romm_resources:/romm/resources 
            - romm_redis_data:/redis-data 
            - ./data/library:/romm/library 
            - ./data/assets:/romm/assets 
            - ./data/config:/romm/config 
        # No 'ports' section here anymore; Nginx handles external access
        depends_on:
            romm-db:
                condition: service_healthy

    romm-db:
        # ... (romm-db configuration remains the same) ...
        image: mariadb:latest
        container_name: romm-db
        restart: unless-stopped
        environment:
            - MARIADB_ROOT_PASSWORD=your_secure_db_password 
            - MARIADB_DATABASE=romm
            - MARIADB_USER=romm-user
            - MARIADB_PASSWORD=your_secure_db_password
        volumes:
            - mysql_data:/var/lib/mysql
        healthcheck:
            test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
            interval: 10s
            timeout: 5s
            retries: 5

    nginx:
        image: nginx:latest
        container_name: romm-nginx
        restart: unless-stopped
        ports:
            # Map host ports 80 and 443 to the container
            - "80:80"
            - "443:443" 
        volumes:
            # Mount Nginx configuration
            - ./nginx/conf.d:/etc/nginx/conf.d:ro
            # Mount SSL certificates
            - ./nginx/certs:/etc/nginx/certs:ro 
        depends_on:
            - romm

Step 4: Creating the Nginx Configuration

Now, create the directories for Nginx configuration and certificates:

mkdir -p nginx/conf.d nginx/certs

Create a file named nginx/conf.d/default.conf with the following content. This configuration listens on ports 80 and 443, redirects HTTP to HTTPS, and proxies requests to the romm service.

server {
    # Explicitly listen on IPv6 and IPv4 for HTTP
    listen [::]:80 ipv6only=on; 
    listen 80; 
    # Replace with your server's IP address or domain name
    server_name your_server_ip_or_domain; 

    # Redirect HTTP to HTTPS
    location / {
        return 301 https://$host$request_uri;
    }
}

server {
    # Explicitly listen on IPv6 and IPv4 for HTTPS
    listen [::]:443 ssl ipv6only=on; 
    listen 443 ssl; 
    # Replace with your server's IP address or domain name
    server_name your_server_ip_or_domain; 

    # --- SSL Certificate Configuration ---
    # Option 1: Let's Encrypt (Requires a domain name)
    # ssl_certificate /etc/nginx/certs/live/your_domain.com/fullchain.pem; 
    # ssl_certificate_key /etc/nginx/certs/live/your_domain.com/privkey.pem; 

    # Option 2: Self-Signed Certificates (Use if you only have an IP address)
    ssl_certificate /etc/nginx/certs/nginx-selfsigned.crt; 
    ssl_certificate_key /etc/nginx/certs/nginx-selfsigned.key; 
    # --- End SSL Certificate Configuration ---

    # Recommended SSL settings (adjust ciphers as needed)
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers off;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
    ssl_session_cache shared:SSL:10m;
    ssl_session_tickets off;
    # ssl_stapling and ssl_stapling_verify are commented out 
    # as they are not applicable to self-signed certs.
    # ssl_stapling on; 
    # ssl_stapling_verify on; 

    location / {
        proxy_pass http://romm:8080; # Forward requests to the romm service
        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;

        # Support for WebSockets (if needed by romm)
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

Important:

Step 5: Handling HTTPS without a Domain Name (Self-Signed Certs)

If you don’t have a domain name and are using your server’s IP address, you generally cannot get publicly trusted certificates from authorities like Let’s Encrypt. The solution is to generate your own “self-signed” certificate.

  1. Ensure Nginx Config Uses Self-Signed Paths: Make sure the ssl_certificate and ssl_certificate_key directives in nginx/conf.d/default.conf point to /etc/nginx/certs/nginx-selfsigned.crt and /etc/nginx/certs/nginx-selfsigned.key.

  2. Generate the Certificate: Run the following openssl command in your terminal, replacing your_server_ip with your actual IP address (use brackets for IPv6):

    # Example for IPv6
    openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
      -keyout nginx/certs/nginx-selfsigned.key \
      -out nginx/certs/nginx-selfsigned.crt \
      -subj "/CN=[2a01:4f8:c013:f709::1]" 
    
    # Example for IPv4
    # openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
    #  -keyout nginx/certs/nginx-selfsigned.key \
    #  -out nginx/certs/nginx-selfsigned.crt \
    #  -subj "/CN=192.0.2.1" 

    This creates the certificate (.crt) and private key (.key) files in the nginx/certs/ directory, valid for 365 days.

Step 6: Running the Setup

With the docker-compose.yml and Nginx configuration ready (and self-signed certificates generated if needed), you can start the services:

docker compose up -d

This command will download the necessary images (if not already present), create the containers, networks, and volumes, and start everything in the background.

Check the status:

docker compose ps 

You should see romm, romm-db, and romm-nginx running.

Check Nginx logs for errors (especially useful during troubleshooting):

docker logs romm-nginx

Step 7: Accessing Romm via HTTPS

Open your web browser and navigate to:

https://your_server_ip_or_domain

(Replace your_server_ip_or_domain with the same IP or domain used in the Nginx config).

Important Note on Self-Signed Certificates: If you used self-signed certificates, your browser will display a prominent security warning (e.g., “Your connection is not private,” “Warning: Potential Security Risk Ahead”). This is expected because the certificate isn’t signed by a trusted authority known to the browser. You will need to manually accept the risk (usually under an “Advanced” or “Details” section) to proceed. The connection is encrypted, but the identity verification step fails.

Troubleshooting Tips

Conclusion

You now have a self-hosted Romm instance running securely over HTTPS! While using self-signed certificates for IP-based access works, getting a domain name and using Let’s Encrypt certificates is recommended for a smoother experience without browser warnings. Enjoy managing and playing your retro game collection!