Skip to content
Return to Projects
Case Study

IceLegends SMP

A vanilla-first Romanian Minecraft SMP built for clean 3–4 month seasons.

The Problem

Running a community SMP means balancing plugin depth against vanilla feel, managing a world-reset cycle, and keeping operations simple enough for a solo admin. IceLegends SMP solves this with 11 carefully chosen plugins, strict feature gating per a "vanilla-first" principle, and fully automated backups and world resets.

My Role

Designed, configured, and deployed solo — server architecture, 11-plugin selection and configuration, LuckPerms permission model, economy and nation design, Docker infrastructure, and Cloudflare Tunnel ingress for Dynmap.

Highlights

  • PaperMC 1.21 on Docker with a hard 11-plugin cap — every plugin is justified and none are redundant
  • Towny nations with seasonal quests (Quests + Citizens NPCs) and a single manually-spawned MythicMobs boss per season
  • AureliumSkills passive bonuses only — active abilities disabled to preserve vanilla combat (P-01)
  • DiscordSRV bridge logs every death, join, and chat event to dedicated Discord channels in real time
  • Daily automated backups with 7-day retention; resource world resets every Monday at 05:00 via cron

Stack

PaperMC 1.21DockerCloudflare TunnelLuckPermsTownyAureliumSkillsMythicMobsDiscordSRVDynmap

Constraints

  • Solo admin — every operation must complete in under 10 minutes (P-02).
  • Vanilla-first (P-01): if any plugin disappeared tomorrow, the game must continue unchanged.
  • 11-plugin hard cap — every addition must displace something or prove unique, irreplaceable value.

System Architecture

Edge
Cloudflare Tunnel (Dynmap web map)
TCP :25565 open (Minecraft clients)
Runtime
Docker + docker-compose
itzg/minecraft-server image
Core
PaperMC 1.21
Vault
LuckPerms
EssentialsX
CoreProtect
Gameplay
Towny
AureliumSkills
ShopGUI+
Quests + Citizens
MythicMobs
Multiverse-Core
Ops
DiscordSRV bridge
Daily backup cron (04:00)
Weekly world reset cron (Mon 05:00)

Key Trade-offs

The decisions worth defending — what I chose, what I turned down, and why.

Plugin count cap

Chose

Hard cap at 11 plugins

Rejected

Unlimited plugin stack

Each plugin is a maintenance surface and a potential conflict point. The cap forces prioritisation and keeps the dependency graph legible for a solo admin.

Public ingress for Dynmap

Chose

Cloudflare Tunnel (no open ports)

Rejected

Expose :8123 directly via UFW

No origin IP exposure, free DDoS protection, and zero firewall rule maintenance — the same pattern used by the IceLegends portfolio site.

AureliumSkills scope

Chose

Passive bonuses only, active abilities disabled

Rejected

Full AureliumSkills feature set including active combat abilities

Active abilities alter vanilla combat — a direct violation of P-01. Passive bonuses reward playtime additively without changing the combat model.

Season lifecycle

Chose

Full world reset every 3–4 months

Rejected

Incremental map expansion with no reset

Resets keep the world fresh, prevent terrain exhaustion, and give every new player an equal start — critical for community health in a small SMP.

What I'd Do Differently

An honest retrospective — the stuff I'd change with more time, more users, or a second pass.

  1. 1Add a staging compose stack so plugin updates can be tested before touching the live season — one bad update cost several hours of rollback work.
  2. 2Move backup storage off-site from day one; local-only archives are a single hardware failure away from a full season loss.
  3. 3Wire Dynmap as a named Docker service rather than relying on the host port, so the Cloudflare Tunnel config survives container IP reassignment on restart.

Technical Deep-Dive

Architecture, specifications, and implementation details.

11 — Permissions & Staff

"

LuckPerms groups, staff hierarchy, command reference

#Group Hierarchy

Admin       (weight 100) — full permissions, server owner
  └── Moderator   (weight 30) — ban/rollback/manage
        └── Helper     (weight 20) — inspect/mute/kick
              └── Jucator    (weight 10) — default, all players

#LuckPerms Setup Script

# scripts/setup-luckperms.sh
# Run once after first server start

# Create groups
/lp creategroup jucator
/lp creategroup helper
/lp creategroup moderator
/lp creategroup admin

# Set weights (display order)
/lp group jucator  setweight 10
/lp group helper   setweight 20
/lp group moderator setweight 30
/lp group admin    setweight 100

# Set prefixes (shown in TAB and chat)
/lp group jucator   meta setprefix '&7Jucător &8| '
/lp group helper    meta setprefix '&aHelper &8| '
/lp group moderator meta setprefix '&bModerator &8| '
/lp group admin     meta setprefix '&cAdmin &8| '

# Set default group
/lp group jucator setdefault true

# Helper permissions
/lp group helper permission set essentials.mute true
/lp group helper permission set essentials.kick true
/lp group helper permission set coreprotect.inspect true
/lp group helper permission set essentials.invsee true

# Moderator permissions (inherits Helper)
/lp group moderator parent add helper
/lp group moderator permission set essentials.ban true
/lp group moderator permission set essentials.tempban true
/lp group moderator permission set coreprotect.rollback true
/lp group moderator permission set coreprotect.restore true
/lp group moderator permission set multiverse.access.* true

# Admin permissions
/lp group admin parent add moderator
/lp group admin permission set * true

#Assigning Staff Ranks

# Promote a player to Helper
/lp user <username> group add helper

# Promote to Moderator
/lp user <username> group add moderator
/lp user <username> group remove helper     # remove lower rank

# Demote
/lp user <username> group remove moderator
/lp user <username> group add jucator

# View a player's permissions
/lp user <username> info

#Command Reference by Rank

##Helper Commands

CommandUse Case
/co inspectClick block to see who placed/broke it
/co lookup u:Name t:24hReview player activity last 24h
/mute Name 30m SpamTemp mute for spam/offensive language
/kick Name MotivKick player from server
/invsee NameView player inventory
/warn Name MotivIssue official warning

##Moderator Commands (+ all Helper)

CommandUse Case
/ban Name MotivPermanent ban (hacking/serious grief)
/tempban Name 7d MotivTemporary ban
/co rollback u:Name t:2hRollback grief (max 2h without Admin approval)
/co rollback u:Name r:20 t:24hRollback in 20-block radius
/ta town Name deleteDelete a town (extreme grief cleanup)

##Admin Commands (+ all Mod)

CommandUse Case
/lp user Name group add <rank>Assign any rank
/mm m spawn BossFinalSpawn season boss
/mv create resurse NORMALCreate resource world
/eco give Name 500Grant coins manually
/lp editorOpen web permission editor

#Staff Rules

  1. No personal advantage — staff commands are for administration, not gameplay benefit
  2. No public discussion of ban/unban decisions — use #staff-log Discord channel
  3. No spoilers — don't reveal quest storyline to players
  4. Log every action — all bans, kicks, rollbacks posted in #staff-log
  5. No op to players — under any circumstances
  6. Escalate uncertainty — if unsure, ask Admin before acting
"

⚠️ All contested decisions (unban, rank changes, economy adjustments) require Admin approval.

#EssentialsX Home Limits per Group

# Set via LuckPerms permission nodes
essentials.sethome.multiple.jucator: 3
essentials.sethome.multiple.helper: 5
essentials.sethome.multiple.moderator: 10
essentials.sethome.multiple.admin: unlimited

#Related Documents

  • Security (who has VPS access) → 12_security.md
  • Staff runbooks → 14_backup_recovery.md
~ End of Document ~
Kiwi