Eternal Cloud
Eternal Cloud is a standalone node that replaces CloudNet. It is built into the API jar (EternalApi-<ver>.jar) and enabled in api.yml (see Installation). It starts proxy and game servers from templates and manages server versions and JDKs. Everything else – tasks, groups, templates, versions, Java, plugins – is done in the dashboard under Cloud.
The basic setup (JDK, versions, „Erste Einrichtung“, "initial setup") is described as step 7 in the Installation. This page explains the concepts and features behind it.
Environments and software
| Environment | Software | Plugins/mods | Note |
|---|---|---|---|
| Minecraft server | Paper, Purpur, Folia, Pufferfish, Leaf, Spigot, Vanilla | plugins/ | Eternal plugin is included |
| Modded server | Fabric, Quilt, NeoForge, Forge, SpongeVanilla | mods/ | Packages from mcjars.app; no Eternal plugin |
| Limbo | LOOHP Limbo | plugins/ | Waiting/fallback server without a world |
| Bedrock server | PowerNukkitX | plugins/ | UDP port from 19133, xbox-auth via task property |
| BungeeCord | BungeeCord, Waterfall | plugins/ | Eternal proxy plugin registers backends |
| Velocity | Velocity | plugins/ | Eternal plugin is included (the same Eternal.jar), registers backends; legacy forwarding |
| WaterdogPE | WaterdogPE | plugins/ | Bedrock proxy, backends by hand in config.yml |
Sources include: PaperMC Fill (Paper, Folia, Velocity, Waterfall), Purpur API, Mojang (Vanilla), SpigotMC Jenkins (BungeeCord), mcjars.app (Spigot, Pufferfish, Leaf, Fabric, Quilt, NeoForge, Forge, Sponge, Limbo), GitHub Releases (PowerNukkitX, WaterdogPE).
Tasks, groups, templates
| Term | Meaning |
|---|---|
| Task | Blueprint for services: environment, version, templates, groups, RAM, Java, min/max count, ports, static/dynamic, auto-stop, fallback, maintenance. Services are named <Task>-<Nr>. |
| Group | Attaches templates, JVM flags and process arguments to several tasks (by name or by environment: Global-Server matches all Minecraft servers). |
| Template | Folder cloud/templates/<Prefix>/<Name>/, copied into the service on start. Plugins go into plugins/, worlds as folders. File browser and editor in the dashboard; SFTP works just as well. |
| Fallback | Task toggle. The proxy sends players to the emptiest fallback service on join, on kick/crash and via /hub, /lobby, /l. |
| Maintenance | Task toggle. Entry only with eternal.cloud.maintenance. |
| View distance | Task field (2–32 chunks) for view-distance, plus the simulation distance; empty = whatever server.properties says. From 16 chunks the node raises Paper's chunk sending rate (75 → 400 chunks/s per player, only while Paper's default is still set) – otherwise 32 chunks take almost a minute. |
| Whitelist | Always off on cloud servers: the node writes white-list=false on every start, the plugin switches it off and refuses /whitelist on. Maintenance is the way to close the network. |
Static vs. dynamic
- Dynamic service: folder
cloud/services/<Name>/, built from the templates on every start and deleted after stopping. - Static service: folder
cloud/static/<Task>/<Name>/, kept (important for worlds!). Templates only on the first start, unless an entry isalwaysCopy. In the dashboard under Services → „Statische Services“ ("Static services") with a file browser.
Setup mode (dynamic tasks)
Just a new lobby map? Upload it under Welten → Lobby-Karte instead, see Lobby – no setup server needed.
Dynamic servers (lobby, minigames …) start fresh from the template every time. To build them there is setup mode under Cloud → Tasks → task:
- Start setup: an extra server
<task>-Setup-1starts from the task's templates. It keeps its folder, runs in maintenance (only witheternal.cloud.maintenance) and is no fallback. The normal services keep running. - Build: join with
/server <task>-Setup-1, build, configure plugins, e.g. set the lobby spawn. - Finish setup: the server saves, stops, and its folder (worlds, plugin settings) is copied into the template. Logs, the server jar, crash reports and the Eternal database stay out. Optionally the running services restart afterwards.
- Discard stops the setup server and deletes it; nothing is copied.
The target is the template named like the task, otherwise the task's last one; with several templates it can be chosen first. Proxies, static tasks and gamemodes (they have their own setup) have no setup mode.
Versions & Java
- Cloud → Java: install JDKs (Adoptium/Temurin). On start, the node automatically picks a suitable Java according to the version boundaries: 1.8–1.16 → 17 (the Eternal plugin needs Java 17; Paper's own Java check is skipped), 1.17 → 16+, 1.18–1.20.4 → 17+, 1.20.5–1.21 → 21+, 26.x → 25+ (or the task setting).
- Cloud → Versionen ("Versions"): install server software. Paper jars are pre-patched once (
cache/,versions/,libraries/); after that, starting a service is just copying.
Node: RAM budget and restart
At the top of the cloud you see the RAM the running services use next to the node's budget. With eternal.web.cloud.manage you change both in the dashboard:
- RAM budget: click the RAM chip. The budget covers all services together, 0 = unlimited. It applies at once: new starts are checked against it, running services keep running. It is saved in
api.yml(cloud.max-memory-mb), so it survives a restart too. - Restart node ("Node neu starten"): restarts API and cloud. Every service of this node is stopped and players are disconnected, then the services come back with their minimum count. This only works when the API runs under a service manager, i.e. systemd with
Restart=always(as intools/api/eternal-api.service) or Docker with a restart policy. If the dashboard detects none, it warns first. Under another manager setETERNAL_SUPERVISED=1to drop the warning.
Smart scaling
In the task editor:
preparedServicesbuilds service folders in advance (status PREPARED, no JVM) – starting is then just the JVM start.percentOfPlayersForNewServicestarts another service at this average load (spacing viastartDelaySeconds).autoStopEmptySecondsstops empty extras, never belowminServiceCount.replaceIngame: services in status INGAME do not count toward the minimum; a replacement is started.
Ingame status: /service ingame (permission eternal.cloud.service) removes a service from the fallback pool; /service lobby releases it; /service extra <Text> sets a status text.
Deployments & inclusions
- Deployments: on stop (or via „Deploy“ in the dashboard;
save-allbeforehand), the service folder is copied into the specified templates. Never copied:server.jar,cache/,libraries/,versions/,logs/,session.lock,server.properties, the Eternal database; plus your own exclusions (ordner/,datei,*.log– i.e. folder, file, pattern). - Inclusions: URLs downloaded into the service before start (map zip, plugin jar);
cachekeeps them undertemp/inclusions/. - Delete after stop: relative paths that are removed before the deployments.
Cluster (several nodes)
Several machines, one dashboard:
- The head is the normal node with dashboard and database. It decides which node gets which service.
- Workers only run services. They have no HTTP port and no dashboard of their own.
Setting up:
- Head: enable it in its
api.yml, then restart the API. The cluster secret is created incluster-secret.txt.
``yaml cluster: enabled: true role: head ``
- Worker: install Java 21+ on the new machine and drop the API jar there.
- Copy the head's api.yml. The worker needs the same
database(MySQL) and the sameinternal.shared-secret, because tasks and groups are stored signed with it in the database. - Adjust:
cloud.node-name- the addresses (
host-address,backend-address,public-address) cloud.api-url: the head's API as the servers reach it- paste the block from Cloud → Nodes → Worker hinzufügen (with head URL and secret)
- Start it, best as a service like the head. The worker shows up under Cloud → Nodes.
How the head distributes:
- New services go to the allowed node with the most free memory budget. Which nodes are allowed is set in the task under Nodes (empty = all).
- Docker tasks only go to nodes with Docker.
- Static services stay on the node that has their folder.
- What the worker fetches itself: templates (changed ones only, compared by hash),
cloud/Eternal.jarand missing server versions. - What goes back to the head: deployments land in the head's templates, dumps and crash reports under Cloud → Reports.
- Everything in the dashboard: console, commands, stop, restart, kill and dumps work for every service as for a local one.
- Players: servers on workers register with the head. Proxies get every backend with its node's backend address.
When a worker drops out:
- Its services stay in the list for 2 minutes, since they most likely keep running.
- If it comes back, the head reconciles.
- If it stays away, the head writes the services off and starts replacements on other nodes.
- When a node restarts after a crash, it ends server processes left over from its last run. This applies to every node, with or without a cluster.
Cloud → Nodes shows every node with memory, services, Docker, addresses and version. There you set a worker's memory budget and remove disconnected workers.
The head detects a different internal.shared-secret on a worker. The nodes page shows it in red: the worker then reads no tasks. Take the api.yml from the head.
Docker services
Instead of a JVM on the node, every service of a task runs in its own Docker container. Set it in the task editor under Ressourcen → Laufzeit: Docker-Container (resources → runtime).
- Image: empty means
eclipse-temurin:<java>-jre. The Java version comes from the task setting or automatically from the server version, e.g. 21 for 1.21. - Own image: possible, e.g.
eclipse-temurin:21-jdk. Heap dumps work then too. - Folder: the service folder is mounted at the same path. Templates, deployments, static services, console, commands and logs work as usual.
- Network: the container uses the host network, so the server listens on the port the node assigns. Proxy and API reach it like a normal process.
- Security:
- The container runs as the service user (
run-as-user), without Linux capabilities and withno-new-privileges. - The memory limit is RAM + 25 % (at least + 256 MB).
- Credentials go through an env file that is deleted after the start. They never appear on the command line.
- The container runs as the service user (
- Clean-up: stop and kill remove the container. If one is left after a node crash, the node removes it on its next start.
Docker services need a Linux node with Docker (the docker command must work for the API). On Docker Desktop (Mac/Windows) "host network" is the internal VM. The server starts there, but neither the API nor the proxy can reach it. The cloud overview shows whether the node has Docker.
Disk space
A full disk breaks everything: worlds stop saving and every console fills with errors. The cloud watches it:
- Display: the header of Cloud shows the free space. Below 5 GB it turns red, with a notice and a warning in the log every 10 minutes.
- Below 1 GB no service starts, neither automatically nor by hand. Running services keep running.
- Cleanup (hourly, every 5 minutes when space is short): old logs (30 kept each), spark temp files, crash reports (10 kept each), heap dumps (
*.hprof) older than a day, old files in the temp folder. - Spark: Paper's bundled profiler records in the background all the time. The cloud switches that background recording off (
plugins/spark/config.json→backgroundProfiler: false)./spark profilerstill works. If the key is already in the file, it stays as it is. - To find what else takes space (backups, worlds, database), run
du -xh -d1 / | sort -h | tailon the server.
Backups
Cloud → Backups backs up automatically:
- the cloud: templates, tasks, groups,
api.yml(passwords removed) and the H2 database - every static server (worlds!)
Schedule: interval and hour, how many zips to keep per target, folder. Running Minecraft servers save first and pause autosave while they are zipped.
Per backup: download it, protect it from deletion (pin), restore it (the old state stays as <name>.before-restore) or upload your own zip.
In a cluster: static servers on workers show up with @ <node>.
- Backing up: the worker zips the folder and sends the zip to the head.
- Restoring: the worker fetches the zip from the head and restores it on its machine.
Offsite (S3)
Under Offsite (S3) every new backup also goes to an S3 bucket. If the machine fails, the backups are then somewhere else.
| Field | Meaning |
|---|---|
| Endpoint | empty for AWS S3, otherwise the provider's URL: Backblaze B2 https://s3.<region>.backblazeb2.com, Cloudflare R2 https://<account>.r2.cloudflarestorage.com, Wasabi, MinIO http://minio:9000 |
| Region | e.g. eu-central-1; R2: auto |
| Bucket, access key, secret key | credentials with write, read and delete rights on the bucket |
| Folder in the bucket | prefix, e.g. eternal |
| Keep per target (S3) | this many backups per target stay in the bucket; pinned ones do not count |
Verbindung testen (test connection) writes, lists and deletes a small test object. The secret key stays on the node, the dashboard only shows ********. Uploads are streamed, so a big world backup takes no memory (up to 5 GB per file).
Metrics (Prometheus, InfluxDB)
For Grafana & co. in the head's api.yml:
metrics:
enabled: true
token: "long-random-string" # at least 16 characters
influx: # optional
url: "http://127.0.0.1:8086"
org: "my-org"
bucket: "eternal"
token: "influx-token"
interval-seconds: 30
- Prometheus:
GET /metricswithAuthorization: Bearer <token>or?token=<token>. - InfluxDB 2: with
influx.url, the same values go to its write API everyinterval-seconds(line protocol).
| Metric | Content |
|---|---|
eternal_services{task,node,state} | number of services per task, node and state |
eternal_service_players · _max_players · _memory_mb · _running · _uptime_seconds | per service |
eternal_players_online | players on all proxies |
eternal_node_online · _memory_used_mb · _memory_budget_mb | per node (head and workers) |
eternal_api_heap_used_bytes · _heap_max_bytes · _uptime_seconds | the API itself |
Reports & dumps
Cloud → Reports:
- Node report: a zip with
- node, OS, Java, memory and disk facts
- every service, task and group (passwords, tokens and keys masked)
- the node log and the API's threads
- the last 1000 console lines of each running service
- Crash report: created automatically when a service dies while starting or exits with an error code (not on stop/kill). It holds the last console lines,
logs/latest.log, the newestcrash-reports/andhs_err_pid*.log. It is written before the folder is deleted, so the evidence survives. - Thread dump and heap dump: in a service's console (Cloud → open a service).
- The thread dump shows where a hanging server is stuck. It uses the service Java's
jcmd, otherwiseSIGQUIT. - The heap dump (
.hprof) is as big as the used RAM, the server pauses briefly while it is written. It needs a JDK, not just a JRE.
- The thread dump shows where a hanging server is stuck. It uses the service Java's
The newest 30 files are kept. Download and delete on the page, permission eternal.web.cloud.manage (dashboard only, not in game).
External plugins
Cloud → Plugins: library cloud/plugins/ (upload in the dashboard or via SFTP). The dashboard reads plugin.yml/bungee.yml/velocity-plugin.json and shows name, version and the templates the plugin is in. „Installieren“ ("Install") copies the jar into the selected templates and removes older versions there. The Katalog ("Catalog") searches Modrinth; without a search term it shows recommendations in four groups:
| Group | Plugins |
|---|---|
| Welt & Bau (world & building) | WorldEdit, FastAsyncWorldEdit, WorldGuard, Multiverse-Core/-Inventories/-Portals, Chunky, CraftBook |
| CityBuild | PlotSquared, CoreProtect, GriefPrevention, QuickShop-Hikari, ChestShop, zAuctionHouse, PlayerVaultsX, GSit, SilkSpawners, Head Database, CrazyCrates, VeinMiner, Sleeper, ChestSort, DecentHolograms |
| Lobby | Citizens, FancyNpcs, FancyHolograms, DeluxeHub, DeluxeMenus, CommandPanels, UltraCosmetics, SkinsRestorer, TAB, ajQueue, ajLeaderboards, ImageFrame, Parkour |
| Technik (technical) | PlaceholderAPI, VaultUnlocked, ProtocolLib, PacketEvents, NBT-API, ViaVersion/-Backwards/-Rewind, spark, Geyser, Floodgate, Simple Voice Chat, BlueMap, Dynmap, DiscordSRV, Grim, LuckPerms, EssentialsX |
Modrinth plugins: choose a version → Herunterladen ("Download"; one click, lands in the library) → install into templates. In the version list, the version already in the library is marked In Bibliothek ("in library"), one installed in templates Installiert ("installed", with the templates).
Auto-update
The Auto-Update bar in the library keeps Modrinth plugins up to date:
- Every library jar is recognised at Modrinth by its checksum, including uploaded or URL-loaded Modrinth builds. Jars Modrinth doesn't know (PlotSquared, Citizens, own builds) show „ohne Auto-Update“ ("no auto-update").
- It checks every 6 hours and on Jetzt prüfen ("check now"). It looks for the newest build for the same loaders and the Minecraft version of the tasks whose templates have the plugin (proxy plugins: any version). A build is never less stable than the installed one (release stays release).
- für alle Plugins ("for all plugins") turns auto-update on for every plugin without its own setting. The arrow icon in a row switches it for that plugin only.
- Without auto-update the row shows Update verfügbar ("update available") with the Aktualisieren ("update") button.
- An update downloads the jar from Modrinth, checks its SHA-512 and replaces it in the library and in every template that had the plugin. Running servers keep the old jar until their next restart; until then the row says „Update installiert · wirkt nach Neustart“ ("installed, takes effect after restart").
- Verlauf ("history") lists every update (who, from → to, templates, errors) and is searchable. Everything is stored in
cloud/plugins/.updates.json.
PlotSquared and Citizens are not available on any download API (PlotSquared only on SpigotMC, Citizens on its own Jenkins). Their cards link to the download page. Then upload the jar or paste the direct jar link under Bibliothek → Von URL laden ("Library → Load from URL"; https only, internal addresses blocked, max. 200 MB). PlotSquared requires WorldEdit or FAWE.
Signs, NPCs, navigator
Lobbies receive the service list from the node via push – without the cloud, this stays off.
- Signs:
/cloudsign set <Task>while looking at a sign (permissioneternal.cloud.signs). A task's services are distributed across its signs; right-click connects. States SEARCHING, STARTING, EMPTY, ONLINE, FULL, INGAME, MAINTENANCE with their own frames; layout in Dashboard → Cloud → Schilder & NPCs ("Signs & NPCs"). - NPCs:
/cloudnpc create <Task> [Entity|PLAYER[:Skin]] [OPEN_MENU|CONNECT_BEST|CONNECT_LOWEST|CONNECT_RANDOM](permissioneternal.cloud.npcs). The NPC is frozen, has a hologram (%task% %online% %services%), and a right-click opens the service menu or connects.- Player NPC with a skin (default from Minecraft 1.21.9/Paper, a mannequin; on older servers a villager stands in until the update). Without anything else the NPC gets the preloaded skin of its game mode, matched by the task name: lobby, CityBuild, survival, farm world, SkyBlock, BedWars, SkyWars, SkyPvP, TTT, PvP, UHC, build, minigames, event, zombie.
PLAYER:<preset>picks a preset (e.g.PLAYER:bedwars),PLAYER:<player name>any player's skin (e.g.PLAYER:Notch). The server loads the skins itself, so they show in offline mode behind the proxy too. - In the dashboard (Cloud → Signs & NPCs → edit NPC → Aussehen): player or mob, then the skin — matching the task, one of the presets or a player name, with a head preview.
- Player NPC with a skin (default from Minecraft 1.21.9/Paper, a mannequin; on older servers a villager stands in until the update). Without anything else the NPC gets the preloaded skin of its game mode, matched by the task name: lobby, CityBuild, survival, farm world, SkyBlock, BedWars, SkyWars, SkyPvP, TTT, PvP, UHC, build, minigames, event, zombie.
- Navigator (lobby module):
server: "task:BedWars"instead of a fixed server name connects to the best service of the task.
Everything is stored network-wide in the database, per lobby server name.
Proxy display
Netzwerk › Proxy-Anzeige (SyncProxy replacement), stored in the database; every proxy re-reads it every 5 s – with or without a cloud node:
- MOTD frames with animation and separate maintenance MOTDs.
- Tab header/footer with placeholders
%online% %max% %proxy% %player% %server% %task% %ping% %version% %time%. - Slots: fixed value, listener limit or auto slot; full → kick text,
eternal.cloud.joinfullmay join anyway. - Maintenance with whitelist (names/UUIDs);
eternal.cloud.maintenancegets through.
Modules and permissions per task
With the cloud two settings apply per task instead of per server – new services of a task start right away:
- Einrichtung › Module (
eternal.web.modules): modules for every service of a task. A single server with its own setup (list "Einzelne Server") wins over its task; without the cloud the page stays in the dashboard under Server › Module (per server). - Ränge & Rechte › Rechte je Task (
eternal.web.perms): permissions a rank or a player has only on the services of one task (e.g. WorldEdit only on the build server).
What lives where
While the cloud runs (and the account may use it), everything that belongs to the network sits in the Cloud area: logs, live view, map, worlds, lobby map, world rules, proxy display and modules. Ranks and permissions have their own area Ränge & Rechte in the bar on top (/perms): roles & permissions, users & ranks, permissions per task, permission requests, requesting permissions and the permission catalog; old addresses (/admin/users …) lead there. The dashboard keeps only what has nothing to do with the cloud. Without the cloud there is no Cloud area – the same pages (logs, worlds, proxy display …) are in the dashboard. The addresses (/cloud/logs …) are the same either way.
Logs, farm worlds, world rules
These pages work without a cloud node too (then in the dashboard):
- Betrieb › Live-Ansicht (
eternal.web.live): watch servers live in 3D, see Replays. - Betrieb › Logs (
eternal.web.logs): API, cloud node, live consoles and log files of every server. Per server the newest 30 rotated logs are kept (the node deletes older ones hourly, the archive of stopped dynamic services too); the overview lists the newest 7, "load older logs" opens the full list. - Welten › Farmwelten (
eternal.web.farm), see Farm worlds. - Welten › Lobby-Karte (
eternal.web.lobbymap), see Lobby. - Welten › Welt-Regeln (
eternal.web.worldrules), see Settings.
Without eternal.web.cloud these pages are in the dashboard.
Player executor
Dashboard → Services → player name: connect, kick, send a message, run a command. The node passes this on to all registered proxies.
Managing in game (/cloud)
On every cloud service, /cloud opens a menu with the node status, all tasks and services. In it:
- Task: start a service, min/max count (left +1, right −1), RAM (±256 MB, shift ±1024), max players, maintenance, fallback, offline, version, delete (shift+click, then confirm).
- Service: join, restart, stop (shift = kill), console (type a command in chat), last log lines, set offline, list of players.
Everything also works as a command: /cloud tasks | services | status, /cloud start <task> [n], /cloud stop|restart|kill|join <service>, /cloud cmd <service> <command>, /cloud log <service> [lines], /cloud task <task> <min|max|memory|players|version|java|port|maintenance|fallback|static|offline> <value>, /cloud create <name> <server|proxy|velocity|limbo> <version> [RAM], /cloud delete <task> confirm, /cloud groups | templates | versions [software].
In game the same permissions as in the dashboard apply: /cloud itself needs eternal.cloud.admin, every action additionally eternal.web.cloud (view), .manage (change) or .console (console, log). The server calls the API with its service token on behalf of the player; the node only accepts this while exactly that player (name and UUID) is online on exactly that service, and only for /cloud/….
The push channel (REST & WebSocket)
Everything under /cloud/* (dashboard token) or /cloud/internal/* (plugin secret). Live console via ws://…/cloud/ws/console/<Service>?token=…, service push via /cloud/ws/services. Behind Apache/nginx, these WebSockets must be passed through – the snippets for this are in the Installation.
Security
The API runs as root (JDK installation, low ports) and starts third-party server jars. The essentials:
- Run-as-user: game servers run via
runuserascloud.run-as-user, not as root. Without a user set, the node refuses to start them (unlessallow-root-services: true). Be sure to set it during setup – see Installation. - Permissions are effectively shell access:
eternal.web.cloud.manageand.consoleallow JVM flags, plugins and the console as the service user – give them to owners only (see Permissions). - Network: only the proxy port should be exposed. Backends bind to
127.0.0.1withonline-mode=falseand must never be reachable from the internet. - Signed configuration: task/group rows in the DB are HMAC-signed; someone with DB write access cannot inject JVM flags or inclusions.
- One token per service instead of the network key; downloads only via HTTPS with a checksum;
api.ymlis written with mode600.
Permissions
eternal.web.cloud (view), eternal.web.cloud.manage (change, start, stop, install), eternal.web.cloud.console (console + commands), eternal.web.cloud.java (JDKs), eternal.cloud.maintenance (enter during maintenance). Complete list under Permissions.
Limitations
- Workers need the same MySQL as the head. With H2 the cloud stays on one node.
- The Eternal plugin runs on Spigot/Paper 1.8.8 to 26.x and needs Java 17 or newer. The node therefore starts 1.8–1.16 with Java 17 automatically – unless the task sets a fixed older Java version, then the plugin does not load (see Versions).
Next
- Installation & Setup – set up from scratch.
- Auto-updates – keep the plugin jar in the templates up to date.