| # 📖 Alpha MC — Complete Setup Guide |
| > Everything explained: what every file does, how to set it up, and how it all works together. |
|
|
| --- |
|
|
| ## 📋 Table of Contents |
| 1. [How It All Works](#how-it-all-works) |
| 2. [File-by-File Explanation](#file-by-file-explanation) |
| 3. [Step-by-Step Setup](#step-by-step-setup) |
| 4. [HuggingFace Secrets Reference](#huggingface-secrets-reference) |
| 5. [Google Drive Backup Setup](#google-drive-backup-setup) |
| 6. [How to Connect and Play](#how-to-connect-and-play) |
| 7. [Troubleshooting](#troubleshooting) |
| 8. [FAQ](#faq) |
|
|
| --- |
|
|
| ## How It All Works |
|
|
| ``` |
| ┌─────────────────────────────────────────────────────┐ |
| │ HuggingFace Space (Docker) │ |
| │ │ |
| │ ┌──────────────┐ ┌─────────────────────────┐ │ |
| │ │ Minecraft │ │ Status Dashboard │ │ |
| │ │ Bedrock │ │ (port 7860, HTTP) │ │ |
| │ │ Server │ │ Shows: status, logs, │ │ |
| │ │ (UDP 19132) │ │ tunnel address, RAM/CPU │ │ |
| │ └──────┬───────┘ └─────────────────────────┘ │ |
| │ │ │ |
| │ ┌──────▼───────┐ ┌─────────────────────────┐ │ |
| │ │ playit.gg │ │ Cron Jobs │ │ |
| │ │ agent │ │ - Backup every 6 hours │ │ |
| │ │ (tunnel) │ │ - Log rotation weekly │ │ |
| │ └──────┬───────┘ └─────────────────────────┘ │ |
| └─────────┼───────────────────────────────────────────┘ |
| │ UDP tunnel |
| ▼ |
| playit.gg servers |
| │ |
| ▼ |
| Your friends' Minecraft (anywhere in the world) |
| ``` |
|
|
| **Why playit.gg?** HuggingFace Spaces only expose HTTP (TCP) ports publicly. Minecraft Bedrock uses UDP, which HF doesn't support directly. playit.gg acts as a free UDP tunnel — the Minecraft server connects outward to playit.gg, and players connect to playit.gg's address. |
|
|
| **Why port 7860?** HuggingFace watches this port. If nothing responds on 7860, it marks your Space as "idle" and pauses it. Our status dashboard runs on 7860 and keeps it alive. |
|
|
| --- |
|
|
| ## File-by-File Explanation |
|
|
| ### 🐳 `Dockerfile` |
| The main blueprint Docker uses to build your container. |
|
|
| **What it does, line by line:** |
| - `FROM ubuntu:22.04` — Starts with a clean Ubuntu Linux environment |
| - `ENV DEBIAN_FRONTEND=noninteractive` — Stops apt-get from asking questions during install |
| - `apt-get install` — Installs tools: curl/wget (downloading), unzip (extracting), python3 (status server + Drive backup), cron (scheduled jobs) |
| - `pip install` — Installs Python libraries for Google Drive API |
| - `mkdir -p` — Creates the folder structure: `/data/bedrock-server/`, `/data/scripts/`, `/data/logs/`, `/data/playit_gg/` |
| - `COPY prepare.sh ... && RUN bash prepare.sh` — Runs the download script at build time (downloads Minecraft + playit) |
| - `WORKDIR /data/bedrock-server` — Sets the default directory (fixes the path bug from before) |
| - `COPY server.properties allowlist.json permissions.json ./` — Copies game config INTO the server folder |
| - `COPY start.sh run.sh status_server.py ./` — Copies startup scripts into server folder |
| - `COPY backup.sh ... /data/scripts/` — Copies utility scripts to their own folder |
| - `chmod +x` — Makes all `.sh` scripts executable (runnable) |
| - `EXPOSE 7860 19132/udp 19133/udp` — Declares which ports the container uses |
| - `CMD ["bash", "/data/bedrock-server/start.sh"]` — The command that runs when the container starts |
|
|
| --- |
|
|
| ### 🔧 `prepare.sh` |
| Runs **once during Docker build** to download Minecraft and playit. |
|
|
| **What it does:** |
| 1. Fetches the Minecraft.net download page with a browser-like User-Agent (so the site doesn't block it) |
| 2. Tries 3 different URL patterns to extract the Bedrock server download link (in case the page layout changes) |
| 3. Downloads the `.zip` with retry logic (retries up to 4 times with exponential backoff: 5s, 10s, 20s) |
| 4. Extracts it to `/data/bedrock-server/` |
| 5. Downloads the latest `playit-linux-amd64` binary from GitHub releases |
| 6. Makes both executables |
|
|
| **Why it runs at build time:** So the Minecraft server binary is baked into the Docker image. Every time your Space restarts, Minecraft is already there — no download wait. |
|
|
| --- |
|
|
| ### 🚀 `start.sh` |
| The **main startup script** — this is what runs every time your Space starts. |
|
|
| **What it does, in order:** |
| 1. **Sets up graceful shutdown** — catches SIGTERM signal (when HF stops the container), runs a final backup, stops Minecraft cleanly before exiting |
| 2. **Prints a banner** with system info (CPU cores, RAM, disk) |
| 3. **Warns if SECRET_KEY is missing** so you know immediately if the tunnel won't work |
| 4. **Starts `status_server.py`** on port 7860 — the live dashboard (also prevents HF from pausing the Space) |
| 5. **Starts cron daemon** for scheduled backups and log rotation |
| 6. **Configures playit.toml** — tells playit.gg which local port to tunnel (19132 UDP) |
| 7. **Starts the playit agent** with your SECRET_KEY |
| 8. **Waits up to 180 seconds** for the tunnel to show a claim URL or live address, printing progress every 2 seconds |
| 9. **Starts `bedrock_server`** (the actual Minecraft process), redirecting its output to `/data/logs/minecraft.log` |
| 10. **Starts monitor.sh and health_check.sh** in the background |
| 11. **Keeps running** in a loop, updating the status JSON file every 30 seconds and watching if Minecraft/playit are still alive |
| |
| **Key fixes from original:** |
| - No `set -e` (crashes won't kill the whole script) |
| - Proper SIGTERM trap for graceful shutdown |
| - Status JSON written continuously so the dashboard has fresh data |
| |
| --- |
| |
| ### 🌐 `status_server.py` |
| A Python HTTP server that serves a **live web dashboard** on port 7860. |
|
|
| **What it shows:** |
| - 🟢/🔴 Server online/offline status |
| - RAM usage (used / total) |
| - Disk usage (used / total / percent) |
| - CPU load average |
| - Tunnel address to copy into Minecraft |
| - Last 40 lines of the Minecraft server log (auto-scrolled) |
|
|
| **How it works:** |
| - Reads `/tmp/mc_status.json` (written by `start.sh` every 30 seconds) |
| - Reads the last 40 lines of `/data/logs/minecraft.log` using the `tail` command |
| - Reads the playit log to extract the tunnel address |
| - Generates a full HTML page on every request |
| - Page auto-refreshes every 15 seconds via a `<meta refresh>` tag |
| - No dependencies needed beyond Python's built-in `http.server` |
|
|
| **Why this matters:** Without something running on port 7860, HuggingFace marks your Space as idle within minutes and pauses it. This keeps it alive 24/7. |
|
|
| --- |
|
|
| ### ⚙️ `server.properties` |
| The Minecraft Bedrock server configuration file. |
|
|
| | Setting | Value | Explanation | |
| |---|---|---| |
| | `server-name` | Gaganpreet's Minecraft Server | Name shown in the server browser | |
| | `gamemode` | survival | Players are in survival mode | |
| | `difficulty` | hard | Hard difficulty | |
| | `allow-cheats` | false | No commands like /gamemode | |
| | `max-players` | 20 | Up to 20 players at once | |
| | `online-mode` | true | Requires legitimate Xbox/Microsoft account | |
| | `white-list` | false | Anyone can join (set to true + edit allowlist.json to restrict) | |
| | `server-port` | 19132 | Main UDP port (IPv4) | |
| | `server-portv6` | 19133 | IPv6 UDP port | |
| | `view-distance` | 10 | Chunks loaded around each player | |
| | `tick-distance` | 4 | Active simulation distance | |
| | `player-idle-timeout` | 30 | Kick players idle for 30 minutes | |
| | `level-name` | Bedrock level | Name of the world folder | |
| | `level-seed` | *(blank)* | Random seed — fill in a number for a specific world | |
| | `server-authoritative-movement` | server-auth | Server controls player movement (anti-cheat) | |
|
|
| --- |
|
|
| ### 📋 `allowlist.json` |
| Controls who can join if `white-list=true` in server.properties. |
|
|
| **Default:** Empty array `[]` — whitelist is disabled so anyone can join. |
|
|
| **To add a player:** |
| ```json |
| [ |
| { |
| "ignoresPlayerLimit": false, |
| "name": "PlayerGamerTag", |
| "xuid": "2535123456789" |
| } |
| ] |
| ``` |
| Get a player's XUID from sites like xuidgrabber.com. |
|
|
| --- |
|
|
| ### 🔑 `permissions.json` |
| Sets operator/member/visitor permissions for specific players. |
|
|
| **Default:** Empty `[]` — everyone gets the `default-player-permission-level` from server.properties. |
|
|
| **To make someone an operator:** |
| ```json |
| [ |
| { |
| "permission": "operator", |
| "xuid": "2535123456789" |
| } |
| ] |
| ``` |
|
|
| --- |
|
|
| ### 💾 `backup.sh` |
| Creates world backups locally and optionally uploads to Google Drive. |
|
|
| **What it does:** |
| 1. Checks if the worlds folder exists and has data |
| 2. Creates a `.tar.gz` archive of the entire `worlds/` folder with a timestamp in the filename (e.g. `world_20240425_143000.tar.gz`) |
| 3. Saves it to `/data/bedrock-server/backups/` |
| 4. Prunes old backups — keeps only the 10 most recent |
| 5. If `GDRIVE_SA_KEY` secret is set → calls `google_drive_backup.py` to upload the new backup to Google Drive |
| 6. If `GDRIVE_SA_KEY` is not set → logs a helpful message and skips Drive upload gracefully |
|
|
| **Run manually:** `bash /data/scripts/backup.sh` |
|
|
| --- |
|
|
| ### ☁️ `google_drive_backup.py` |
| Uploads the latest backup `.tar.gz` to a Google Drive folder using a **Service Account** (no browser, works headlessly). |
|
|
| **How it's different from the original:** |
| - Original used `InstalledAppFlow` → needs a browser popup → **impossible in Docker** |
| - New version uses a **Service Account** → authenticates silently using a JSON key stored as a base64 HF Secret |
| - Supports **resumable uploads** with 5 MB chunks → handles large world files reliably |
| - Shows upload progress percentage |
| - Auto-deletes oldest Drive backups beyond 10 files |
| - Cleans up the temp credentials file after upload |
|
|
| **Required HF Secret:** `GDRIVE_SA_KEY` (base64-encoded service account JSON — see setup below) |
|
|
| --- |
|
|
| ### 🏥 `health_check.sh` |
| Runs in the background and **automatically restarts** Minecraft or playit if either crashes. |
| |
| **What it does (every 30 seconds):** |
| 1. Checks if `bedrock_server` process is running — if not, restarts it |
| 2. Checks if `playit` process is running — if not, restarts it with your SECRET_KEY |
| 3. Logs all events with timestamps to `/data/logs/health.log` |
| |
| **Why this matters:** Without this, if Minecraft crashes at 3am while your friends are playing, the server stays down until you manually restart the Space. With this, it auto-recovers within 30 seconds. |
| |
| --- |
| |
| ### 📊 `monitor.sh` |
| Logs system resource stats every 60 seconds to `/data/logs/monitor.log`. |
| |
| **Each log line contains:** timestamp, CPU load average, RAM used/total, disk used/total, Minecraft PID, playit PID. |
| |
| Useful for diagnosing performance issues or seeing exactly when the server went down. |
| |
| --- |
| |
| ### 🔄 `auto_restart.sh` |
| A simple utility script to restart Minecraft after a delay. |
|
|
| **Usage:** `bash /data/scripts/auto_restart.sh 10` — restarts after 10 seconds |
| Called automatically by health_check.sh when needed. |
| |
| --- |
| |
| ### 🕐 `crontab.txt` |
| Schedules automatic recurring tasks using cron. |
| |
| ``` |
| 0 */6 * * * bash /data/scripts/backup.sh |
| ``` |
| → Runs backup.sh every 6 hours (at 00:00, 06:00, 12:00, 18:00) |
| |
| ``` |
| 0 0 * * 0 truncate -s 0 /data/logs/*.log |
| ``` |
| → Every Sunday at midnight, clears all log files so they don't grow forever |
| |
| --- |
| |
| ### 🗺️ `run.sh` |
| A simple wrapper that changes directory to the Minecraft server folder and runs `start.sh`. Kept for compatibility. |
| |
| --- |
| |
| ### 🔧 `setup_google_drive.sh` |
| Run this **on your own computer** (not on HuggingFace) to walk through the Google Cloud setup and generate your `GDRIVE_SA_KEY`. It even base64-encodes the key file for you automatically if `service-account.json` is in the same folder. |
| |
| --- |
| |
| ## Step-by-Step Setup |
| |
| ### Step 1 — Upload all files to HuggingFace |
| |
| 1. Go to your Space: `https://huggingface.co/spaces/Gagan1322/alpha_mc` |
| 2. Click **Files** tab → **Add file** → **Upload files** |
| 3. Upload ALL of these files: |
| ``` |
| Dockerfile |
| README.md |
| prepare.sh |
| start.sh |
| run.sh |
| status_server.py |
| server.properties |
| allowlist.json |
| permissions.json |
| backup.sh |
| monitor.sh |
| health_check.sh |
| auto_restart.sh |
| crontab.txt |
| google_drive_backup.py |
| setup_google_drive.sh |
| ``` |
| 4. Commit the changes |
|
|
| --- |
|
|
| ### Step 2 — Add your playit Secret Key |
|
|
| 1. Go to your Space → **Settings** tab → **Repository secrets** |
| 2. Click **New secret** |
| 3. Name: `SECRET_KEY` |
| 4. Value: your playit agent secret key from `https://playit.gg/account/agents` |
| - **Regenerate your key first!** The old one was exposed in a screenshot. |
| 5. Click **Add secret** |
|
|
| --- |
|
|
| ### Step 3 — Restart the Space |
|
|
| 1. Go to the **App** tab of your Space |
| 2. Click the **⋮** (three dots) menu → **Restart Space** |
| 3. Wait 2-3 minutes for the Docker image to build (first time only) |
| 4. The Space URL will show your live status dashboard |
|
|
| --- |
|
|
| ### Step 4 — Get the Tunnel Address |
|
|
| 1. Open the Space URL in your browser |
| 2. Look at the dashboard — when the tunnel is live, the **Connect Address** section shows something like: |
| ``` |
| abc123.playit.gg:12345 |
| ``` |
| 3. If it shows a **claim URL** instead: |
| - Click that URL → sign into playit.gg → accept the tunnel |
| - Wait 10 seconds → the dashboard will show the live address |
|
|
| --- |
|
|
| ### Step 5 — Connect in Minecraft |
|
|
| 1. Open Minecraft Bedrock Edition on your phone/PC/console |
| 2. Go to **Play** → **Servers** tab → **Add Server** |
| 3. Server Address: paste the `*.playit.gg` address |
| 4. Port: paste the port number after the colon (or leave as 19132 if no port shown) |
| 5. Click **Save** → Join! |
|
|
| --- |
|
|
| ## HuggingFace Secrets Reference |
|
|
| Add these in: Space → Settings → Repository Secrets |
|
|
| | Secret Name | Required? | Description | |
| |---|---|---| |
| | `SECRET_KEY` | ✅ Yes | Your playit.gg agent secret key. Without this, nobody can join. | |
| | `GDRIVE_SA_KEY` | ❌ Optional | Base64-encoded Google Service Account JSON. Enables Drive backups. | |
|
|
| --- |
|
|
| ## Google Drive Backup Setup |
|
|
| ### Why Service Account instead of OAuth? |
| The old `credentials.json` approach uses OAuth's "installed app" flow which **opens a browser to sign in**. Docker containers have no browser. It can never work headlessly. A Service Account authenticates with a static JSON key — no browser, no interaction, just works. |
|
|
| ### Step-by-Step |
|
|
| **1. Create a Google Cloud Project** |
| - Go to https://console.cloud.google.com |
| - Top bar → Select project → New Project → name it `alpha-mc` → Create |
|
|
| **2. Enable Google Drive API** |
| - APIs & Services → Library → search "Google Drive API" → Enable |
|
|
| **3. Create a Service Account** |
| - IAM & Admin → Service Accounts → Create Service Account |
| - Name: `minecraft-backup` → Done |
| - Click the new account → **Keys** tab → Add Key → Create new key → **JSON** → Download |
| - This downloads something like `alpha-mc-abc123.json` — rename it to `service-account.json` |
|
|
| **4. Share a Drive Folder with the Service Account** |
| - Open Google Drive → New folder → name it exactly: `Minecraft-Bedrock-Backups` |
| - Right-click the folder → Share |
| - Open `service-account.json` and find the `"client_email"` field — copy that email address |
| - Paste it in the share dialog → role: **Editor** → Share |
|
|
| **5. Generate the GDRIVE_SA_KEY** |
|
|
| On your computer (Linux/Mac terminal or Git Bash on Windows): |
| ```bash |
| base64 -w 0 service-account.json |
| ``` |
| This prints a long string of random-looking characters. Copy ALL of it. |
|
|
| On Windows (PowerShell): |
| ```powershell |
| [Convert]::ToBase64String([IO.File]::ReadAllBytes("service-account.json")) |
| ``` |
|
|
| **6. Add to HuggingFace** |
| - Space → Settings → Repository Secrets → New secret |
| - Name: `GDRIVE_SA_KEY` |
| - Value: paste the base64 string |
| - Add secret → Restart Space |
|
|
| **7. Verify** |
| - Check `/data/logs/backup.log` after 6 hours |
| - Should show: `Google Drive upload successful!` |
| - Check your Google Drive folder — the backup `.tar.gz` should appear there |
|
|
| ### Security Rules |
| - ❌ NEVER commit `service-account.json` to git |
| - ❌ NEVER upload credentials/key files to HuggingFace files tab |
| - ❌ NEVER share your service account JSON publicly |
| - ✅ ONLY store it as a HF Secret (encrypted, not visible in git history) |
|
|
| --- |
|
|
| ## How to Connect and Play |
|
|
| ### Finding the Address |
| The address is shown in two places: |
| 1. **Status Dashboard** — open the Space URL in any browser |
| 2. **Space Logs** — in the App tab logs, look for `TUNNEL IS LIVE` |
|
|
| ### Sharing with Friends |
| Just give them the `*.playit.gg:PORT` address. They need: |
| - Minecraft Bedrock Edition (any platform — mobile, PC, Xbox, PlayStation, Switch) |
| - Add Server → paste address → join |
|
|
| ### Adding Friends to Allowlist (optional) |
| If you want only specific people to join: |
| 1. Set `white-list=true` in `server.properties` |
| 2. Edit `allowlist.json` to add their gamertags and XUIDs |
| 3. Re-upload both files and restart the Space |
|
|
| --- |
|
|
| ## Troubleshooting |
|
|
| ### Space shows "Paused" |
| - Click the Space → click **Run** or **Restart** |
| - Make sure `SECRET_KEY` is set in secrets |
| - The status dashboard on port 7860 should prevent future pausing |
|
|
| ### "Tunnel not found" / no address shown |
| - Check that `SECRET_KEY` is correct (regenerate on playit.gg if needed) |
| - Open Space logs → look for playit errors |
| - If you see a claim URL → click it and accept the tunnel on playit.gg website |
|
|
| ### Build fails (Docker build error) |
| - Check that ALL files listed in Step 1 are uploaded |
| - The most common cause: a file is missing so `COPY` fails |
| - Check the build logs for which `COPY` line failed |
|
|
| ### Server starts but nobody can join |
| - Make sure you're using the playit.gg address (NOT the HuggingFace URL) |
| - The HF URL is HTTP only — Minecraft needs the playit.gg address |
| - Check `online-mode` in server.properties — if `true`, players need a valid Microsoft account |
|
|
| ### Minecraft crashes (server goes offline) |
| - `health_check.sh` should auto-restart it within 30 seconds |
| - Check `/data/logs/minecraft.log` for the crash reason |
| - Common causes: out of RAM (HF free tier has limited RAM), corrupted world |
|
|
| ### Google Drive backup fails |
| - Check that `GDRIVE_SA_KEY` is set in HF Secrets |
| - Verify the Drive folder is named exactly `Minecraft-Bedrock-Backups` |
| - Verify the folder is shared with the service account email |
| - Check `/data/logs/backup.log` for the specific error message |
|
|
| ### Dashboard not loading / Space gets paused |
| - `status_server.py` might have crashed |
| - It should auto-recover but if not, restart the Space |
| - Check `/data/logs/status_server.log` for Python errors |
|
|
| --- |
|
|
| ## FAQ |
|
|
| **Q: Is this free?** |
| A: Yes! HuggingFace Spaces (free tier) + playit.gg (free tier) = $0/month. The limitation is HF's free tier has limited RAM (~16GB shared) and CPU. |
|
|
| **Q: Will the world be lost if the Space restarts?** |
| A: No! HuggingFace persistent storage keeps `/data` between restarts. Your world is safe. Backups are extra insurance. |
|
|
| **Q: How many players can join?** |
| A: Up to 20 (set in server.properties). The actual limit depends on the HF server's available RAM — more players = more RAM. |
|
|
| **Q: Can I use mods / behaviour packs?** |
| A: Yes! Upload your pack files to the Space and place them in the correct Minecraft Bedrock folder under `/data/bedrock-server/`. This requires a bit of manual setup. |
|
|
| **Q: How do I update the Minecraft server version?** |
| A: Rebuild the Space (Settings → Factory reboot). `prepare.sh` always downloads the latest version from Minecraft.net. |
|
|
| **Q: Can I change the world seed?** |
| A: Yes — set `level-seed=` in `server.properties` to any number before the world is first created. Once a world exists, changing the seed has no effect. |
|
|
| **Q: How do I give myself operator (admin) permissions?** |
| A: Edit `permissions.json` with your XUID and set `"permission": "operator"`. Alternatively, if you're the first person to join, type your gamertag in the server console — but there's no easy console access on HF. |
|
|
| **Q: The playit address changes every restart — how do I get a permanent address?** |
| A: Upgrade to playit.gg Pro ($3/month) for a permanent subdomain. On the free tier the address changes each time. |
|
|
| --- |
|
|
| *Alpha MC Setup Guide — All files written and debugged for HuggingFace Spaces + playit.gg* |
|
|