This guide covers deploying Project Riddle locally using Docker.
- Docker installed and running (Get Docker)
- Google OAuth credentials (Google Cloud Console)
- OpenRouter API key (OpenRouter)
-
Create a project directory:
mkdir riddle && cd riddle
-
Create a
.envfile with your credentials:# .env GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com GOOGLE_CLIENT_SECRET=your-client-secret OPENROUTER_API_KEY=sk-or-v1-your-openrouter-api-key -
Create
appsettings.json(required - the container needs this file):{ "ConnectionStrings": { "DefaultConnection": "Data Source=riddle.db" }, "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*", "OpenRouter": { "DefaultModel": "google/gemini-2.0-flash-001", "SiteUrl": "https://your-site.example.com", "SiteName": "Project Riddle" }, "AdminSettings": { "AdminEmails": [ "your-admin-email@example.com" ] }, "WhitelistSettings": { "IsEnabled": false, "RejectionMessage": "This application is currently in private beta. Contact the administrator for access." } }Important: The
appsettings.jsonfile is required! The container will fail to start without it. The volume mount maps this file into the container. -
Create
docker-compose.yml:services: riddle: image: peakflames/riddle:latest container_name: riddle restart: unless-stopped ports: - "1983:8080" environment: - ASPNETCORE_ENVIRONMENT=Production - Kestrel__Endpoints__Http__Url=http://+:8080 - ConnectionStrings__DefaultConnection=Data Source=/app/data/riddle.db env_file: - .env volumes: - ./data:/app/data - ./appsettings.json:/app/appsettings.json:ro healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8080/health"] interval: 30s timeout: 10s retries: 3 start_period: 15s
-
Start the container:
docker compose up -d
-
Access the application: Open http://localhost:1983
Why port 1983? That's the year the Dungeons & Dragons animated TV series debuted!
- Stop the container when done:
docker compose down
Note: You still need to create an
appsettings.jsonfile (see step 3 above) and mount it into the container.
docker run -d \
--name riddle \
-p 1983:8080 \
-e ASPNETCORE_ENVIRONMENT=Production \
-e Kestrel__Endpoints__Http__Url=http://+:8080 \
-e ConnectionStrings__DefaultConnection="Data Source=/app/data/riddle.db" \
-e GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com \
-e GOOGLE_CLIENT_SECRET=your-client-secret \
-e OPENROUTER_API_KEY=your-openrouter-api-key \
-v riddle-data:/app/data \
-v ./appsettings.json:/app/appsettings.json:ro \
peakflames/riddle:latestThen open http://localhost:1983 in your browser.
To stop the container:
docker stop riddleTo remove the container (data is preserved in the volume):
docker rm riddleFor local development, you need to configure Google OAuth to allow your local callback URL.
- Go to Google Cloud Console
- Create or select a project
- Navigate to APIs & Services > Credentials
- Create an OAuth 2.0 Client ID (Web application type)
- Add authorized redirect URI:
http://localhost:1983/signin-google - Copy the Client ID and Client Secret
| Tag | Description |
|---|---|
latest |
Latest stable release from main branch |
develop |
Latest development build from develop branch (may be unstable) |
| Variable | Required | Description |
|---|---|---|
GOOGLE_CLIENT_ID |
Yes | Google OAuth Client ID |
GOOGLE_CLIENT_SECRET |
Yes | Google OAuth Client Secret |
OPENROUTER_API_KEY |
Yes | API key for LLM access (Get key) |
ASPNETCORE_ENVIRONMENT |
No | Runtime environment (default: Production) |
Kestrel__Endpoints__Http__Url |
Yes | Internal listen URL - use http://+:8080 |
ConnectionStrings__DefaultConnection |
Yes | SQLite database path - use Data Source=/app/data/riddle.db |
The appsettings.json file is required for the container to start. It configures application-specific settings that aren't suitable for environment variables.
| Setting | Description |
|---|---|
OpenRouter.DefaultModel |
LLM model to use (e.g., google/gemini-2.0-flash-001, anthropic/claude-3.5-sonnet) |
OpenRouter.SiteUrl |
Your site URL for OpenRouter analytics |
OpenRouter.SiteName |
Display name in OpenRouter dashboard |
AdminSettings.AdminEmails |
Array of email addresses with admin privileges |
WhitelistSettings.IsEnabled |
Set true to require users to be whitelisted |
WhitelistSettings.RejectionMessage |
Message shown to non-whitelisted users |
Popular models available through OpenRouter:
google/gemini-2.0-flash-001- Fast, cost-effective (recommended for most use)google/gemini-2.5-flash- Latest Gemini Flashanthropic/claude-3.5-sonnet- High quality reasoningopenai/gpt-4o- OpenAI's latest
See OpenRouter Models for the full list.
The container runs on port 8080 internally and should be mapped to your preferred external port:
External (host) → Internal (container)
1983 → 8080
- Internal port (8080): The ASP.NET Core app listens on this port inside the container
- External port (1983): The port you access in your browser
The SQLite database is stored at /app/data/riddle.db inside the container. To persist data across container restarts:
- Docker Compose: Use a bind mount (
./data:/app/data) or named volume - Docker Run: Use
-v riddle-data:/app/data
The data directory will be created automatically and will contain:
riddle.db- SQLite databaseriddle.db-shm- SQLite shared memory file (temporary)riddle.db-wal- SQLite write-ahead log (temporary)
# Copy database from container
docker cp riddle:/app/data/riddle.db ./backup-riddle.db
# Restore database to container
docker cp ./backup-riddle.db riddle:/app/data/riddle.db
docker restart riddleTo update to the latest version:
# Docker Compose
docker compose pull
docker compose down
docker compose up -d
# Docker Run
docker pull peakflames/riddle:latest
docker stop riddle && docker rm riddle
# Re-run the docker run command from Quick StartCheck the logs:
docker logs riddleCommon issues:
- Missing appsettings.json: The container requires an
appsettings.jsonfile mounted at/app/appsettings.json. Create one using the template in the Quick Start section. - HTTPS certificate errors: The container is HTTP-only. Do not configure HTTPS endpoints.
- Kestrel binding errors: Ensure
Kestrel__Endpoints__Http__Url=http://+:8080is set.
Verify that:
- Your redirect URI exactly matches
http://localhost:1983/signin-google - The OAuth consent screen is configured
- The client ID and secret are correct
If you need to reset the database:
# Docker Compose
docker compose down
rm -rf ./data
docker compose up -d
# Docker Run
docker stop riddle && docker rm riddle
docker volume rm riddle-data
# Re-run docker run commandFor production deployments:
- Use HTTPS via reverse proxy: Place nginx, Caddy, or Traefik in front of the container
- Use a proper database: Consider PostgreSQL instead of SQLite for concurrent users
- Set up proper secrets management: Don't commit
.envfiles to version control - Configure proper OAuth redirect URIs: Update to your production domain
- Memory: ~256-512 MB
- Storage: ~100 MB for image + database growth
- CPU: Minimal (single core is sufficient)