From 9e613f7f1b252f2c7aded29646f4d8c7c0cc8c59 Mon Sep 17 00:00:00 2001 From: LaijieJi Date: Sat, 11 Apr 2026 10:46:31 +0200 Subject: [PATCH 1/2] feat: minecraft service --- README.md | 1 + instructions.md | 274 ++++++++++++++++++++++++++++++++ services/minecraft/.env | 23 +++ services/minecraft/README.md | 79 +++++++++ services/minecraft/compose.yaml | 62 ++++++++ 5 files changed, 439 insertions(+) create mode 100644 instructions.md create mode 100644 services/minecraft/.env create mode 100644 services/minecraft/README.md create mode 100644 services/minecraft/compose.yaml diff --git a/README.md b/README.md index 7cf4a6bb..2a3fc15b 100644 --- a/README.md +++ b/README.md @@ -100,6 +100,7 @@ A huge thank you to all our contributors! ScaleTail wouldn’t be what it is tod | 📚 **BookLore** | A self-hosted application for managing and reading books. | [Details](services/booklore) | | 🎥 **Frigate** | A self-hosted NVR with real-time AI object detection for IP cameras and local video monitoring. | [Details](services/frigate) | | 🎮 **Hytale** | A self-hosted Hytale game server. | [Details](services/hytale) | +| 🎮 **Minecraft** | A self-hosted Minecraft Java Edition server accessible privately over your Tailnet — no port forwarding required. | [Details](services/minecraft) | | 🖼️ **Immich** | A self-hosted Google Photos alternative with face recognition and mobile sync. | [Details](services/immich) | | 📺 **Jellyfin** | An open-source media system that puts you in control of managing and streaming your media. | [Details](services/jellyfin) | | 📖 **Kavita** | An open-source, self-hosted digital library for comics, manga, and ebooks. | [Details](services/kavita) | diff --git a/instructions.md b/instructions.md new file mode 100644 index 00000000..61ff0f8f --- /dev/null +++ b/instructions.md @@ -0,0 +1,274 @@ +# Claude Code Instructions: ScaleTail Minecraft Service Contribution + +You are helping contribute a new service to the ScaleTail repository +(https://github.com/tailscale-dev/ScaleTail). ScaleTail is a collection +of Docker Compose configurations that run self-hosted services alongside +a Tailscale sidecar container, making them accessible privately over a +Tailnet without port forwarding. + +Your task is to create a complete, contribution-ready Minecraft server +entry following the exact structure and conventions of existing services +in the repo. + +--- + +## CONTEXT: How ScaleTail services are structured + +Every service in ScaleTail lives at `services//` and +contains exactly these files: + +- `docker-compose.yml` — the main compose file with Tailscale sidecar +- `README.md` — documentation for the service +- `.env` — environment variable defaults (committed with placeholder values) +- `config/serve.json` — Tailscale Serve config (only for HTTP services) + +The sidecar pattern is always the same: + +1. A `tailscale-` container runs the Tailscale client +2. The actual service container uses `network_mode: service:tailscale-` + to route all traffic through the Tailscale interface +3. The Tailscale container has a healthcheck on `http://127.0.0.1:41234/healthz` +4. Credentials go in a `.env` file; `TS_AUTHKEY` is always required + +**IMPORTANT technical difference from HTTP services:** Minecraft Java Edition +uses TCP port 25565. Tailscale Serve only supports HTTP/HTTPS proxying, +so a `serve.json` is NOT needed or appropriate here. Players connect +directly to the Tailscale node at `minecraft..ts.net:25565`. +Do NOT create a `config/serve.json` file for this service. + +--- + +## TASK: Create the following files + +### 1. `services/minecraft/docker-compose.yml` + +Use `itzg/minecraft-server:latest` as the Minecraft image (it's the +community standard with 1B+ pulls, supports Java Edition with Paper, +Fabric, Forge, Vanilla, etc.). + +The compose file must follow this exact structure pattern (derived from +existing ScaleTail services like radarr, vaultwarden, forgejo): + +```yaml +services: + tailscale-minecraft: + image: tailscale/tailscale:latest + container_name: tailscale-minecraft + hostname: minecraft # becomes the Tailnet device name + environment: + - TS_AUTHKEY=${TS_AUTHKEY} + - TS_STATE_DIR=/var/lib/tailscale + - TS_USERSPACE=false + # NO TS_SERVE_CONFIG — Minecraft uses raw TCP, not HTTP + volumes: + - ./config:/config + - ts-minecraft-data:/var/lib/tailscale + devices: + - /dev/net/tun:/dev/net/tun + cap_add: + - NET_ADMIN + - SYS_MODULE + # Optional DNS override block (commented out by default): + # dns: + # - 1.1.1.1 + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "http://127.0.0.1:41234/healthz"] + interval: 1m + timeout: 10s + retries: 3 + restart: unless-stopped + + minecraft: + image: itzg/minecraft-server:latest + container_name: minecraft + network_mode: service:tailscale-minecraft + environment: + - EULA=TRUE # Required — accepted by the user + - TYPE=${SERVER_TYPE} # VANILLA, PAPER, FABRIC, FORGE, etc. + - VERSION=${MINECRAFT_VERSION} # e.g. LATEST or 1.21.4 + - DIFFICULTY=${DIFFICULTY} + - MAX_PLAYERS=${MAX_PLAYERS} + - MOTD=${MOTD} + - MEMORY=${MEMORY} + - ONLINE_MODE=${ONLINE_MODE} # set false for cracked/offline servers + # Uncomment to also bind to the local network (outside Tailnet): + # ports: + # - "0.0.0.0:25565:25565" + volumes: + - minecraft-data:/data + depends_on: + tailscale-minecraft: + condition: service_healthy + restart: unless-stopped + +volumes: + ts-minecraft-data: + minecraft-data: +``` + +All environment variables must come from the `.env` file. Add inline +comments explaining non-obvious fields, matching the style of other +ScaleTail services (see the radarr compose for reference tone). + +--- + +### 2. `services/minecraft/.env` + +Include all variables referenced in the compose file with sensible +defaults and a comment for each line: + +``` +# Tailscale auth key — get one from https://login.tailscale.com/admin/settings/keys +TS_AUTHKEY= + +# Minecraft server type: VANILLA, PAPER, FABRIC, FORGE, SPIGOT, BUKKIT +SERVER_TYPE=PAPER + +# Minecraft version: LATEST or a specific version like 1.21.4 +MINECRAFT_VERSION=LATEST + +# Server difficulty: peaceful, easy, normal, hard +DIFFICULTY=normal + +# Maximum number of concurrent players +MAX_PLAYERS=10 + +# Message of the day shown in the server list +MOTD=A Minecraft Server on Tailscale + +# JVM memory allocation (increase for larger servers or more players) +MEMORY=2G + +# Online mode: true = requires valid Minecraft account, false = allows offline/cracked clients +ONLINE_MODE=true +``` + +--- + +### 3. `services/minecraft/README.md` + +Write a complete README following the exact style of other ScaleTail +service READMEs (see forgejo and vaultwarden for tone and structure). + +The README must include the following sections: + +**Header section:** +- A brief description of what Minecraft is +- A brief description of what this Docker Compose config does + (Tailscale sidecar, private Tailnet access, no port forwarding needed) + +**Key features section** (bullet list): +- Private multiplayer without opening router ports +- Friends connect using the Tailscale MagicDNS hostname +- Supports multiple server types (Vanilla, Paper, Fabric, Forge) +- Persistent world data in a named Docker volume +- Configurable via environment variables + +**Technical note on networking:** +Explain clearly that this service uses raw TCP on port 25565, NOT +Tailscale Serve/Funnel (which are for HTTP only). Players connect at +`minecraft..ts.net:25565`. This is the key distinction +from web-based services in the repo. + +**How it works section:** +Explain the sidecar pattern briefly — the tailscale container runs the +Tailscale client, the Minecraft container shares its network stack via +`network_mode: service:tailscale-minecraft`, and all traffic is routed +over the Tailnet. + +**Setup instructions:** +1. Clone the repo and navigate to `services/minecraft/` +2. Edit `.env` and paste in your Tailscale auth key (from https://login.tailscale.com/admin/settings/keys) +3. (Optional) Adjust SERVER_TYPE, MEMORY, MAX_PLAYERS in `.env` +4. Run `docker compose up -d` +5. Find your Tailnet name in the Tailscale admin console +6. Connect in Minecraft: Multiplayer → Add Server → `minecraft..ts.net` + +**Connecting section:** +- All players must be on the same Tailnet (or have been shared access to the node) +- ONLINE_MODE=false allows offline/non-premium accounts but reduces security +- Bedrock Edition uses UDP port 19132, which is not proxied through Tailscale Serve. + Bedrock players can still connect directly via the Tailscale IP on port 19132, + but this requires using the Bedrock edition of itzg/minecraft-server (set TYPE=BEDROCK) + and is outside the scope of this configuration. + +**Environment variables table:** +A markdown table documenting every variable in `.env`: + +| Variable | Default | Description | +|---|---|---| +| TS_AUTHKEY | _(empty)_ | Tailscale auth key from the admin console | +| SERVER_TYPE | PAPER | Server software: VANILLA, PAPER, FABRIC, FORGE, SPIGOT, BUKKIT | +| MINECRAFT_VERSION | LATEST | Game version: LATEST or a pinned version like 1.21.4 | +| DIFFICULTY | normal | Game difficulty: peaceful, easy, normal, hard | +| MAX_PLAYERS | 10 | Maximum concurrent players | +| MOTD | A Minecraft Server on Tailscale | Message shown in the server browser | +| MEMORY | 2G | JVM heap size — increase for larger worlds or player counts | +| ONLINE_MODE | true | Require valid Minecraft accounts (set false for offline/LAN play) | + +**Useful links section:** +- itzg/minecraft-server on Docker Hub: https://hub.docker.com/r/itzg/minecraft-server +- itzg/minecraft-server documentation: https://docker-minecraft-server.readthedocs.io +- Tailscale Serve docs: https://tailscale.com/kb/1242/tailscale-serve +- Tailscale Funnel docs: https://tailscale.com/kb/1223/funnel +- Tailscale Docker guide: https://tailscale.com/blog/docker-tailscale-guide + +--- + +### 4. Update the root `README.md` + +Open the root `README.md` of the repository. Find the table of available +services. Check whether a `### 🎮 Gaming` section already exists. + +- **If a Gaming section exists:** add the Minecraft row to its table. +- **If no Gaming section exists:** create a new section immediately before + `### 📱 Utilities` (or wherever makes sense alphabetically/thematically) + with the following header and table: + +```markdown +### 🎮 Gaming + +| 🎮 Service | 📝 Description | 🔗 Link | +| --- | --- | --- | +| 🎮 **Minecraft** | A self-hosted Minecraft Java Edition server accessible privately over your Tailnet — no port forwarding required. | [Details](/services/minecraft) | +``` + +Match the column order and formatting of existing tables exactly +(emoji + bold name | description | link). + +--- + +## CONSTRAINTS + +- Do NOT create a `config/serve.json` — this is a TCP game server, + not an HTTP service. Tailscale Serve is for HTTP proxying only. +- Do NOT include `TS_SERVE_CONFIG` in the compose environment. +- Use named volumes (not bind mounts) for both `minecraft-data` and + `ts-minecraft-data` — this is consistent with other ScaleTail services + and avoids file permission issues on different host systems. +- The `./config` bind mount for the Tailscale container is correct and + intentional — it stores the Tailscale config files that persist the + node identity across container restarts. +- Match the comment style and density of existing ScaleTail services — + helpful but not excessive. +- The contribution must be self-contained inside `services/minecraft/`. + The only other file to modify is the root `README.md`. + +--- + +## VALIDATION CHECKLIST + +After creating all files, verify the following before finishing: + +- [ ] `docker-compose.yml` references only variables defined in `.env` +- [ ] No `serve.json` file exists anywhere under `services/minecraft/` +- [ ] No `TS_SERVE_CONFIG` appears anywhere in the Minecraft service files +- [ ] The Tailscale healthcheck uses `http://127.0.0.1:41234/healthz` + (this is Tailscale's built-in health endpoint used by all services) +- [ ] The `minecraft` container has `depends_on` pointing to + `tailscale-minecraft` with `condition: service_healthy` +- [ ] The root `README.md` table row matches the column structure of + adjacent rows exactly +- [ ] All `.env` variables have a comment explaining their purpose +- [ ] The README networking section explicitly states this is TCP/raw + socket, NOT Tailscale Serve/Funnel diff --git a/services/minecraft/.env b/services/minecraft/.env new file mode 100644 index 00000000..b91cc26b --- /dev/null +++ b/services/minecraft/.env @@ -0,0 +1,23 @@ +# Tailscale auth key — get one from https://login.tailscale.com/admin/settings/keys +TS_AUTHKEY= + +# Minecraft server type: VANILLA, PAPER, FABRIC, FORGE, SPIGOT, BUKKIT +SERVER_TYPE=PAPER + +# Minecraft version: LATEST or a specific version like 1.21.4 +MINECRAFT_VERSION=LATEST + +# Server difficulty: peaceful, easy, normal, hard +DIFFICULTY=normal + +# Maximum number of concurrent players +MAX_PLAYERS=10 + +# Message of the day shown in the server list +MOTD=A Minecraft Server on Tailscale + +# JVM memory allocation (increase for larger servers or more players) +MEMORY=2G + +# Online mode: true = requires valid Minecraft account, false = allows offline/cracked clients +ONLINE_MODE=true diff --git a/services/minecraft/README.md b/services/minecraft/README.md new file mode 100644 index 00000000..82fd939f --- /dev/null +++ b/services/minecraft/README.md @@ -0,0 +1,79 @@ +# Minecraft Server with Tailscale Sidecar Configuration + +This Docker Compose configuration sets up a [Minecraft Java Edition](https://www.minecraft.net/) server with Tailscale as a sidecar container, enabling private multiplayer access over your Tailnet. No port forwarding or public IP required — only players on your Tailscale network can connect. + +## Minecraft + +[Minecraft](https://www.minecraft.net/) is one of the most popular sandbox games in the world, offering open-ended survival, building, and exploration gameplay. This configuration uses the [itzg/minecraft-server](https://hub.docker.com/r/itzg/minecraft-server) Docker image, the community standard with support for Vanilla, Paper, Fabric, Forge, and other server types. + +## Key Features + +* **Private multiplayer** — no router ports need to be opened; all traffic stays on your Tailnet. +* **MagicDNS hostname** — players connect using `minecraft..ts.net:25565`. +* **Multiple server types** — switch between Vanilla, Paper, Fabric, Forge, Spigot, and Bukkit via an environment variable. +* **Persistent world data** — world files and server config are stored in a named Docker volume. +* **Fully configurable** — server type, version, difficulty, player limit, MOTD, and memory are all set through `.env`. + +## Networking Note + +Unlike web-based services in this repository, Minecraft uses **raw TCP on port 25565**. Tailscale Serve and Funnel only proxy HTTP/HTTPS traffic, so they are **not used here**. Instead, the Minecraft server listens directly on the Tailscale interface and players connect at: + +``` +minecraft..ts.net:25565 +``` + +No `serve.json` configuration is needed for this service. + +## Configuration Overview + +In this setup, the `tailscale-minecraft` service runs the Tailscale client to join your private mesh network. The `minecraft` service is configured with `network_mode: service:tailscale-minecraft`, so all network traffic for the game server is routed through the Tailscale container. The Minecraft server binds TCP port 25565, which is reachable only from devices on your Tailnet. + +## Setup + +1. Clone the repository and navigate to the service directory: + + ```bash + git clone https://github.com/tailscale-dev/ScaleTail.git + cd ScaleTail/services/minecraft + ``` + +2. Edit `.env` and paste in your Tailscale auth key (from [https://login.tailscale.com/admin/settings/keys](https://login.tailscale.com/admin/settings/keys)). + +3. (Optional) Adjust `SERVER_TYPE`, `MEMORY`, `MAX_PLAYERS`, or other variables in `.env`. + +4. Start the stack: + + ```bash + docker compose up -d + ``` + +5. Find your Tailnet name in the [Tailscale admin console](https://login.tailscale.com/admin/machines). + +6. Connect in Minecraft: **Multiplayer** → **Add Server** → `minecraft..ts.net` + +## Connecting + +* All players must be on the same Tailnet (or have been shared access to the node). +* `ONLINE_MODE=false` allows offline/non-premium accounts but reduces security — only use this on trusted networks. +* Bedrock Edition uses UDP port 19132, which is not proxied through Tailscale Serve. Bedrock players can still connect directly via the Tailscale IP on port 19132, but this requires using the Bedrock edition of itzg/minecraft-server (set `TYPE=BEDROCK`) and is outside the scope of this configuration. + +## Environment Variables + +| Variable | Default | Description | +| --- | --- | --- | +| `TS_AUTHKEY` | _(empty)_ | Tailscale auth key from the admin console | +| `SERVER_TYPE` | `PAPER` | Server software: VANILLA, PAPER, FABRIC, FORGE, SPIGOT, BUKKIT | +| `MINECRAFT_VERSION` | `LATEST` | Game version: LATEST or a pinned version like 1.21.4 | +| `DIFFICULTY` | `normal` | Game difficulty: peaceful, easy, normal, hard | +| `MAX_PLAYERS` | `10` | Maximum concurrent players | +| `MOTD` | `A Minecraft Server on Tailscale` | Message shown in the server browser | +| `MEMORY` | `2G` | JVM heap size — increase for larger worlds or player counts | +| `ONLINE_MODE` | `true` | Require valid Minecraft accounts (set false for offline/LAN play) | + +## Useful Links + +* [itzg/minecraft-server on Docker Hub](https://hub.docker.com/r/itzg/minecraft-server) +* [itzg/minecraft-server documentation](https://docker-minecraft-server.readthedocs.io) +* [Tailscale Serve docs](https://tailscale.com/kb/1242/tailscale-serve) +* [Tailscale Funnel docs](https://tailscale.com/kb/1223/funnel) +* [Tailscale Docker guide](https://tailscale.com/blog/docker-tailscale-guide) diff --git a/services/minecraft/compose.yaml b/services/minecraft/compose.yaml new file mode 100644 index 00000000..af1d89ef --- /dev/null +++ b/services/minecraft/compose.yaml @@ -0,0 +1,62 @@ +services: +# Make sure you have updated/checked the .env file with the correct variables. +# All the ${ xx } need to be defined there. + # Tailscale Sidecar Configuration + tailscale-minecraft: + image: tailscale/tailscale:latest # Image to be used + container_name: tailscale-minecraft # Name for local container management + hostname: minecraft # Name used within your Tailscale environment + environment: + - TS_AUTHKEY=${TS_AUTHKEY} + - TS_STATE_DIR=/var/lib/tailscale + - TS_USERSPACE=false + # No TS_SERVE_CONFIG — Minecraft uses raw TCP, not HTTP + - TS_ENABLE_HEALTH_CHECK=true # Enable healthcheck endpoint: "/healthz" + - TS_LOCAL_ADDR_PORT=127.0.0.1:41234 # The : for the healthz endpoint + #- TS_ACCEPT_DNS=true # Uncomment when using MagicDNS + - TS_AUTH_ONCE=true + volumes: + - ./config:/config # Config folder used to store Tailscale files - you may need to change the path + - ts-minecraft-data:/var/lib/tailscale # Tailscale state data + devices: + - /dev/net/tun:/dev/net/tun # Network configuration for Tailscale to work + cap_add: + - net_admin # Tailscale requirement + # If any DNS issues arise, use your preferred DNS provider by uncommenting the config below + #dns: + # - 1.1.1.1 + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "http://127.0.0.1:41234/healthz"] # Check Tailscale has a Tailnet IP and is operational + interval: 1m # How often to perform the check + timeout: 10s # Time to wait for the check to succeed + retries: 3 # Number of retries before marking as unhealthy + start_period: 10s # Time to wait before starting health checks + restart: unless-stopped + + # Minecraft Server + minecraft: + image: itzg/minecraft-server:latest # Community standard Minecraft server image + container_name: minecraft # Name for local container management + network_mode: service:tailscale-minecraft # Sidecar configuration to route Minecraft through Tailscale + environment: + - EULA=TRUE # Required — accepted by the user + - TYPE=${SERVER_TYPE} # VANILLA, PAPER, FABRIC, FORGE, etc. + - VERSION=${MINECRAFT_VERSION} # e.g. LATEST or 1.21.4 + - DIFFICULTY=${DIFFICULTY} + - MAX_PLAYERS=${MAX_PLAYERS} + - MOTD=${MOTD} + - MEMORY=${MEMORY} + - ONLINE_MODE=${ONLINE_MODE} # Set false for cracked/offline servers + volumes: + - minecraft-data:/data # Persistent world and server data + # Uncomment to also bind to the local network (outside Tailnet): + #ports: + # - "0.0.0.0:25565:25565" + depends_on: + tailscale-minecraft: + condition: service_healthy + restart: unless-stopped + +volumes: + ts-minecraft-data: + minecraft-data: From bd8c97232ee30855845bd4bb0e488de14f09a021 Mon Sep 17 00:00:00 2001 From: LaijieJi Date: Sat, 11 Apr 2026 11:06:42 +0200 Subject: [PATCH 2/2] chore: defaulted server type to vanilla --- services/minecraft/.env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/minecraft/.env b/services/minecraft/.env index b91cc26b..4c1bb1a5 100644 --- a/services/minecraft/.env +++ b/services/minecraft/.env @@ -2,7 +2,7 @@ TS_AUTHKEY= # Minecraft server type: VANILLA, PAPER, FABRIC, FORGE, SPIGOT, BUKKIT -SERVER_TYPE=PAPER +SERVER_TYPE=VANILLA # Minecraft version: LATEST or a specific version like 1.21.4 MINECRAFT_VERSION=LATEST