Next Level Snake
Made almost entirely with arena.ai ❤️

01 — The game

Beautiful.
Modern.
Snake.

One long green body, a red box of food, and walls that wrap instead of ending the run. Preset or fully custom speed and growth, seven themes, per-combination records — and signed-in players fighting over the hall.

Score 0
Best —
Mode Normal · ×1
Score 0

Ready

Press a direction to start

W A S D or the arrow keys on desktop — swipe anywhere on the page while playing. Drive through a wall and you come out the other side.

Held

Paused

Speed
Normal · 105 ms / step
Growth
×1 blocks per food

You can scroll while paused. Modifiers stay locked for this run. Enter, P or any steering key resumes; Esc quits.

After you resume, pausing again is locked for 5 seconds.

Hold up

You for sure want to quit the run man?

Esc again quits for real. Enter or any steering key takes you back to the game.

Run complete

Game over

Final score 0

Speed
Normal · 105 ms / step
Growth
×1 blocks per food
Best here
—

02 — The leaderboard

One game. Two boards. Everyone watching.

Signed-in players only. Standard ranks preset modifiers; Custom ranks any run that used a custom slider. Each player appears once per modifier combination with their best run, so use the filters to see exactly the speed and growth you care about. Guests can play, but their runs are not saved unless they sign in.

Speed
Growth

Loading the board…

# Player Speed Growth Score Set
Loading the board…

03 — Self-hosting · Podman first

One container. Two commands. Yours.

Host this website yourself!

Run the Rails app and PostgreSQL directly with podman compose up --build -d from the repository root. No helper script. Prefer Docker? Use docker compose up --build -d with the same file. Fedora keeps SELinux enforcing: database storage has a private :Z label, and application files are built into the image.

  1. Bring it up Requires Podman or Docker

    Builds the image and starts PostgreSQL with persistent, SELinux-labeled storage.

    git clone https://github.com/HelloJesusBeloved/next-level-snake
    cd ./next-level-snake
    cp .env.example .env
    # Set SECRET_KEY_BASE in .env to this generated value:
    sed -i "s/^SECRET_KEY_BASE=.*/SECRET_KEY_BASE=$(openssl rand -hex 64)/" .env
    podman compose up --build -d
  2. Configure it

    The whole surface is .env at the repo root — port, secret, database credentials. Restart after editing: podman compose up -d --force-recreate.

    # .env
    SECRET_KEY_BASE=<openssl rand -hex 64>
    PORT=3000
    DATABASE_USER=snake
    DATABASE_PASSWORD=snake
    DATABASE_NAME=snake_production
    RAILS_LOG_LEVEL=info
  3. Operate it

    Logs, a console, backups — all one line each, engine-agnostic.

    podman compose logs -f app
    podman compose exec app bin/rails console
    podman compose exec db pg_dump -U snake snake_production > snake.sql
  4. No containers? Bare metal (I Wouldn’t personally recommend)

    On Fedora or Ubuntu, install.sh detects the distro, installs mise, compiles the pinned Ruby and wires up systemd.

    chmod +x install.sh
    sudo ./install.sh
    systemctl status snake

The compose surface

  • app — builds the Rails image, publishes $BIND_ADDRESS:$PORT (default 0.0.0.0:3000) to the container’s 3000, and healthchecks /up with curl every 30s.
  • db — postgres:16-alpine, role and database created from your .env values, healthchecked with pg_isready before app starts.
  • pgdata — named volume; podman compose down keeps it, down -v wipes it.
  • Both services use restart: unless-stopped on the snakenet network, and the entrypoint waits for Postgres itself rather than trusting compose ordering.
  • Fedora: pgdata:/var/lib/postgresql/data:Z gives the database a private SELinux label. No privileged mode or disabled security labels.
  • Tunables, all optional in .env: RAILS_LOG_LEVEL, RAILS_FORCE_SSL, RAILS_MAX_THREADS, WEB_CONCURRENCY, DATABASE_PORT.
# compose.yaml — read by both Podman and Docker compose
# Native commands, from the repository root:
#   podman compose up --build -d
#   docker compose up --build -d   # optional alternative; same configuration
#
# Fedora/SELinux: application files are copied INTO the image, not bind-mounted
# from your home directory. Database storage uses a private :Z-labeled named
# volume. SELinux stays enforcing; do not use privileged or label=disable.
services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    image: next-level-snake:local
    restart: unless-stopped
    networks:
      - snakenet
    ports:
      - "${BIND_ADDRESS:-0.0.0.0}:${PORT:-3000}:3000"
    environment:
      RAILS_ENV: production
      RAILS_LOG_LEVEL: ${RAILS_LOG_LEVEL:-info}
      PORT: 3000
      SECRET_KEY_BASE: ${SECRET_KEY_BASE:?Set SECRET_KEY_BASE in .env with openssl rand -hex 64}
      DATABASE_HOST: db
      DATABASE_PORT: 5432
      DATABASE_USER: ${DATABASE_USER:-snake}
      DATABASE_PASSWORD: ${DATABASE_PASSWORD:-snake}
      DATABASE_NAME: ${DATABASE_NAME:-snake_production}
      RAILS_FORCE_SSL: ${RAILS_FORCE_SSL:-false}
      RAILS_MAX_THREADS: ${RAILS_MAX_THREADS:-5}
      WEB_CONCURRENCY: ${WEB_CONCURRENCY:-0}
      # Absolute public origin used for invitation links and QR codes when the
      # app is served through a proxy or Cloudflare Tunnel (e.g.
      # https://snake.example.com). Empty means "use the request's own origin".
      MULTIPLAYER_PUBLIC_ORIGIN: ${MULTIPLAYER_PUBLIC_ORIGIN:-}
      # Comma-separated origins allowed to open the /cable WebSocket when a
      # proxy rewrites Origin. Same-origin already works without this.
      ACTION_CABLE_ALLOWED_ORIGINS: ${ACTION_CABLE_ALLOWED_ORIGINS:-}
    depends_on:
      db:
        condition: service_healthy
    # The entrypoint ALSO waits for Postgres; it does not rely solely on the
    # compose provider supporting health-based dependency ordering.
    healthcheck:
      test: ["CMD", "curl", "-fsS", "http://127.0.0.1:3000/up"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 60s

  db:
    image: docker.io/library/postgres:16-alpine
    restart: unless-stopped
    networks:
      - snakenet
    environment:
      POSTGRES_USER: ${DATABASE_USER:-snake}
      POSTGRES_PASSWORD: ${DATABASE_PASSWORD:-snake}
      POSTGRES_DB: ${DATABASE_NAME:-snake_production}
    volumes:
      # Private SELinux relabeling for this database container. Docker on
      # non-SELinux hosts also accepts this suffix, so no override is required.
      - pgdata:/var/lib/postgresql/data:Z
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U \"$${POSTGRES_USER}\" -d \"$${POSTGRES_DB}\""]
      interval: 5s
      timeout: 3s
      retries: 12

  # Official Cloudflare Tunnel connector (remotely-managed, token-based).
  # DISABLED by default: it belongs to the "tunnel" Compose profile and only
  # starts when .env sets COMPOSE_PROFILES=tunnel. The connector dials OUT to
  # Cloudflare, so no inbound port, port-forward or firewall rule is needed.
  # The token is deliberately guarded with :- (not :?) because Compose
  # interpolates the whole file even for profile-disabled services; a hard
  # guard here would break plain `compose up` for people not using the tunnel.
  cloudflared:
    image: docker.io/cloudflare/cloudflared:latest
    restart: unless-stopped
    profiles:
      - tunnel
    command: tunnel --no-autoupdate run
    environment:
      TUNNEL_TOKEN: ${CLOUDFLARE_TUNNEL_TOKEN:-}
      # Local-only metrics endpoint; also what the health probe asks "ready?".
      TUNNEL_METRICS: 127.0.0.1:20241
    networks:
      - snakenet
    depends_on:
      app:
        condition: service_healthy
    healthcheck:
      # The cloudflared binary probes its own metrics server; the distroless
      # image has no shell, curl or wget, so this is the supported check.
      test: ["CMD", "cloudflared", "tunnel", "--metrics", "127.0.0.1:20241", "ready"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 20s

networks:
  snakenet:

volumes:
  pgdata:

Your name on the board