Theriodex is an open-source Pokédex written in Ruby. It displays a random Pokémon with a bunch of information on the homepage but also has information about Pokémons’ moves, their abilties, evolutions and more! It also has a small Pokémon guessing game. https://theriodex.net
  • Ruby 42.8%
  • HTML 35.3%
  • CSS 20.7%
  • JavaScript 0.7%
  • Dockerfile 0.4%
  • Other 0.1%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Hex aacc15bd2c Merge pull request 'Merge evolutions feature into main branch' (#2) from dev into main
add evolutions feature, move to new database

A bunch of stuff was done here! I added a new evolutions feature that shows the evolutions for each Pokémon, updated to the new database and did a few other things. The evolutions feature is still not 100% done but I think it works well enough that it can be released on the main branch.
2026-08-08 07:42:41 +00:00
.woodpecker add missing "steps" to woodpecker 2026-03-28 15:02:11 +01:00
app add item info meta tags 2026-08-07 16:16:29 +02:00
config Add workers and threads, use puma 2026-07-26 12:23:24 +02:00
public Remove flex-wrap: wrap; from div.evolution-info 2026-08-02 23:07:43 +02:00
views remove unncessary each_with_index 2026-08-08 09:39:22 +02:00
.dockerignore add serials to .dockerignore 2026-02-24 17:51:49 +01:00
.gitignore add serial for unlocking fonts 2026-02-23 11:31:20 +01:00
.herb.yml add .herb.yml 2026-08-03 22:23:49 +02:00
.rubocop.yml add rubocop (not sure if going to use this yet) 2025-09-16 09:39:19 +02:00
.ruby-version Move to debian slim image for YJIT (seems to net a few percent performance improvements on 2026-07-28 12:10:13 +02:00
BENCHMARKS.md Update README 2026-07-26 14:29:30 +02:00
config.ru Add workers and threads, use puma 2026-07-26 12:23:24 +02:00
docker-compose.yml Fix ENV check (has to be string), remove debug puts 2026-07-30 19:18:56 +02:00
Dockerfile Delete my own privacy policy and add placeholder thing 2026-07-30 19:00:33 +02:00
Dockerfile.alpine Move to debian slim image for YJIT (seems to net a few percent performance improvements on 2026-07-28 12:10:13 +02:00
Dockerfile.dev Fix broken parens after removing format sprites helper 2026-08-02 12:39:39 +02:00
Dockerfile.old No more sprites in container 2026-07-28 11:41:27 +02:00
Gemfile Store sessions in Redis also 2026-07-26 12:40:01 +02:00
Gemfile.lock Store sessions in Redis also 2026-07-26 12:40:01 +02:00
LICENSE Add licence. Change title and add meta description 2025-08-20 14:38:39 +02:00
logo.png add logo 2026-03-17 20:36:21 +01:00
Makefile add some docker dev stuff 2026-03-26 13:00:40 +01:00
README.md Update README.md 2026-08-04 15:53:11 +02:00
routes.md Split view functions into separate files, add route info markdown 2026-07-24 15:40:09 +02:00
screenshot.png Update screenshot.png 2026-07-24 15:46:49 +02:00
server.rb move cache building into own function 2026-07-30 12:11:26 +02:00
theriodex.service add systemd service template 2025-08-29 08:59:31 +02:00
TODO.md Update TODO.md 2026-08-05 11:40:24 +02:00

Screenshot of Theriodex showing Vaporeon

Ruby Version License Docker Codeberg CI Website

Most established Pokédex sites are filled with ads and tracking and use a lot of bandwidth. Additionally, a lot of them can be somewhat hard to navigate as they contain a lot of information which is great in general, but if you just want to quickly look up a Pokémon's moves in a particular generation or see its stats, it can be a bit of a hindrance and this is where Theriodex comes in.

Theriodex is designed to be fast and easily glancable with information clearly laid out and meant as a complementary tool for other, more detailed websites. Across a test of numerous Pokémon and move pages, Theriodex consistently requires much less data to fully load, even with adblock enabled on other sites:

Site Avg Weight (Baseline) Avg Weight (EasyList)
Theriodex 98 KB 98 KB
Bulbapedia 2148 KB 893 KB
Serebii 3712 KB 2487 KB
PokémonDB 757 KB 219 KB

Note: Tests were conducted in July 2026 from a German IP address using a headless Chromium setup and hitting the official https://theriodex.net installation. The pages tested were: Gligar, Pikachu, Charizard, Mewtwo, Bulbasaur's moves in Gen IX and Poison Sting move info. Each page was tested five times with and five times without EasyList blocking.

Try it out at theriodex.net and see for yourself!

Info: The name Theriodex comes from therio- (Ancient Greek for "animal" or "beast") and -dex (reference to the Pokédex).

Table of Contents

Features

Core Pokédex Information and Features

  • All 1000+ Pokémon through Generation IX, including regional variants, mega evolutions and other special forms
  • A quick overview of type damage, taking dual-types into account, so you can see at a glance what a Pokémon is weak or resistant to
  • Full move learnsets (level-up, TM/HM, egg, tutor) with STAB highlighting
  • Ability descriptions, plus which Pokémon can actually have them
  • Base stats and full evolution overviews
  • Plenty of sprites for all Pokémon from various generations
  • A random Pokémon on the homepage if you just want to go and explore (there are so many Pokémon…)
  • An evolution tree with information about when a Pokémon evolves into another and what requirements there are for its evolving

Browsing

  • Filter by type, generation, and more
  • A full item database with descriptions and effects
  • Fast, fuzzy search across both Pokémon and moves
  • Interface localisation via a lang parameter (still a work in progress, see below)

Game

  • A "Who's That Pokémon?"-type guessing game, with blurred sprites, adjustable difficulty, and a daily challenge
  • Filter by generation if you want to practise, say, only Gen 3
  • A global leaderboard backed by Redis

Customisation

  • A handful of pixel fonts to pick from
  • Various themes, some of which you'll have to unlock by playing the game
  • Custom CSS cursor packs
  • Cards or table view, whichever you prefer for browsing

Quick Start

Prerequisites:

  • Docker and Docker Compose installed on a supported operating system mostly Linux or macOS (Windows might be supported but no guarantees!).
  • Approximately 512 MB of available RAM (you should be fine with less, but this number is a safe one)
  • Two CPU cores

Note: Should you have less cores or RAM available to you, please read the sections below on how to disable Puma workers; this will allow you to run Theriodex (at the expense of much worse concurrent user performance) on lower-end hardware.

First, create a new folder with a docker-compose.yml file and edit it to include the following text. You may also use the docker-compose.yml file from this repository.

services:
  theriodex:
    image: codeberg.org/hexaitos/theriodex:latest
    platform: linux/amd64
    ports:
      - "5678:5678"
    environment:
      - REDIS_HOST=redis
    depends_on:
      - redis
    volumes:
      - ./privacy.md:/usr/src/app/views/privacy.md
      - ./public/sprites:/usr/src/app/public/sprites
      - ./public/cries:/usr/src/app/public/cries
    restart: unless-stopped

  redis:
    image: valkey/valkey:latest
    volumes:
      - redis_data:/data
    restart: unless-stopped

volumes:
  redis_data:

Afterwards, create another file titled privacy.md in the same folder and copy your privacy policy into that file. You may also leave it empty if you do not wish to have one at all, but you must change the privacy policy, you may not use the one included in this repository.

Additionally, should you wish to have sprites and sounds, you will be required to download those separately. See the section further below on how to acquire these.

You can then start Theriodex with the following command:

(sudo) docker compose up -d

Theriodex will be available at http://localhost:5678 after the images have been downloaded. The -d flag makes it start as a daemon so that it all runs in the background and so that it autostarts when you start your computer / server. To make it available on the public Internet, please follow the instructions in the Reverse Proxy section.

Podman notes: The above-mentioned docker-compose.yml file works without any problems under Podman as well! Simply install podman and podman-compose (or docker-compose) and run podman compose up -d instead.

Installation (advanced)

For most folk, the Quick Start should be sufficient to get started. If you want to run it without Docker or have different requirements (like using a different operating systems like OpenBSD), this section is for you.

Docker

(sudo) docker run \
  --name theriodex \
  -e REDIS_HOST=REDIS_HOST_HERE \
  -p 5678:5678 \
  -d \
  codeberg.org/hexaitos/theriodex:latest

The container is linux/amd64 only.

Building from Dockerfile

Clone the repository and build using the provided Dockerfile (follow standard Docker build procedures). This is a required step if you wish to run Docker but need to adjust the amount of Puma workers or if you wish to disable workers entirely.

From Source

Requirements: Ruby (tested with 4.0.5) and Bundler.

  1. Clone the repository
  2. Run bundle install to install gems
  3. Place the sprites folder in /public/ (see Sprites and Sounds)
  4. Start the server: puma -C config/puma.rb

Production Mode

# Production mode with Puma (recommended for multi-worker):
REDIS_HOST="localhost" puma -C config/puma.rb

# Production mode (single process, no Puma config):
REDIS_HOST="localhost" ruby server.rb -e production

Important: Redis is required by default and needs to be used when using Puma workers. If you do not need a multi-worker setup, please read the Session Storage section on how to disable Rack::Session::Redis and switch to Rack::Session::Pool. Redis is still required for the leaderboard functionality, but otherwise, Theriodex will run well without Redis otherwise if you follow the steps outlined.

Puma workers

Because the SQLite database that Theriodex uses is read-only, multi-threading with Puma can easily be used and is enabled by default in the Docker image. The Docker sets a somewhat safe default of two workers, this should run comfortably even on lower-end systems.

If you wish to adjust the values for your own use case, please edit the config/puma.rb and adjust the workers and threads values accordingly.

Please note that each new worker will almost double the required amount of RAM used by Theriodex. One instance of Theriodex uses approximately 190 MB of memory. A two-worker installation already can require 300+ MB of memory; the reason for this is that each worker will need to spawn its own instance of Theriodex.

This brings us to yet another important point. As each worker spawns its own session, sessions need to be stored in a central location; thus, the default, multi-worker Puma setup requires Rack::Session::Redis for session storage.

I would highly recommend at least two Puma workers to be used. On a stress test / performance test conducted with Locust and 100 users (starting at one user and increasing by one user every second for 2 minutes and 20 seconds), we saw a performance increase from a median of 1,456ms with the default Puma configuration to a median of 430ms with the two-worker configuration.

YJIT

We recommend using YJIT in production installation, especially those that expect a large traffic volume.

What is YJIT?

This is especially helpful if you are trying to run Theriodex in an environment where you expect a lot of requests and a lot of traffic. YJIT (Yet Another Ruby JIT) is a just-in-time compiler for Ruby. Essentially, what it does is it observes the program and notices when certain things get executed a bunch of times over and over again (especially when they use the same types, such as always integers or always strings). It then compiles it to regular bytecode that the CPU can execute directly without needing to go through the Ruby interpreter.

This can save considerable time in the execution of your program because the interpreter is much slower than bytecode directly executed by the CPU , but this compiled bytecode has to be stored somewhere, namely in memory. This means that you can expect a performance increase but also an increase in memory usage. The observed increase in performance is around 10-20% depending on workload with an increase in memory usage of about the same amount.

How to use YJIT

If you use the official docker container, YJIT will already be enabled. If you are running Theriodex on bare hardware, you will need several things:

  • Ruby compiled with Rust support. For this, your system will need the rustc compiler. Please check how to install this for your operating system!
  • Setting the ENV RUBY_YJIT_ENABLE=1 environment variable in your environment

If you use a Ruby version management tool such as rbenv and rbenv install (which I, personally, use), then once you have the rustc compiler installed you can simply run rbenv install 4.0.5 (or whichever version is current at the time of your reading this) and enable it locally in Theriodex by running rbenv enable 4.0.5.

You can check whether the installation was successful by running RUBY_YJIT_ENABLE=1 ruby -e "puts RubyVM::YJIT.enabled?". This command should simply output true if Ruby has been compiled with YJIT support. Start the server as follows:

# Production mode with Puma (recommended for multi-worker):
RUBY_YJIT_ENABLE=1 REDIS_HOST="localhost" puma -C config/puma.rb

# Production mode (single process, no Puma config):
RUBY_YJIT_ENABLE=1 REDIS_HOST="localhost" ruby server.rb -e production

jemalloc

We urge you use jemalloc if you are using Puma workers, but it is recommended in either case to avoid high memory usage.

Why use jemalloc

We have found that GLib's malloc seems to cause some issues, especially when using multi-worker setups with Puma causing the memory usage to baloon way above what is reasonable without properly being able to free memory again. After having run Theriodex for about 15 hours on a VPS and having done a few traffic tests, memory had balooned to over 1.6 GB with heavy swapping on a VPS that only has 2 GB of physical memory and 1 GB of zram.

The Theriodex Docker container reported around 800 MB of memory usage while using about the same amount again in swap. Having found out about that, we ended up switching over to jemalloc which has reduced the memory footprint dramatically. After further tests, memory now plateaus at around 500-600 MB with two workers in total rather than more than 1.6 GB.

How to install jemalloc

The installation of jemalloc differs from system to system. These instructions are for installations on Linux or macOS, specifically! Firstly, you will need to install jemalloc on your system using your system's package manager:

# macOS
brew install jemalloc

# Ubuntu, Debian and derivatives
sudo apt install libjemalloc-dev

# Fedora
sudo dnf install jemalloc-devel

# Arch and derivatives
sudo pacman -S jemalloc

Afterwards, you have several options of using jemalloc with your Ruby installation. First, check if your Ruby version is, perhaps, already compiled with jemalloc. Run ruby -r rbconfig -e 'puts RbConfig::CONFIG["configure_args"]' in your terminal. If the output includes --with-jemalloc, then your Ruby version is already using it. If not, follow the steps below to install it.

Scenario 1 Compiling Ruby with jemalloc support

If you are able to compile your Ruby version yourself, you can compile it with the --with-jemalloc flag. If you use rbenv, you can do so as follows: RUBY_CONFIGURE_OPTS="--with-jemalloc" rbenv install 4.0.5

Scenario 2 Using LD_PRELOAD (if you cannot change your Ruby installation)

If you are unable to change your Ruby installation, you can still use jemalloc with the caveat of your needing yet another environment variable, name LD_PRELOAD. First, figure out where your jemalloc library is located by running find /usr/lib /usr/local/lib -name "libjemalloc.so*". You should receive a list of various library files such as this:

/usr/lib/libjemalloc.so
/usr/lib/libjemalloc.so.2

Copy and remember the path of the libjemalloc.so.2 file and then start Ruby with the LD_PRELOAD=/path/to/jemalloc.so.2 environment variable. For example, on my system (where it is located at /usr/lib/libjemalloc.so.2), it would look as follows:

# Production mode with Puma (recommended for multi-worker):
LD_PRELOAD=/usr/lib/libjemalloc.so.2 RUBY_YJIT_ENABLE=1 REDIS_HOST="localhost" puma -C config/puma.rb

# Production mode (single process, no Puma config):
LD_PRELOAD=/usr/lib/libjemalloc.so.2 RUBY_YJIT_ENABLE=1 REDIS_HOST="localhost" ruby server.rb -e production

Redis/Valkey

Redis (or Valkey) is used for two things:

  1. Leaderboard: Stores highscores for the "Who's That Pokémon?" guessing game
  2. Session storage: Stores session data such as UI customisation as well as game data as it is being played.

Configuration:

Set the REDIS_HOST environment variable:

REDIS_HOST="localhost" ruby server.rb -e production

Session storage

By default, Theriodex uses Rack::Session::Redis to store session data in Redis. This is required for multi-worker Puma, since in-memory sessions (Rack::Session::Pool) are not shared between workers.

If you are running a single process (no Puma workers), you can switch back to Rack::Session::Pool:

# In server.rb, comment out or remove the Redis session line:
# use Rack::Session::Redis, key: 'rack.session', expire_after: 86_400, url: "redis://#{ENV['REDIS_HOST']}:6379/0"

# And uncomment or add this line instead:
use Rack::Session::Pool, key: 'rack.session', expire_after: 86_400

Note: Rack::Session::Cookie is unfortunately not really viable because the game session data (especially in longer sessions) may exceed the 4 KB cookie size limit.

Configuration

Sprites and Sounds

Sprites and cries are served from local storage, not GitHub.

  • Sprites: Download from PokeAPI/sprites (~1.5 GB) and place the sprites folder in /public/
  • Cries: Download from PokeAPI/cries (~30 MB) and place the cries folder in /public/

Note: Theriodex should work without sprites but then, of course, it will not display any of the images.

Database

The SQLite database (db.sqlite3) is derived from PokeAPI and Veekun's Pokédex. You can rebuild it yourself using PokeAPI's instructions and replace the included database. No modifications have been made yet, though migrations may be required in the future.

Leaderboard (Redis)

The guessing game uses Redis/Valkey for score tracking. Entries are stored in a sorted set score with format: USERNAME-YYYYMMDDHHMMSS>RANDOM_HASH.

Configuration: Set the REDIS_HOST environment variable:

REDIS_HOST="localhost" ruby server.rb -e production

Reverse Proxy

To make your version of Theriodex publicly accessible, you will most likely want to put it behind a reverse proxy. My preferred method of doing so is by using Caddy, as it makes the whole process rather simple!

Let us assume that you have a server or other device which has a public IP and which is currently not hosting anything else ports 80 and 443 are available and open to the public; let us also assume that you own the domain theriodex.net and have entered your servers public IP addresses into your domain registrar so that theriodex.net points to the public IP address(es) of your server.

You can, then, simply install Caddy and use the following configuration assuming that you use the default port of 5678:

theriodex.net {
	@assets path *.css *.js
	@images path *.png *.jpg *.jpeg *.gif *.svg *.webp *.ico
	@fonts path *.woff2 *.ttf

	header @assets Cache-Control "public, max-age=3600"
	header @images Cache-Control "public, max-age=2592000"
	header @fonts Cache-Control "public, max-age=2592000"

	reverse_proxy localhost:5678 {
		header_down -Cache-Control
	}

	encode zstd gzip
}

Caddy will automatically obtain an SSL certificate for you and in just a few moments, your website should be available under theriodex.net and have HTTPS enabled! It also enables (hopefully) decent caching defaults.

Privacy Policy

This repo includes the privacy policy for my own hosted instance of Theriodex. This will obviously not apply to you, so if you wish to host it yourself, change the privacy policy under /views/privacy.md accordingly.

With Docker, mount your own policy:

docker run \
  -e REDIS_HOST="redis_host" \
  -p 5678:5678 \
  -v /path/to/your/privacy.md:/usr/src/app/views/privacy.md \
  theriodex

Development & Deployment

Auto-restart with rerun

The rerun gem auto-restarts the server when files change, useful for development or auto-updating deployments:

rerun -- ruby server.rb -e production

Caching

Theriodex uses rack-cache for disk caching. Configuration is in /app/helpers/vars.rb:

CACHE_DIR = "/tmp/cache/rack"

Cache is cleared on server start via this line in server.rb:

FileUtils.remove_dir(CACHE_DIR) if Dir.exist?(CACHE_DIR)

Disabling Caching

Use the no_rack_cache environment:

ruby server.rb -e no_rack_cache

Or manually remove all rack/cache references, cache_control lines, and the FileUtils.remove_dir call from server.rb.

Memory Caching

Change the cache stores to heap (not recommended for production due to unbounded growth):

use Rack::Cache,
  :metastore => 'heap:/',
  :entitystore => 'heap:/',
  :verbose => true

See rack-cache documentation for details.

Platform Notes (OpenBSD)

Tested on OpenBSD 7.6 and 7.7 with Ruby 3.3.5/3.4.2.

Nokogiri Issues

You should be able to install bundler itself without any problems, but when running bundle install, you may run into problems with installing nokogiri. To solve this, running the following commands should make it work:

pkg_add libxml libxslt libiconv
bundle config build.nokogiri --use-system-libraries
bundle install

Permission Errors (Non-root)

You may also have trouble installing the gems in the repository by running bundle install and will get permission errors, as Bundler is unable to write to the directories it wants to write to. A possible fix for this problem is changing the directory that Bundler installs its gems into to something that the current user can access, such as ~/.gem. To do so, add the following to your ~/.profile:

Add to ~/.profile:

export GEM_HOME=$HOME/.gem

Or run:

echo 'export GEM_HOME=$HOME/.gem' >> $HOME/.profile

Acknowledgements

A special and immense thanks also to all my partners who are way better at programming and database queries than I am for helping me out a lot and answering my questions and giving helpful tips. I definitely would not have been able to get as far with this project if it hadnt been for them. Thank you, I love you all 💛

Data sources:

Fonts:

Icons: Iconoir

  • Logo: The fantastic logo was made by my partner Coyote 💛

Support the original creators! Please consider donating to / buying the fonts and icons used in Theriodex if you are a frequent vistor of theriodex.net or if you wish to host it yourself.

Pokémon and Pokémon character names are trademarks of Nintendo, Game Freak, and Creatures Inc. Theriodex is not affiliated with, authorized, or endorsed by The Pokémon Company, Game Freak, Creatures, or Nintendo.

The project is licensed under the 3-Clause BSD License.