universe docs source
browse docs
docs /configuration /instance-config

Instance configuration

A configuration file under ./configuration/ defines one deployable service: the command to run, the runtime that runs it, the ports it claims, how many copies Universe keeps alive, and the templates copied into its working directory.

Instance configurations live in ./configuration/ as JSON files, one per service. The Master loads them into the Hazelcast configurations map at startup, and the filename (minus .json) must equal the name field inside. A configuration is a template for instances, not an instance itself: you create running instances from it by name through the REST API or console, and Universe decides which Wrapper hosts each one.

Anatomy of a configuration

A complete example for a Minecraft lobby that Universe keeps two copies of, spread across two Wrappers:

{
	"name": "lobby",
	"runtime": "screen",
	"command": "java -Xmx2G -jar server.jar nogui",
	"static": false,
	"ramMB": 2048,
	"cpu": 100,
	"nodes": ["wrapper-1", "wrapper-2"],
	"hostAddress": "%NODE_ADDRESS%",
	"availablePorts": { "min": 25565, "max": 25600 },
	"additionalPorts": [
		{ "name": "query", "port": 25565, "protocol": "UDP" }
	],
	"minimumServiceCount": 2,
	"instanceGroups": ["lobby"],
	"environmentVariables": {
		"INSTANCE_PORT": "%PORT%",
		"PROXY_SECRET": "shared-velocity-secret"
	},
	"templateInstallationConfig": {
		"allOf": [
			{ "name": "server", "group": "global", "storage": "local", "priority": 0 },
			{ "name": "default", "group": "lobby", "storage": "local", "priority": 10 }
		],
		"allInGroups": [],
		"oneOf": [],
		"oneInGroups": [],
		"onTemplatePasteOverridePresentFiles": false
	},
	"fileModifications": ["server.properties", "config/proxy.yml"],
	"properties": {
		"SERVER_MOTD": "A Universe-managed lobby",
		"MAX_PLAYERS": "100"
	}
}
FieldTypeDefaultDescription
namestringrequiredUnique configuration identifier. Must match the filename.
runtimestringscreenRuntime provider key. Built in: screen, tmux, process. Extensions add docker and k8s.
commandstringrequiredShell command run inside the instance working directory. Not scanned for template variables.
staticbooleanfalsetrue keeps the working directory between restarts instead of cleaning it up on stop.
ramMBint2048Memory reservation per instance, in megabytes. Counts against the node’s maxRamMB.
cpuint100CPU reservation per instance, where 100 equals one physical core.
nodesarrayrequiredList of nodeId values eligible to host this configuration.
hostAddressstring127.0.0.1Address advertised to clients connecting to instances. Supports template variables such as %NODE_ADDRESS%.
availablePortsobjectrequiredInclusive min/max range the PortAllocator scans for the primary instance port.
additionalPortsarray[]Extra fixed ports with name, port, and protocol (TCP/UDP).
minimumServiceCountint1Target running count the InstanceCountEnforcer maintains.
instanceGroupsarray[]Logical group names used for proxy routing and manifest export.
environmentVariablesobject{}Environment variables passed to the process. Values support built-in template variables.
templateInstallationConfigobjectrequiredControls which templates are copied and in what order. See Templates.
fileModificationsarray[]File paths scanned for %VARIABLE% replacement after the template copy.
propertiesobject{}Custom key/value pairs exposed as %KEY% template variables.
!
warning

The command string runs verbatim and is not scanned for %VARIABLE% placeholders. To pass the allocated port into a process, surface it through environmentVariables (for example "INSTANCE_PORT": "%PORT%") or write it into a config file listed in fileModifications.

Scaling with minimumServiceCount

On the Master, InstanceCountEnforcer compares the live instance count for each configuration against its minimumServiceCount. When the count falls short, whether from a fresh start, a deliberate stop, or a Wrapper going OFFLINE, it dispatches new deploy tasks to bring the service back to target. Set it to 0 for a configuration you only ever start manually, or to a higher number for a pool that should always be warm.

Each new instance is placed on an eligible node from nodes that has live Hazelcast membership and enough headroom under its maxRamMB and maxCpu caps to fit the configuration’s ramMB and cpu reservation.

i
note

minimumServiceCount is a floor, not a fixed size. You can create instances above the minimum by hand; the enforcer only ever adds instances to reach the floor, it does not stop extras.

Runtime selection

The runtime key resolves to a RuntimeProvider in the Wrapper’s RuntimeRegistry. The three built-in providers ship in the core JAR; the rest are contributed by extensions and must be installed on the target Wrapper before an instance using them deploys.

KeySourceMechanism
screenbuilt inscreen -dmS session, stdin via screen -X stuff.
tmuxbuilt intmux new-session -d, stdin via tmux send-keys.
processbuilt inDirect ProcessBuilder, no session manager.
dockerextensionPer-instance container via runtime-docker.
k8sextensionPod per instance via runtime-k8s.

Ports

Every instance gets one primary port, chosen from availablePorts by the PortAllocator and exposed to templates as %PORT%. Services that need fixed side channels, an RCON port or a UDP query port, declare them in additionalPorts with an explicit number and protocol.

"availablePorts": { "min": 25565, "max": 25600 },
"additionalPorts": [
	{ "name": "rcon", "port": 25575, "protocol": "TCP" },
	{ "name": "query", "port": 25565, "protocol": "UDP" }
]
tip

The allocator avoids ports already claimed by other instances cluster-wide and probes the host OS before committing, so overlapping availablePorts ranges across configurations on the same node are safe. See the port allocation strategy in the architecture reference.

Reloading at runtime

Configurations are editable without restarting the Master. Run config reload in the console, or call POST /api/node/reload, to re-read ./configuration/ into the Hazelcast map. The CRUD endpoints under /api/configurations let tooling create, update, and delete configurations over HTTP, writing the JSON files back to disk.