Skip to content

Quickstart

You need: Docker Engine with the Compose v2 plugin (docker compose version prints 2.x; the older standalone docker-compose is not supported), curl, and a Linux or macOS host — the stack runs as your own user id (id -u / id -g), which Docker Desktop on Windows/WSL also provides.

  1. Get the compose file

    Terminal window
    curl -fsSL https://raw.githubusercontent.com/Strandgaard96/mc-gamertime/main/docker-compose.yml -o docker-compose.yml

    No clone needed — this one file is the whole self-host setup.

  2. Set your admin credentials

    Create a file named .env next to docker-compose.yml containing:

    Terminal window
    ADMIN_USERNAME=admin
    ADMIN_PASSWORD=<your-password>

    Use at least 12 characters — the first-boot bootstrap does not check, but every later password change does. Compose auto-loads .env from the same directory. The first boot creates this admin account; afterwards the variables are ignored, so an existing password is never overwritten. Once you have logged in, delete both lines again so the password isn’t left on disk in plain text.

  3. Start the stack

    Terminal window
    docker compose up -d

    Pulls the pre-built image from GHCR and creates the SQLite database and storage directory under ./config/app/ on first run. Wait a few seconds for the mc-gamertime container to become healthy.

    Image tags: latest and X.Y.Z follow releases; main is the edge build from every commit. Pin image: ghcr.io/strandgaard96/mc-gamertime:X.Y.Z in docker-compose.yml if you want to control when you upgrade.

  4. Open the app

    Navigate to http://localhost:4263 and log in.

    That works on the host itself only: the port is bound to 127.0.0.1 by default. To open it from a phone or another machine on your LAN, add APP_BIND=0.0.0.0 to .env and run docker compose up -d again — see Configuration → Host port and bind address for what that exposes.

Container Role Port (internal)
mc-gamertime FastAPI + React frontend, embedded SQLite, local filesystem storage 4263
mc-gamertime-init One-shot, fixes ownership on config/app then exits none

Only mc-gamertime is exposed, at 127.0.0.1:4263 on the host (loopback only unless you set APP_BIND).

docker-compose.yml
services:
mc-gamertime-init:
container_name: mc-gamertime-init
image: alpine:3
user: root
command: >-
sh -c "mkdir -p /config/app &&
chown -R ${PUID:-1000}:${PGID:-1000} /config/app"
volumes:
- ./config:/config
restart: "no"
deploy:
resources:
limits:
memory: 32m
cpus: "0.1"
security_opt:
- no-new-privileges:true
cap_add:
- CHOWN
- DAC_OVERRIDE
cap_drop: [ALL]
mc-gamertime:
container_name: mc-gamertime
image: ghcr.io/strandgaard96/mc-gamertime:latest
# Must match init's chown target above, or the app can't write /data.
user: "${PUID:-1000}:${PGID:-1000}"
ports:
# APP_BIND=127.0.0.1 (default) keeps the port loopback-only for a reverse proxy on
# this host; APP_BIND=0.0.0.0 exposes it to the LAN. Docker port bindings bypass
# ufw/firewalld, so this line is the only firewall the app has.
- "${APP_BIND:-127.0.0.1}:${APP_PORT:-4263}:4263"
environment:
DB_BACKEND: sqlite
STORAGE_BACKEND: local
SECRETS_PROVIDER: env
ORIGIN_GUARD_ENABLED: "false"
PUBLIC_RECOMMENDED_ENABLED: "false"
BGG_TOKEN: ${BGG_TOKEN:-}
ADMIN_USERNAME: ${ADMIN_USERNAME:-}
ADMIN_PASSWORD: ${ADMIN_PASSWORD:-}
APP_BASE_URL: ${APP_BASE_URL:-}
SMTP_HOST: ${SMTP_HOST:-}
SMTP_PORT: ${SMTP_PORT:-587}
SMTP_USERNAME: ${SMTP_USERNAME:-}
SMTP_PASSWORD: ${SMTP_PASSWORD:-}
SMTP_FROM_ADDRESS: ${SMTP_FROM_ADDRESS:-}
METRICS_ENABLED: ${METRICS_ENABLED:-false}
FORWARDED_ALLOW_IPS: ${FORWARDED_ALLOW_IPS:-}
CORS_ALLOWED_ORIGINS: ${CORS_ALLOWED_ORIGINS:-}
volumes:
- ./config/app:/data
restart: unless-stopped
depends_on:
mc-gamertime-init:
condition: service_completed_successfully
deploy:
resources:
limits:
memory: 512m
cpus: "1.0"
reservations:
memory: 128m
cpus: "0.25"
security_opt:
- no-new-privileges:true
cap_drop: [ALL]