Docker Setup
Table of contents
Installation
Pull the Image
# Latest stable version (recommended for production)
docker pull ghcr.io/sethwv/game-thumbs:latest
# Development version (latest features, may be unstable)
docker pull ghcr.io/sethwv/game-thumbs:dev
Basic Usage
Run the server on port 3000:
docker run -p 3000:3000 ghcr.io/sethwv/game-thumbs:latest
The API will be available at http://localhost:3000.
Redirect Root to Documentation
Optionally redirect the root path (/) to your documentation site:
docker run -p 3000:3000 \
-e ROOT_REDIRECT_URL=https://game-thumbs-docs.swvn.io/ \
ghcr.io/sethwv/game-thumbs:latest
When enabled, visitors to http://localhost:3000/ will be permanently redirected (301) to the specified URL.
Environment Variables
Configure the server behavior using environment variables:
Server Configuration
| Variable | Description | Default |
|---|---|---|
PORT | Server port | 3000 |
NODE_ENV | Environment mode (development or production). In development, error stack traces are included in responses. | production |
SERVER_TIMEOUT | Timeout for HTTP connections (in milliseconds). | 30000 |
REQUEST_TIMEOUT | Timeout for external API calls and image downloads (in milliseconds). | 10000 |
ROOT_REDIRECT_URL | Optional URL to redirect from root path (/) with a 301 permanent redirect. Useful for directing users to documentation. | Not set (disabled) |
ALLOW_CUSTOM_BADGES | Allow custom badge parameter entries on matchup generation endpoints. | false |
ALLOW_EVENT_OVERLAYS | Enable the league event overlay feature on /:league/thumb and /:league/cover. When true, the title, subtitle, and iconurl query parameters are honored, default fonts are loaded at startup, and per-league custom fonts are registered. When disabled, those query parameters are ignored. | false |
ALLOW_INSECURE_OVERLAY_URLS | Controls whether iconurl may reference private/loopback/local addresses. false (default): only public DNS targets allowed. true: validation skipped entirely. Comma-separated list (e.g. 192.168.1.5,printer.local): only those hostnames bypass validation, everything else is still checked. | false |
Caching
| Variable | Description | Default |
|---|---|---|
IMAGE_CACHE_HOURS | How long to cache generated images (in hours). Set to 0 to disable caching. | 24 |
Note: When IMAGE_CACHE_HOURS=0, every request generates a new image. Useful for testing but not recommended for production.
Rate Limiting
| Variable | Description | Default |
|---|---|---|
RATE_LIMIT_PER_MINUTE | Maximum image generation requests per minute per IP. Set to 0 to disable rate limiting. | 30 |
TRUST_PROXY | Number of proxy hops to trust for rate limiting (0 for local dev, 1+ for production behind proxies). | 2 |
Notes:
- Rate limiting only applies to uncached requests; cached images are served without limits
- General API endpoints (like
/raw) have 3x the image generation rate limit - Set
TRUST_PROXYto the number of proxies between the internet and your app for accurate IP detection - When
RATE_LIMIT_PER_MINUTE=0, there are no request limits (use with caution if exposed to the internet)
Logging
| Variable | Description | Default |
|---|---|---|
SHOW_TIMESTAMP | Whether to show timestamps in logs. Set to false to hide timestamps. | true |
FORCE_COLOR | Force colored output in logs (useful for Docker/CI environments). Set to 1 or true to enable. | false |
LOG_TO_FILE | Enable file logging. Set to true or 1 to enable. | false |
MAX_LOG_FILES | Maximum number of log files to keep (oldest are deleted). | 10 |
Notes:
- When
LOG_TO_FILE=true, logs are written to files in./logsdirectory with automatic rotation (~100KB per file) - Log files are named
app-YYYY-MM-DD-NNN.logand old files are automatically cleaned up - File logs always include full timestamps and stack traces (regardless of console settings)
CORS
| Variable | Description | Default |
|---|---|---|
CORS_ORIGIN | Allowed CORS origin(s). Set to * for all origins, or a specific domain like https://example.com. | * |
CORS_MAX_AGE | How long (in seconds) browsers should cache CORS preflight requests. | 86400 (24 hours) |
League Feature Flags
Optional leagues can be enabled/disabled using environment variables. When disabled, these leagues will not initialize caches and will not be available via the API.
| Variable | Description | Default |
|---|---|---|
LEAGUES_ENABLE_TENNIS | Enable tennis leagues (ATP, WTA). When enabled, initializes athlete cache (~33,800 athletes). | false |
LEAGUES_ENABLE_MMA | Enable MMA/combat sports leagues (UFC, PFL, Bellator). When enabled, initializes athlete caches. | false |
Example - Enable tennis:
docker run -p 3000:3000 \
-e LEAGUES_ENABLE_TENNIS=true \
ghcr.io/sethwv/game-thumbs:latest
Example - Enable both tennis and MMA:
docker run -p 3000:3000 \
-e LEAGUES_ENABLE_TENNIS=true \
-e LEAGUES_ENABLE_MMA=true \
ghcr.io/sethwv/game-thumbs:latest
Notes:
- These leagues are disabled by default to reduce startup time and memory usage
- Tennis leagues have 33,800+ athletes and may take 5-30 minutes for initial cache population
- When disabled, API requests to these leagues will return a “league not found” error
- Athlete caches are not created when the league is disabled
ESPN Athlete Provider Rate Limiting
Control the rate of requests to ESPN’s athlete API to avoid rate limiting during cache initialization.
| Variable | Description | Default |
|---|---|---|
ESPN_ATHLETE_REQUEST_DELAY | Minimum delay between ESPN athlete API requests (in milliseconds). | 250 |
ESPN_ATHLETE_MAX_CONCURRENT | Maximum concurrent requests to ESPN athlete API. | 3 |
Example - Faster cache initialization (may hit rate limits):
docker run -p 3000:3000 \
-e LEAGUES_ENABLE_TENNIS=true \
-e ESPN_ATHLETE_REQUEST_DELAY=100 \
-e ESPN_ATHLETE_MAX_CONCURRENT=5 \
ghcr.io/sethwv/game-thumbs:latest
Example - Conservative rate limiting (slower but safer):
docker run -p 3000:3000 \
-e LEAGUES_ENABLE_TENNIS=true \
-e ESPN_ATHLETE_REQUEST_DELAY=500 \
-e ESPN_ATHLETE_MAX_CONCURRENT=2 \
ghcr.io/sethwv/game-thumbs:latest
Notes:
- Default settings (250ms delay, 3 concurrent) balance speed and reliability
- ESPN doesn’t publish rate limits, but testing shows ~200-300 requests/min is safe
- If you encounter 403/429 errors, increase delay or decrease concurrency
- Request queue automatically handles pacing - no need for manual delays
Volume Mounts
Custom Team and League Overrides
Game Thumbs supports additive configuration - your custom files merge with built-in data rather than replacing it. You only need to specify what you want to customize.
Directory-Based Configuration
Mount directories containing your custom JSON files. All .json files in each directory will be loaded and merged:
docker run -p 3000:3000 \
-v /path/to/custom-teams:/app/json/teams:ro \
-v /path/to/custom-leagues:/app/json/leagues:ro \
ghcr.io/sethwv/game-thumbs:latest
Benefits:
- Organize configurations into multiple files (e.g.,
my-teams.json,ncaa-teams.json) - Easy to add/remove specific configuration sets
- Files are loaded in alphabetical order and merged together
Example directory structure:
custom-teams/
├── premier-league.json
├── ncaa-football.json
└── my-favorites.json
custom-leagues/
└── custom-leagues.json
Example teams.json file:
{
"epl": {
"man-utd": {
"aliases": ["man utd", "man u", "mufc"],
"override": {
"abbreviation": "MUN"
}
}
}
}
How merging works:
- Your custom teams/leagues are merged with the built-in data
- For teams that exist in both, aliases are combined (duplicates removed)
- Your override values take precedence over built-in values
- You only need to include what you want to customize
See the Team Matching and Customization documentation for complete details.
Persistent Logs
Mount a volume to persist log files:
docker run -p 3000:3000 \
-e LOG_TO_FILE=true \
-v /path/to/logs:/app/logs \
ghcr.io/sethwv/game-thumbs:latest
Provider Cache Directory
Recommended for Tennis: Mount the
.cachedirectory to persist athlete data across container restarts. Tennis has 33,800+ athletes and takes 5-30 minutes to cache initially. Persisting the cache makes restarts instant.
docker run -p 3000:3000 \
-v /path/to/cache:/app/.cache \
ghcr.io/sethwv/game-thumbs:latest
Benefits:
- Instant restarts - no need to re-fetch 33,800+ tennis athletes
- Preserves MMA fighter rosters (UFC, PFL, Bellator)
- Reduces load on ESPN APIs
- Container updates don’t lose cached data
Example Configurations
Production Setup with Custom Teams
docker run -d \
--name game-thumbs \
--restart unless-stopped \
-p 3000:3000 \
-e NODE_ENV=production \
-e RATE_LIMIT_PER_MINUTE=60 \
-e TRUST_PROXY=1 \
-e CORS_ORIGIN=https://yourdomain.com \
-e LOG_TO_FILE=true \
-v /path/to/custom-teams:/app/json/teams:ro \
-v /path/to/logs:/app/logs \
-v /path/to/cache:/app/.cache \
ghcr.io/sethwv/game-thumbs:latest
Development Setup
docker run -p 3000:3000 \
-e NODE_ENV=development \
-e RATE_LIMIT_PER_MINUTE=0 \
-e IMAGE_CACHE_HOURS=0 \
-e SHOW_TIMESTAMP=true \
-e FORCE_COLOR=true \
ghcr.io/sethwv/game-thumbs:dev
Docker Compose (Recommended Approach)
version: '3.8'
services:
game-thumbs:
image: ghcr.io/sethwv/game-thumbs:latest
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- RATE_LIMIT_PER_MINUTE=60
- TRUST_PROXY=1
- LOG_TO_FILE=true
volumes:
# Recommended: Mount directories
- ./custom-teams:/app/json/teams:ro
- ./custom-leagues:/app/json/leagues:ro
- ./logs:/app/logs
- ./cache:/app/.cache # Persist athlete/provider cache (recommended for Tennis)
restart: unless-stopped
Docker Compose (Backward Compatible Single File)
version: '3.8'
services:
game-thumbs:
image: ghcr.io/sethwv/game-thumbs:latest
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- RATE_LIMIT_PER_MINUTE=60
- TRUST_PROXY=1
- LOG_TO_FILE=true
volumes:
# Alternative: Mount single files
- ./teams.json:/app/teams.json:ro
- ./leagues.json:/app/leagues.json:ro
- ./logs:/app/logs
restart: unless-stopped
Troubleshooting
Port Already in Use
If port 3000 is already in use, map to a different host port:
docker run -p 8080:3000 ghcr.io/sethwv/game-thumbs:latest
The API will be available at http://localhost:8080.
Rate Limit Issues
If you’re hitting rate limits during development or testing:
docker run -p 3000:3000 \
-e RATE_LIMIT_PER_MINUTE=0 \
ghcr.io/sethwv/game-thumbs:latest
Warning: Disabling rate limits in production is not recommended.
Timeout Issues
If you’re experiencing timeouts with slow network connections:
docker run -p 3000:3000 \
-e REQUEST_TIMEOUT=30000 \
-e SERVER_TIMEOUT=60000 \
ghcr.io/sethwv/game-thumbs:latest
Custom Configuration Not Loading
Ensure your configuration files:
- Are valid JSON
- Use the correct file path in the volume mount
- Have the correct league keys (lowercase)
- Use proper team slugs (check via
/rawendpoint)
Check container logs for merge confirmation messages:
docker logs <container-id>
You should see:
✓ Loaded base teams.json
✓ Loading 2 custom file(s) from json/teams/
✓ Merged my-teams.json
✓ Merged ncaa-teams.json
{
"espn": {
"espnSlug": "custom",
"espnSport": "football"
}
}
]
}
}