Files
minecraft/docker-compose.yml
T

178 lines
6.3 KiB
YAML

name: minecraft
services:
# ── Minecraft server (Fabric via itzg/minecraft-server) ─────────────────
mc:
image: itzg/minecraft-server:latest
container_name: mc
restart: unless-stopped
stop_grace_period: 2m
tty: false
stdin_open: false
environment:
EULA: "TRUE"
TYPE: ${SERVER_TYPE:-FABRIC}
VERSION: ${MC_VERSION:-26.2}
MEMORY: ${MC_MEMORY:-6G}
USE_AIKAR_FLAGS: "true"
# Offline mode + whitelist as the approval gate (see docs/auth-flow.md).
# enforce-secure-profile must be off or offline-launcher players are
# rejected before EasyAuth can even prompt them (EasyAuth issue #68).
ONLINE_MODE: "FALSE"
ENFORCE_SECURE_PROFILE: "FALSE"
ENABLE_WHITELIST: "TRUE"
ENFORCE_WHITELIST: "TRUE"
MOTD: ${MC_MOTD:-A cozy server for friends}
DIFFICULTY: ${MC_DIFFICULTY:-normal}
MAX_PLAYERS: ${MC_MAX_PLAYERS:-10}
VIEW_DISTANCE: ${MC_VIEW_DISTANCE:-10}
SIMULATION_DISTANCE: ${MC_SIMULATION_DISTANCE:-8}
SEED: ${MC_SEED:-}
SPAWN_PROTECTION: "0"
# RCON is only reachable inside the compose network (never published)
ENABLE_RCON: "true"
RCON_PASSWORD: ${RCON_PASSWORD:?generate with scripts/setup.sh}
# Server-side mods, auto-resolved for the current MC version on every start.
# ":beta" allows beta-typed builds for that project (EasyAuth publishes
# its 26.x builds as beta).
MODRINTH_PROJECTS: ${MODRINTH_PROJECTS:-fabric-api,easyauth:beta,spark,lithium,ferrite-core}
MODRINTH_DOWNLOAD_DEPENDENCIES: required
# Modpack support (leave empty for lean Fabric; see docs/mods-and-modpacks.md)
MODRINTH_MODPACK: ${MODRINTH_MODPACK:-}
# Enforce mod configs on every boot (proxy-protocol/TCPShield settings —
# see docs/tcpshield.md). Mods that rewrite their own config files can't
# clobber these: patches re-apply before each server start.
PATCH_DEFINITIONS: /patches/patch-set.json
TZ: ${TZ:-UTC}
ports:
- "${MC_PORT:-25565}:25565"
# BlueMap web map (only active if the bluemap mod is installed)
- "${MAP_PORT:-8100}:8100"
volumes:
- ./data/mc:/data
- ./config/patches:/patches:ro
mem_limit: ${MC_CONTAINER_MEM_LIMIT:-7g}
# ── Scheduled world backups (itzg/mc-backup) ─────────────────────────────
backup:
image: itzg/mc-backup:latest
container_name: mc-backup
restart: unless-stopped
environment:
BACKUP_INTERVAL: ${BACKUP_INTERVAL:-24h}
INITIAL_DELAY: 10m
PRUNE_BACKUPS_DAYS: ${BACKUP_RETENTION_DAYS:-7}
BACKUP_NAME: world
TAR_COMPRESS_METHOD: gzip
PAUSE_IF_NO_PLAYERS: "false"
RCON_HOST: mc
RCON_PORT: "25575"
RCON_PASSWORD: ${RCON_PASSWORD}
EXCLUDES: "*.tmp,cache,logs"
TZ: ${TZ:-UTC}
volumes:
- ./data/mc:/data:ro
- ./data/backups:/backups
depends_on:
mc:
condition: service_healthy
# ── Web management dashboard ─────────────────────────────────────────────
dashboard:
build: ./dashboard
image: minecraft-dashboard:local
container_name: mc-dashboard
restart: unless-stopped
environment:
PANEL_SESSION_SECRET: ${PANEL_SESSION_SECRET:?generate with scripts/setup.sh}
PANEL_ADMIN_PASSWORD: ${PANEL_ADMIN_PASSWORD:-}
RCON_HOST: mc
RCON_PORT: "25575"
RCON_PASSWORD: ${RCON_PASSWORD}
MC_CONTAINER: mc
BACKUP_CONTAINER: mc-backup
MC_DATA_DIR: /mc-data
BACKUPS_DIR: /backups
APP_DATA_DIR: /app-data
PROJECT_DIR: /project
REGISTRATION_WINDOW_MINUTES: ${REGISTRATION_WINDOW_MINUTES:-2}
DISCORD_WEBHOOK_URL: ${DISCORD_WEBHOOK_URL:-}
TZ: ${TZ:-UTC}
ports:
# LAN/VPN only — do not port-forward this on your router (docs/networking.md)
- "${DASHBOARD_PORT:-8080}:8080"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./data/mc:/mc-data
- ./data/backups:/backups
- ./data/dashboard:/app-data
- ./:/project
mem_limit: 512m
depends_on:
- mc
# ── Public READ-ONLY mirror (portfolio deployment) ───────────────────────
# Opt-in via the "public" profile: `docker compose --profile public up -d`.
# This service is safe to expose to the internet. It serves ONLY the
# exported feed and CANNOT touch the server: no Docker socket, no project
# dir, no RCON password, no .env secrets. See docs/public-mode.md.
dashboard-public:
build: ./dashboard
image: minecraft-dashboard:local
container_name: mc-dashboard-public
restart: unless-stopped
profiles: ["public"]
environment:
PUBLIC_READONLY: "1"
FEED_PATH: /feed/feed.db
TZ: ${TZ:-UTC}
ports:
- "${DASHBOARD_PUBLIC_PORT:-8090}:8080"
volumes:
# Feed only, read-only. Deliberately NO docker.sock / project / secrets.
- ./data/feed:/feed:ro
mem_limit: 256m
healthcheck:
test: ["CMD", "curl", "-fsS", "http://127.0.0.1:8080/api/health"]
interval: 30s
timeout: 5s
start_period: 20s
depends_on:
- feed-export
# ── Feed exporter sidecar ────────────────────────────────────────────────
# Snapshots the LAN panel DB (read-only) into the public feed on an interval.
# Reads only the panel DB; writes only the feed volume. No server access.
feed-export:
build: ./dashboard
image: minecraft-dashboard:local
container_name: mc-feed-export
restart: unless-stopped
profiles: ["public"]
working_dir: /app
entrypoint: ["/bin/sh", "-c"]
command:
- 'while true; do node scripts/export-feed.mjs || echo "export failed"; sleep ${FEED_INTERVAL_SECONDS:-30}; done'
environment:
SOURCE_DB: /app-data/craftdeck.db
FEED_PATH: /feed/feed.db
MC_VERSION: ${MC_VERSION:-}
SERVER_TYPE: ${SERVER_TYPE:-}
TZ: ${TZ:-UTC}
volumes:
- ./data/dashboard:/app-data:ro
- ./data/feed:/feed
mem_limit: 128m
healthcheck:
test: ["CMD", "test", "-f", "/feed/feed.db"]
interval: 30s
timeout: 5s
start_period: 40s