Date: 2026-05-11
Time: 15:54
This is a NeoForge Minecraft server provisioning script built on FTL2's automation framework. It handles the full lifecycle of a Minecraft server on Linode infrastructure: provisioning a VM, installing Java and NeoForge, configuring security (SSH hardening, fail2ban), setting up DNS, and managing the running server (start, stop, backup, restore, destroy). It was ported from an earlier hardcoded script, with all constants replaced by a ServerConfig object and progress reported via a log() callback.
The module exposes several top-level async functions, each taking an ftl automation handle, a ServerConfig, and a log callback:
# Full provision from scratch
await provision(ftl, config, log)
# Server lifecycle
await start_server(ftl, config, log)
alive = await verify_server(ftl, config, log)
await save_and_stop(ftl, config)
# World management
await restore_world(ftl, config, backup_path, log)
local_path = await backup_world(ftl, config, log)
# Player monitoring
count = await get_player_count(ftl, config) # returns int or None
# Teardown
await destroy(ftl, config, log)
The provision() function orchestrates seven sequential steps (infrastructure, minecraft install, utilities, security, swap, ownership, DDNS) plus an optional NeoForge install. Each step is idempotent — infrastructure checks ftl.state before creating a new Linode.
ServerConfig attributes used:
name — server label / Linode labeladmin_user — unprivileged admin account created on the VMlinodetype, linoderegion, linode_image — Linode instance specsserver_url — URL for vanilla server.jar downloadneoforge_url — URL for NeoForge installer jar (optional; skips NeoForge if absent)world_name — Minecraft world directory nameddns_hostname — Cloudflare DNS record to update (optional)slack_channel — Slack channel for status notificationsconfigdirpath — local directory containing config files (eula.txt, server.properties, whitelist.json, ops.json, userjvmargs.txt, run.sh, jail.local)get_ip() — returns the server's public IPConstants: TMUX_SESSION = "minecraft" — the tmux session name used for the server process.
Config files copied to remote: eula.txt, server.properties, whitelist.json, ops.json, userjvmargs.txt, and conditionally run.sh (only when not using NeoForge).
ftl.state.has() / ftl.state.get() / ftl.state.add() to avoid re-provisioning existing servers. destroy() cleans up state.root, creates admin user, hardens SSH (disables password auth, IPv4-only), then re-registers the host with the admin user.save-all, stop, list) are sent via tmux send-keys. Server liveness is checked with tmux has-session.backup_world() sends save-all and polls the server log for up to 30 seconds waiting for "Saved the game" confirmation before archiving. Proceeds with a warning if confirmation never appears.RuntimeError. The top-level provision() catches exceptions and posts to Slack before re-raising.config.slack_channel. Slack failures are silently swallowed (double-wrapped try/except).getplayercount returns None when the tmux session is down (server not running), 0 when running but no players matched, or the parsed player count.ServerConfig from ..config — the configuration dataclass driving all parameterization.updatednsrecord from ..cloudflare (lazily, inside step7ddns) for Cloudflare DNS updates.shell, command, copy, file, geturl, fetch, user, hostname, lineinfile, service, pip, swap, waitforssh, ansible.posix.authorizedkey, ansible.posix.firewalld, community.general.linode_v4, community.general.slack.ftl.add_host() to dynamically register the provisioned server into the minecraft group.ftl.state for persistent tracking of provisioned resources across runs.ftl2-servercraft), which provides the TUI, watchdog, and DNS integration layers that call these functions.