Setting it up¶
Full setup from scratch. Takes 1–2 hours depending on how familiar you are with the pieces.
Prefer an AI to walk you through it?
Clone this repo and open it in Claude Code, Cursor, or any AI coding assistant. Ask it to set everything up — it can read the scripts and docs and guide you interactively, run commands, and answer questions as you go. Way faster than following steps manually.
Choose your setup¶
Droplet size¶
Pick based on how many players you expect and whether you want mods.
| Size | RAM | Good for | Monthly |
|---|---|---|---|
s-1vcpu-2gb |
2 GB | 1–3 players, vanilla/Paper | $12 |
s-2vcpu-4gb |
4 GB | 1–10 players, vanilla/Paper | $24 ← recommended |
s-2vcpu-8gb |
8 GB | 10–20 players, or modpacks | $48 |
The 4 GB droplet is the sweet spot for a friend group server. Go 8 GB if you're running Forge modpacks — they leak memory.
Student? Get $200 free credit
The GitHub Student Developer Pack includes $200 in DigitalOcean credit — enough to run the 4 GB droplet for 8+ months. Apply with your .edu email, takes 1–3 days to approve.
Server type¶
| Type | Best for |
|---|---|
| PaperMC (default) | Best performance, supports plugins, vanilla clients connect without mods |
| Vanilla | Pure Mojang experience, no plugins, no performance patches |
| Fabric | Lightweight mods — performance mods like Sodium work here |
| Forge | Heavy modpacks — use 8 GB+ droplet, all players need the same mods installed |
| Purpur | Paper fork with more config options, same plugin compatibility |
This guide uses PaperMC. For other server types, swap in their jar at step 5 — everything else (backups, panel, hibernate/revive) works the same.
Minecraft version¶
No constraints here. Step 5 shows how to download any PaperMC version directly. Pick the latest stable, or pin to a specific version if your friends are on older clients.
What you need¶
Accounts
- DigitalOcean — personal access token with read + write scope
- Cloudflare — domain managed in Cloudflare; API token with Zone:DNS:Edit scoped to your zone
- Google account — Drive backup storage; free 15 GB tier is enough for most worlds
- Discord server (optional) — webhook URL for event notifications
Local tools
- Python 3.10 or later (no third-party packages; stdlib only)
- SSH key pair: one passphrase-free key for automation, one passphrase-protected for interactive use
Droplet
Tested on Ubuntu 22.04 LTS. A 4 GB / 2 vCPU / 80 GB SSD droplet runs Panel, Wings, and PaperMC comfortably for up to ~10 players. Smaller sizes are untested.
1. Provision the droplet¶
Create a 4 GB Ubuntu 22.04 droplet on DigitalOcean. Upload your automation SSH key during creation. Note the IP address.
2. Configure DNS¶
In Cloudflare, create two A records pointing to the droplet IP:
| Name | Proxy status | Purpose |
|---|---|---|
panel.yourdomain.com |
Proxied (orange) | Pterodactyl Panel |
play.yourdomain.com |
DNS only (grey) | Minecraft connections and Wings API |
The panel subdomain is proxied so Cloudflare handles TLS. The play subdomain must be DNS-only because Minecraft clients connect directly, and Wings needs a Let's Encrypt cert issued to the real IP.
3. Install Pterodactyl Panel¶
Follow the official panel installation guide. Use MariaDB and Redis as documented. Configure nginx with your panel domain.
For TLS on the panel, use a Cloudflare Origin Certificate (15-year validity, no renewal needed):
- Cloudflare → your domain → SSL/TLS → Origin Server → Create Certificate
- Save the certificate and key to the droplet
- Set Cloudflare SSL/TLS mode to Full (not Full Strict)
4. Install Wings¶
Follow the Wings installation guide. Set the node FQDN to your play subdomain (e.g., play.yourdomain.com).
For Wings TLS, use Let's Encrypt (required since the play subdomain isn't Cloudflare-proxied):
Add a certbot deploy hook so Wings restarts on renewal:
cat > /etc/letsencrypt/renewal-hooks/deploy/restart-wings.sh <<'EOF'
#!/bin/bash
systemctl restart wings
EOF
chmod +x /etc/letsencrypt/renewal-hooks/deploy/restart-wings.sh
5. Create the Minecraft server¶
Install a PaperMC egg from Pterodactyl's eggs repository or create a server manually. Enable RCON in startup variables (required by the backup script).
To use a specific PaperMC build that the egg's installer doesn't include:
SERVER_DIR=/var/lib/pterodactyl/volumes/<your-server-uuid>
curl -L -o "$SERVER_DIR/server.jar" \
"https://fill.papermc.io/v3/projects/paper/versions/<version>/builds/latest/download/type/application"
chown 988:988 "$SERVER_DIR/server.jar"
Warning
Do not use the panel's "Reinstall" button after this — it re-runs the egg's install script and overwrites the jar.
6. Configure restic and rclone¶
Install the tools:
Create a Google Cloud OAuth 2.0 client for rclone:
- Google Cloud Console → APIs & Services → Credentials → Create OAuth 2.0 Client ID, type Desktop
- Enable the Google Drive API
- Note the client ID and secret
Configure rclone with drive.file scope (rclone can only see files it created):
rclone config
# type: drive
# scope: drive.file (option 3)
# client_id and client_secret: from step above
# follow the auth URL
Initialize the restic repository on Drive:
Write the credentials file the backup script reads:
mkdir -p /etc/mc-backup
cat > /etc/mc-backup/backup.env <<EOF
PTERO_URL=https://panel.yourdomain.com
PTERO_KEY=<pterodactyl-client-api-key>
SERVER_ID=<first-8-chars-of-server-uuid>
WORLD_DIR=/var/lib/pterodactyl/volumes/<full-server-uuid>
RESTIC_REPOSITORY=rclone:gdrive:minecraft-backups
RESTIC_PASSWORD_FILE=/root/.restic-password
EOF
chmod 600 /etc/mc-backup/backup.env
echo '<your-restic-password>' > /root/.restic-password
chmod 600 /root/.restic-password
7. Deploy the droplet scripts¶
Optionally configure Discord notifications:
echo 'WEBHOOK_URL=https://discord.com/api/webhooks/...' > /etc/mc-backup/webhook.env
chmod 600 /etc/mc-backup/webhook.env
If this file is absent, all webhook calls silently no-op.
8. Enable systemd services¶
cp droplet/services/idle-monitor.service droplet/services/log-tailer.service /etc/systemd/system/
systemctl daemon-reload
systemctl enable --now idle-monitor log-tailer
Add a daily backup cron:
echo '0 6 * * * root /opt/mc-tools/backup.sh >> /var/log/mc-backup-cron.log 2>&1' \
> /etc/cron.d/mc-backup
Add scheduled restart crons (optional — broadcasts a countdown in-game before restarting):
echo '55 4,10,16,22 * * * root /opt/mc-tools/restart-with-warning.sh >> /var/log/mc-restart-cron.log 2>&1' \
>> /etc/cron.d/mc-backup
9. Configure the local .env¶
Getting the tokens¶
DigitalOcean API token Account → API → Tokens → Generate New Token. Read + Write scope. This token can create and destroy droplets on your entire account.
Cloudflare API token Profile → API Tokens → Create Token → Edit zone DNS template. Scope it to your specific zone only.
Pterodactyl client API key Panel → Account (top right) → API Credentials → Create API Key.
SERVER_ADDR and PANEL_URL drive the Cloudflare DNS update logic in revive.py — the script derives the domain and subdomains from these values, so no separate domain variable is needed.
Verify it works¶
Run a manual backup to confirm the whole chain works:
Then check snapshots:
ssh root@<droplet-ip> "source /etc/mc-backup/backup.env && \
export RESTIC_REPOSITORY RESTIC_PASSWORD_FILE && restic snapshots"
You should see at least one snapshot. If not, check /var/log/mc-backup.log.