TapIn Pokémon

Self-hosting

Run your own game server with Docker and a Cloudflare tunnel.

The game server is one container with a data volume. A tunnel in front of it gives it a public HTTPS address without opening ports.

What you need

  • Docker with Compose.
  • The game assets (artwork, sprites, icons, cries and the battle font). They are generated from PokeAPI by the data script and mounted read-only.
  • A build of the TapIn web app pointed at your server's address.
  • A Cloudflare account if you want a stable public hostname.

Compose

name: pokemon

services:
  server:
    image: pokemon-server:local
    build:
      context: .
      dockerfile: apps/pokemon-server/Dockerfile
    environment:
      PORT: "8790"
      DATA_DIR: /data
      ASSETS_DIR: /assets
      WEB_DIR: /web
    volumes:
      - pokemon-data:/data
      - ./assets:/assets:ro
      - ./web-dist:/web:ro
    ports:
      - "127.0.0.1:8790:8790"
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "wget", "-qO-", "http://127.0.0.1:8790/health"]
      interval: 10s

  tunnel:
    image: cloudflare/cloudflared:latest
    command: tunnel --no-autoupdate run
    environment:
      TUNNEL_TOKEN: ${TUNNEL_TOKEN}
    restart: unless-stopped
    depends_on:
      server:
        condition: service_healthy

volumes:
  pokemon-data:

The server binds to localhost only; the tunnel is the way in. The SQLite database lives in the pokemon-data volume, so rebuilding the container keeps every trainer.

The tunnel

  1. In Cloudflare Zero Trust, create a named tunnel and copy its token into a .env file next to the compose file as TUNNEL_TOKEN. Keep that file out of version control.
  2. Add a public hostname to the tunnel, for example play.example.com, pointing at http://server:8790.
  3. docker compose up -d. The hostname serves the web app, the API and the WebSocket on one origin.

For a quick test without an account, cloudflared tunnel --url http://server:8790 prints a temporary address; it changes on every restart.

Configuration

VariableDefaultWhat it does
PORT8790HTTP port inside the container.
DATA_DIR./dataFolder for pokemon.sqlite.
ASSETS_DIR./assetsFolder served at /pokemon-assets.
WEB_DIR./web-distThe built web app.
GIGAVERSE_API_URLBase URL of the TapIn API used to resolve users and communities.
FIREBASE_PROJECT_IDThe identity project whose ID tokens the server accepts.
TURN_SECONDS10Turn timer in battles between people.
CHALLENGE_TTL_SECONDS30How long a challenge waits for an answer.
ENCOUNTER_MIN_SECONDS / ENCOUNTER_MAX_SECONDS120 / 300The random gap between wild encounters.
DISCONNECT_GRACE_SECONDS60How long a disconnected player's battle waits for them.
POKEMON_DEBUGoff1 enables debug encounters (see below). Never on a public server.

Debugging

With POKEMON_DEBUG=1, opening the app with ?pokemonDebug=1 shows a button that sends debug-encounter. speciesId picks a species (or null for a random one), -1 spawns a legendary flyover and -2 sends Team Rocket. Without the flag the server answers debug-disabled.

GET /health returns the server's status as JSON; use it for container health checks and uptime monitors.

TapIn Pokémon is an unofficial fan project. Pokémon and all related names and artwork are trademarks and © of Nintendo, Game Freak and Creatures. Legal

On this page