📖 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
- How It All Works
- File-by-File Explanation
- Step-by-Step Setup
- HuggingFace Secrets Reference
- Google Drive Backup Setup
- How to Connect and Play
- Troubleshooting
- 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 environmentENV DEBIAN_FRONTEND=noninteractive— Stops apt-get from asking questions during installapt-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 APImkdir -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 folderCOPY start.sh run.sh status_server.py ./— Copies startup scripts into server folderCOPY backup.sh ... /data/scripts/— Copies utility scripts to their own folderchmod +x— Makes all.shscripts executable (runnable)EXPOSE 7860 19132/udp 19133/udp— Declares which ports the container usesCMD ["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:
- Fetches the Minecraft.net download page with a browser-like User-Agent (so the site doesn't block it)
- Tries 3 different URL patterns to extract the Bedrock server download link (in case the page layout changes)
- Downloads the
.zipwith retry logic (retries up to 4 times with exponential backoff: 5s, 10s, 20s) - Extracts it to
/data/bedrock-server/ - Downloads the latest
playit-linux-amd64binary from GitHub releases - 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:
- Sets up graceful shutdown — catches SIGTERM signal (when HF stops the container), runs a final backup, stops Minecraft cleanly before exiting
- Prints a banner with system info (CPU cores, RAM, disk)
- Warns if SECRET_KEY is missing so you know immediately if the tunnel won't work
- Starts
status_server.pyon port 7860 — the live dashboard (also prevents HF from pausing the Space) - Starts cron daemon for scheduled backups and log rotation
- Configures playit.toml — tells playit.gg which local port to tunnel (19132 UDP)
- Starts the playit agent with your SECRET_KEY
- Waits up to 180 seconds for the tunnel to show a claim URL or live address, printing progress every 2 seconds
- Starts
bedrock_server(the actual Minecraft process), redirecting its output to/data/logs/minecraft.log - Starts monitor.sh and health_check.sh in the background
- 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 bystart.shevery 30 seconds) - Reads the last 40 lines of
/data/logs/minecraft.logusing thetailcommand - 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:
[
{
"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:
[
{
"permission": "operator",
"xuid": "2535123456789"
}
]
💾 backup.sh
Creates world backups locally and optionally uploads to Google Drive.
What it does:
- Checks if the worlds folder exists and has data
- Creates a
.tar.gzarchive of the entireworlds/folder with a timestamp in the filename (e.g.world_20240425_143000.tar.gz) - Saves it to
/data/bedrock-server/backups/ - Prunes old backups — keeps only the 10 most recent
- If
GDRIVE_SA_KEYsecret is set → callsgoogle_drive_backup.pyto upload the new backup to Google Drive - If
GDRIVE_SA_KEYis 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):
- Checks if
bedrock_serverprocess is running — if not, restarts it - Checks if
playitprocess is running — if not, restarts it with your SECRET_KEY - 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
- Go to your Space:
https://huggingface.co/spaces/Gagan1322/alpha_mc - Click Files tab → Add file → Upload files
- 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 - Commit the changes
Step 2 — Add your playit Secret Key
- Go to your Space → Settings tab → Repository secrets
- Click New secret
- Name:
SECRET_KEY - Value: your playit agent secret key from
https://playit.gg/account/agents- Regenerate your key first! The old one was exposed in a screenshot.
- Click Add secret
Step 3 — Restart the Space
- Go to the App tab of your Space
- Click the ⋮ (three dots) menu → Restart Space
- Wait 2-3 minutes for the Docker image to build (first time only)
- The Space URL will show your live status dashboard
Step 4 — Get the Tunnel Address
- Open the Space URL in your browser
- Look at the dashboard — when the tunnel is live, the Connect Address section shows something like:
abc123.playit.gg:12345 - 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
- Open Minecraft Bedrock Edition on your phone/PC/console
- Go to Play → Servers tab → Add Server
- Server Address: paste the
*.playit.ggaddress - Port: paste the port number after the colon (or leave as 19132 if no port shown)
- 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 toservice-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.jsonand 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):
base64 -w 0 service-account.json
This prints a long string of random-looking characters. Copy ALL of it.
On Windows (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.logafter 6 hours - Should show:
Google Drive upload successful! - Check your Google Drive folder — the backup
.tar.gzshould appear there
Security Rules
- ❌ NEVER commit
service-account.jsonto 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:
- Status Dashboard — open the Space URL in any browser
- 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:
- Set
white-list=trueinserver.properties - Edit
allowlist.jsonto add their gamertags and XUIDs - Re-upload both files and restart the Space
Troubleshooting
Space shows "Paused"
- Click the Space → click Run or Restart
- Make sure
SECRET_KEYis set in secrets - The status dashboard on port 7860 should prevent future pausing
"Tunnel not found" / no address shown
- Check that
SECRET_KEYis 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
COPYfails - Check the build logs for which
COPYline 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-modein server.properties — iftrue, players need a valid Microsoft account
Minecraft crashes (server goes offline)
health_check.shshould auto-restart it within 30 seconds- Check
/data/logs/minecraft.logfor the crash reason - Common causes: out of RAM (HF free tier has limited RAM), corrupted world
Google Drive backup fails
- Check that
GDRIVE_SA_KEYis 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.logfor the specific error message
Dashboard not loading / Space gets paused
status_server.pymight have crashed- It should auto-recover but if not, restart the Space
- Check
/data/logs/status_server.logfor 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