Repository navigation
Configuration reference
This reference contains every assignment documented in the reviewed .env.example, including commented optional settings. Values shown are defaults or examples from that file, not safe production secrets. Provider-specific values and source credentials require your own configuration.
Docker Compose reads .env for substitution and passes it to API and worker with env_file. The Compose files explicitly construct TCG_DATABASE_URL from POSTGRES_PASSWORD. Pydantic reads TCG_* variables and, for local processes, the working directory's .env. Environment values override dotenv values. Unknown dotenv keys are ignored by the base settings object.
Provider-specific settings and plugin tokens are loaded by their own classes/helpers. Do not add undocumented unprefixed names and expect the backend to read them. Lists use JSON, for example ["en","fr"]; booleans use true/false. Compose also interprets dollar signs, so use proper dotenv escaping/quoting for secrets and URL-encode non-hexadecimal database passwords when constructing a URL yourself.
After changes use docker compose up -d api worker; restarting the old containers does not change their attached environment. Persisted synchronization schedules override environment schedule defaults until reset through the administration page/API. Bootstrap administrator settings apply only to a database with no users.
For local HTTP set TCG_COOKIE_SECURE=false; public HTTPS normally uses true. Never commit your real .env.
PostgreSQL password, used by the db service and in the server's database URL.
POSTGRES_PASSWORD=change-meSigns session tokens. At least 32 random characters (the server refuses to start otherwise). Changing it signs everybody out. Generate with: python -c "import secrets; print(secrets.token_urlsafe(48))"
TCG_SECRET_KEY=change-meHost port the web UI and API are published on.
ZUKAN_PORT=8000Image tag pulled by docker-compose.yml: latest, a version (0.1.0) or a dev build
(dev-<branch>). Ignored by docker-compose-dev.yml, which builds from the checkout.
ZUKAN_TAG=latestMTU of the containers' network. Below 1500 so the server also works when the host goes
through a VPN tunnel. Lower it for tunnels with less room; the network is
recreated on docker compose down && docker compose up -d.
TCG_NETWORK_MTU=1420Public URL where the site is reached. SSO redirect URIs
(<url>/api/auth/oidc/<provider>/callback) and the SSO logout return address derive
from it: set it when SSO is enabled.
TCG_PUBLIC_URL=http://localhost:5173Session cookies are only sent over HTTPS. Set to false when the web UI is served over plain HTTP (local testing, LAN without TLS), otherwise web sign-in does not stick. The Android app uses tokens and is not affected.
TCG_COOKIE_SECURE=trueLifetime of access tokens (seconds) and of refresh tokens, i.e. how long a session stays signed in without activity (seconds).
TCG_ACCESS_TOKEN_TTL_SECONDS=3600TCG_REFRESH_TOKEN_TTL_SECONDS=7776000Reverse proxies (comma-separated addresses or networks, * for any) whose X-Forwarded-For / X-Forwarded-Proto headers are trusted. Set it to your proxy's address as the container sees it (often the Docker network gateway, e.g. 172.18.0.1) when Zukan runs behind one, so sign-in limits count real client addresses. Others cannot fake them.
TCG_FORWARDED_ALLOW_IPS=127.0.0.1Sign-in limits (0: none). Failed password sign-ins allowed per account name and per client address within the window (seconds), then refresh and SSO requests per address and minute. Past them the API answers 429 with Retry-After; counters reset on restart.
TCG_LOGIN_MAX_FAILURES=10TCG_LOGIN_IP_MAX_FAILURES=50TCG_LOGIN_WINDOW_SECONDS=900TCG_AUTH_REQUESTS_PER_MINUTE=60Largest request accepted (bytes); CSV imports are up to 5 MB.
TCG_MAX_REQUEST_BYTES=6291456Administrator created on start while the database has no user, so the first sign-in works. Change the password after signing in. Empty password: not created.
TCG_DEFAULT_ADMIN_USERNAME=adminTCG_DEFAULT_ADMIN_PASSWORD=changemeUsername/password sign-in. Turn off once SSO works, keeping an administrator able to
sign in through SSO (recovery: set back to true and run zukan create-admin). With it
off and one SSO provider, the login page goes straight to the provider.
Local and SSO accounts are separate: an SSO login never joins a local account. With it
off, administrators cannot create local accounts (SSO accounts are created at first
sign-in).
TCG_LOCAL_LOGIN_ENABLED=truePassword for zukan create-admin NAME (create or reset an administrator) without
the interactive prompt. Pass it on the command line rather than keeping it here:
docker compose exec -e TCG_ADMIN_PASSWORD=... api zukan create-admin NAME
TCG_ADMIN_PASSWORD=SSO providers, as a JSON list of names (lowercase letters, digits, - and _). A name
appears in URLs (/api/auth/oidc/<name>/callback). Empty: no SSO button.
TCG_OIDC_PROVIDERS=["home"]Each provider is then configured by TCG_OIDC_<NAME>_* variables, <NAME> being the
name in uppercase with - replaced by _. Example for "home":
Login button label (default: the name).
TCG_OIDC_HOME_DISPLAY_NAME=Home SSOIssuer URL (mandatory), discovered through <issuer>/.well-known/openid-configuration.
TCG_OIDC_HOME_ISSUER=https://sso.example.com/realms/homeConfidential client used by the web UI (mandatory) and its secret.
TCG_OIDC_HOME_CLIENT_ID=tcg-webTCG_OIDC_HOME_CLIENT_SECRET=change-mePublic client (PKCE) used by the Android app. Unset: no SSO in the app.
TCG_OIDC_HOME_MOBILE_CLIENT_ID=tcg-androidRequested scopes, JSON list. Authelia also needs "groups".
TCG_OIDC_HOME_SCOPES=["openid","profile","email"]Claims holding the username and the groups.
TCG_OIDC_HOME_USERNAME_CLAIM=preferred_usernameTCG_OIDC_HOME_GROUPS_CLAIM=groupsMembers of these groups are administrators, others users, synced on each SSO login. Empty: roles are managed in Zukan.
TCG_OIDC_HOME_ADMIN_GROUPS=[]Only members of these groups may sign in. Empty: everybody the IdP accepts.
TCG_OIDC_HOME_ALLOWED_GROUPS=[]Create an account on first SSO sign-in. Off: only SSO accounts created before sign in.
TCG_OIDC_HOME_AUTO_CREATE_USERS=truePage where users change their password and email at the provider, opened by the
"Edit my profile" button in Zukan's settings (no button when unset and not guessed). Default: guessed for Keycloak
(<issuer>/account) and Authentik (/if/user/#/settings); other providers need it.
TCG_OIDC_HOME_ACCOUNT_URL=https://sso.example.com/realms/home/accountAdministrators set when the worker runs on the Data sync page (every few hours, or at set times on chosen days). The values below are the defaults until a schedule is saved there.
Hours between catalog refreshes (large downloads, new sets are rare). After an upgrade that changes how a game's catalog is stored, that catalog is refreshed at once.
TCG_CATALOG_INTERVAL_HOURS=168Hours between price and owned card image refreshes.
TCG_SYNC_INTERVAL_HOURS=24Hours between refreshes of outdated cards (missed by the last catalog refresh, or with missing image or details), fetched again one by one. Opening such a card in the web UI also fetches it again (at most once an hour).
TCG_REFRESH_INTERVAL_HOURS=48Games to refresh, as a JSON list (e.g. ["mtg","sorcery"]). Empty: every installed game. Game ids: mtg, pokemon, pokemon-asia, pokemon-china, sorcery, lorcana, riftbound, yugioh, yugioh-ocg. "pokemon" or "yugioh" listed without the other games of its family means all of them (one game before 0.17.0).
TCG_WORKER_GAMES=[]Default time zone of the Data sync page's set times (IANA name).
TCG_SCHEDULE_TIMEZONE=UTCUser-Agent sent to card data APIs and SSO providers. Default: Zukan/<server version>.
TCG_USER_AGENT=Zukan/0.20.1GitHub repository whose Android releases (android-vX.Y.Z) the web UI offers for download and the app's update check reads. Empty: no download link, no update check.
TCG_ANDROID_RELEASE_REPO=FoxOps/zukanMaintenance page while a catalog refresh slows the server down: when API answers take longer than TCG_MAINTENANCE_LATENCY_MS (median over 20 s) during a catalog refresh, users see a maintenance page (the API answers them 503) until answers are fast again for 30 s. Administrators keep access, with a notice. Runs started on the Data sync page are run by the worker, never by the API.
TCG_MAINTENANCE_ENABLED=trueTCG_MAINTENANCE_LATENCY_MS=500MTG catalog: all_cards (~400 MB download, translated names needed to scan non-English cards) or default_cards (~80 MB, English names only).
TCG_MTG_CATALOG_BULK=all_cardsPokémon is three games, one per publisher and market: Pokémon TCG (international: English, French, German, Italian, Spanish, Portuguese), Pokémon TCG Asia (Japanese, Korean, Traditional Chinese, Thai, Indonesian) and Pokémon TCG China (Simplified Chinese). A set listed in several languages is fetched in full once (English, else Japanese, Traditional Chinese, Thai, Indonesian); the others add names and images. TCGdex languages of the international game:
TCG_POKEMON_LANGUAGES=["en","fr","de","it","es","pt"]TCGdex languages of the Asia game (~40k cards fetched one by one). Unset: the Asian languages of TCG_POKEMON_LANGUAGES (it held both before 0.17.0), else these. TCGdex also has "ko" and "zh-cn", nearly empty: Korean and Simplified Chinese come from the datasets below.
TCG_POKEMON_ASIA_LANGUAGES=["ja","zh-tw","th","id"]Korean cards (Asia game) from github.com/type-null/PTCG-database (~33 MB per catalog sync) and mainland China cards (China game) from github.com/duanxr/PTCG-CHS-Datasets (~10 MB; non-commercial use only). false skips them.
TCG_POKEMON_KOREAN=trueTCG_POKEMON_CHINESE=truePictures of Japanese printings TCGdex has none for (its newest sets), from the official Japanese card search (pokemon-card.com), at most this many requests a day, one a second. Its card list is kept under TCG_IMAGE_DIR/cache; the first fill takes a few days. false skips it.
TCG_POKEMON_OFFICIAL_JP=trueTCG_POKEMON_OFFICIAL_JP_DAILY_REQUESTS=300Thai, Indonesian and Traditional Chinese cards from the official Asian card search (asia.pokemon-card.com) and Korean cards from Pokémon Korea's (pokemoncard.co.kr), for the Asia game: names, numbers and pictures that join the Japanese printings (Korean, Traditional Chinese) or make sets of their own (Thai and Indonesian products). At most this many requests a day per language, one a second; what was read is kept under TCG_IMAGE_DIR/cache, the first fill takes a few days. false skips them.
TCG_POKEMON_OFFICIAL_ASIA=trueTCG_POKEMON_OFFICIAL_ASIA_DAILY_REQUESTS=500Yu-Gi-Oh! is two games: the TCG (Europe, the Americas, Oceania) and the OCG (Japan and Asia). Both read YGOPRODeck and YGOJSON (~57 MB per catalog sync, downloaded once for both when they sync within an hour). Languages of the TCG game: English, French, German, Italian and Portuguese names from YGOPRODeck, Spanish ones from YGOJSON.
TCG_YUGIOH_LANGUAGES=["en","fr","de","it","pt","es"]Languages of the OCG game (~85k printings): Japanese, Korean, Chinese and Asian English ("-AE" codes) sets from YGOJSON. Unset: the Asian languages of TCG_YUGIOH_LANGUAGES (it held both before 0.17.0) and "en", else these.
TCG_YUGIOH_OCG_LANGUAGES=["ja","ko","zh-hans","zh-hant","en"]Pages of the official Konami card database read a day (one a second) for the Japanese and Korean names and printings (OCG game) of cards newer than YGOJSON. Each card is read once (kept under TCG_IMAGE_DIR/cache). 0: never.
TCG_YUGIOH_KONAMI_DAILY_REQUESTS=300Tokens of external APIs (card data, prices) that need one. Names follow
TCG_APITOKEN_<SOURCE TYPE>[<CATEGORY>]<API NAME>
with SOURCE TYPE GAMES (card data) or PRICES (markets), CATEGORY the game for game
sources (MTG, PKM, SOR, RFB, YGO, LOR), none for sources covering several games, e.g.
TCG_APITOKEN_GAMES_MTG_SCRYFALL, TCG_APITOKEN_PRICES_CARDNEXUS. Scryfall, MTGJSON,
TCGdex, Sorcery, Riftcodex, Riot's card gallery, YGOPRODeck,
YGOJSON, the Konami database, LorcanaJSON, tcgcsv and Cardmarket files need no key. Without a key, the sources below are skipped.
Riftbound: Riot's content API (official). Needs a key Riot approved for a Riftbound product (developer.riotgames.com); development keys are refused. Without one, the Riftbound catalog comes from Riftcodex (riftcodex.com). How to get one: docs/riftbound.md.
TCG_APITOKEN_GAMES_RFB_RIOT=Card databases filling what each game's own sources miss (pictures, rarities, artists, TCGplayer ids), only for sets with gaps:
- tcg-api.eu (Pokémon, Magic, Lorcana; 10 000 requests a day on the free tier)
TCG_APITOKEN_GAMES_TCGAPIEU=TCG_TCGAPI_EU_DAILY_REQUESTS=9000- apitcg.com (Magic, Pokémon, Riftbound, Yu-Gi-Oh!, Lorcana; 1000 requests a month on the free tier: it runs after every other source and skips sets it found nothing for, 30 days)
TCG_APITOKEN_GAMES_APITCG=TCG_APITCG_MONTHLY_REQUESTS=900CardNexus (cardnexus.com → Settings → API Keys; Magic, Pokémon, Yu-Gi-Oh!, Sorcery, Lorcana, Riftbound): daily files of its catalog and prices, one API request per game and run, the files kept under TCG_IMAGE_DIR/cache. Adds the CardNexus price source (lowest listing, EUR) and Cardmarket / TCGplayer prices no other source has; in the catalog job, fills pictures, rarities and market ids and adds printings a set lacks. CardNexus has no Pokémon China, Korean, Thai or Indonesian cards (Japanese only, as "Pokemon Japan"). Pokémon and Pokémon Asia printings left without a Cardmarket price by TCGdex get one from Cardmarket's public price guide file (no key needed).
TCG_APITOKEN_PRICES_CARDNEXUS=TCG_CARDNEXUS_DAILY_REQUESTS=1000Database (compose builds it from POSTGRES_PASSWORD).
TCG_DATABASE_URL=postgresql+asyncpg://tcg:tcg@localhost:5432/tcgBuilt web UI, card image cache, and folder card images are imported from (image: /app/web, /data/images, /data/import). The image folder holds one folder per game: cards/, sets/, symbols/ and rarity/ (own pictograms there replace Zukan's, see README "Image folders").
TCG_WEB_DIR=../web/distTCG_IMAGE_DIR=data/imagesTCG_IMPORT_DIR=data/import64 documented assignments in .env.example · Core defaults · OIDC defaults.
The generic OIDC names use HOME as an example. Replace that segment with your configured provider prefix, not with a literal angle-bracket placeholder. Values such as change-me, example hostnames and client IDs must be replaced. External-provider free-tier limits quoted in source comments can change; the shown budget is Zukan configuration, not a guarantee from that provider.
Home · Troubleshooting · Documentation baseline · Source
Documented baseline: server 0.20.1 / Android 0.11.0. Game data and trademarks belong to their respective owners.
Collectors
- Getting started
- Catalog and card pages
- Managing your collection
- Prices and valuation
- CSV import and export
- Account and preferences
- Android guide
- Supported games and sources
- FAQ
Server administration
- Docker installation
- Configuration reference
- Reverse proxy and HTTPS
- Single sign on
- User administration
- Synchronization
- Data checks
- Images and symbols
- Operations
- Backup and restore
- Updates and migrations
- Troubleshooting
Technical reference