Custom Profiles
A custom profile is a YAML file that describes a run of your own. A benchmark profile lists which tests to run, in which order and with which parameters. A stress limit profile chooses which workloads to push towards the breaking point, and how.
Profiles live in plugins/VoxelBench/custom_benchmarks/ and run with /bench custom run <name>.
This page is the full reference. Benchmarks gives a short overview. Custom profiles are not the same thing as benchmark-mode: custom in config.yml, which changes what /bench start runs (see Benchmarks).
Quick Start
/bench custom list
/bench custom info standard
/bench custom run standard
/bench custom listshows the profiles that loaded, tagged[STD](benchmark) or[STRESS](stress limit)./bench custom info standardshows a profile's details and its steps./bench custom run standardruns it. A player must type it in-game, and the server must be linked to a voxelbench.com account, even if the profile sends nothing.- To write your own, copy
standard.ymltomy-profile.ymlin the same folder and edit the copy: give it its ownname:, then change the steps. Run/bench custom reload, then/bench custom run my-profile. No restart is needed.
Bundled Profiles
VoxelBench ships nine profiles:
| File | Kind | Content |
|---|---|---|
example.yml | benchmark | Quick smoke test in 7 steps: disk, memory, network, chunkLoading, hopper, singleCoreMax, multiCore |
standard.yml | benchmark | The 16 tests of /bench start with the same loads, as a starting point to copy. Its runs are not scored and are not comparable with /bench start |
showcase.yml | benchmark | All 26 built-in tests, each parameter written out with a comment. It loads like any profile, but it is a reference to copy blocks from, not a benchmark to run |
free-host.yml | benchmark | The 16 standard tests with much lighter loads, for free hosting tiers. It reminds you that many free hosts forbid benchmarks in their terms |
low-memory.yml | benchmark | The 16 standard tests scaled down for servers with a 2-4 GB heap |
hopper-heavy.yml | benchmark | The hopper test alone, at 100 lines per zone: ten times the standard load. Heavy: run it on a test server or off-peak |
stress-redstone.yml | stresslimit | Redstone only, from 400 up to a soft ceiling of 8,000, with a fine ramp and a precise search at the breaking point |
stress-monster.yml | stresslimit | Six stress types (every type except villagers) with their nominal bounds, an aggressive ramp and 15 minutes per type |
stress-freehost.yml | stresslimit | Mobs, redstone and hoppers with low soft ceilings (4,000, 2,000 and 2,000), stricter break thresholds (TPS 19, MSPT 45 ms), 8-second tiers and 2 zones |
Updates and Deleted Profiles
- VoxelBench copies each bundled profile once: all of them when it creates the folder, and a profile added by a later version (such as
hopper-heavy.yml) at the first start after the update. - The folder's
.bundled-profilesfile lists the profiles already copied, so a bundled profile you delete stays deleted. To get one back, remove its name from.bundled-profilesand run/bench custom reload. - Each time the folder is read (at startup and on reload), a copy that is exactly an earlier version of a bundled profile, line endings aside, is replaced by the current version, and the console says so. A copy you changed, even by one character, is never touched. To keep your changes and still receive updates of the original, work on a copy under another name.
Profile Files
- A profile is one
.ymlor.yamlfile inplugins/VoxelBench/custom_benchmarks/. - Its name is the file name without the extension, in lower case:
My-Profile.ymlis run with/bench custom run my-profile. Two files whose names only differ by case or extension share the same name, and only one of them is loaded. - One folder holds both kinds. The top-level
kind:key chooses:benchmark(the default) orstresslimit. Withoutkind:, a file that has astress:section and notests:section is a stress limit profile; any other file is a benchmark profile. Anykind:value other thanstresslimit(in any case) is read asbenchmark.
Common Keys
These keys are optional and work the same way in both kinds:
| Key | Default | Meaning |
|---|---|---|
kind | See above | benchmark or stresslimit |
name | The file name | Display name, shown by list and info and on voxelbench.com |
description | Empty | Shown under the profile in list, and by info |
author | Empty | Shown by info and on voxelbench.com |
version | 1 | A whole number, your own revision number |
tags | None | A list of words, for example [redstone, farms] |
submit | false | true sends the report to voxelbench.com at the end of the run (see On voxelbench.com) |
Other top-level keys are ignored.
Benchmark Profiles
kind: benchmark # optional: benchmark is the default
name: "Redstone and Chunks"
description: "Redstone clocks, then chunk generation"
author: "YourName"
version: 1
tags: [redstone, chunks]
submit: false # true = send the report to voxelbench.com
options:
auto-temp-world: true # false = run in the main world (see Test World)
tests:
- id: disk
params:
threads: 4
fileSizeMb: 1024
- id: redstone
params:
pistonCount: 3200
durationSeconds: 60
- id: chunkLoading
params:
chunksToLoad: 1000
dispersedZones: 8 # more than 1 = the run's 8 zones
- id: worldSave # no parameters
- id: singleCoreBenchmark
params:
durationSeconds: 30
options: has a single setting, auto-temp-world (see Test World). Other keys under options: do nothing, but they count in the profile hash.
Steps
tests:is the list of steps. Each entry is a map with anid:and, optionally,params:.- The steps run in the order written, one after the other. The bundled profiles put hardware tests first, gameplay tests next and CPU tests last, which gives the most reliable numbers.
id:is a test ID: one of the 26 built-in tests, or a test added by an extension. Case,-and_do not matter:chunkLoading,chunk-loadingandCHUNK_LOADINGare the same test./bench test listlists every test the server knows, extension tests included.- The short names that
/bench testalso accepts (lighting,collision,tileentity,villager) are not test IDs: in a profile, writelightingUpdate,entityCollision,tickingTileEntityandvillagerTrading.
Parameters
In a profile, every parameter is written by name under params:, unlike /bench test, where many tests take plain values in a fixed order (see Commands). A parameter you leave out takes its default value. A number outside the allowed range is brought back to the nearest bound when the run starts, with a warning.
| Test | Parameters: default (allowed range) |
|---|---|
disk | threads 4 (1–32), queueDepth 8 (1–64), fileSizeMb 512 (16–8192), randomOps 200 (10–5000), passes 3 (1–20) |
network | None |
memory | tableSize 512 (16–4096), iterations 75000 (1000–1000000), passes 3 (1–20) |
multiCore | threads 100 (1–1000), iterations 100000 (1000–10000000), kernel int |
singleCoreBenchmark | durationSeconds 10 (3–60), kernel int |
singleCoreMax | operations 50000000 (1000000–1000000000), passes 3 (1–20), kernel int |
chunkLoading | chunksToLoad 200 (10–5000)*, dispersedZones 1 (1–10) |
mobSpawn | mobCount 300 (10–5000)*, dispersedZones 1 (1–10) |
hopper | parallelLines 50 (1–500), dispersedZones 1 (1–10) |
explosion | tntCount 100 (1–1000)*, dispersedZones 1 (1–10) |
lightingUpdate | totalUpdates 400 (10–10000), dispersedZones 1 (1–10) |
worldSave | None |
redstone | pistonCount 1000 (10–5000), durationSeconds 15 (5–120) |
blockPhysics | fallingBlockCount 12000 (100–50000), spawnFrequencyTicks 1 (1–20) |
chunkTicking | chunks 200 (10–1000), tickSpeed 3 (1–256), durationSeconds 60 (10–300) |
entityCollision | items 500 (10–5000), entities 200 (10–2000), durationSeconds 30 (10–120) |
tickingTileEntity | total 5000 (100–20000), type ALL, dispersedZones 1 (1–10), durationSeconds 15 (5–120) |
mobAI | villagerCount 100 (50–3000), houseCount 20 (5–500), hostileCount 200 (10–2000), phase1Seconds 10 (5–60), phase2Seconds 15 (5–120) |
mobPathfinding | totalMobs 200 (10–2000), durationSeconds 30 (10–120) |
villagerTrading | villagers 100 (20–2000), houses 20 (5–200), durationSeconds 60 (30–300), zombies 50 (0–500) |
boneMealGrowth | saplingCount 200 (10–2000), cropCount 500 (10–5000), durationSeconds 20 (5–120) |
liquidPhysics | waterSourceCount 100 (1–1000), lavaSourceCount 50 (0–1000), durationSeconds 20 (5–120) |
combatSimulation | zombieCount 60 (0–500), skeletonCount 80 (0–500), pillagerCount 40 (0–500), durationSeconds 30 (10–120) |
projectileStorm | projectilesPerWave 100 (10–2000), durationSeconds 20 (5–120) |
entityCramming | totalEntities 500 (10–5000), durationSeconds 20 (5–120) |
playerWorldLoad | playerCount 10 (1–100) |
* Also capped by config.yml, without a warning: see Settings Read from config.yml.
kernel(CPU tests) isint,float,memoryorbranch. Any other value runsint, without a warning.type(tickingTileEntity) isFURNACE,HOPPER,SPAWNERorALL. Any other value runsALL, with a warning.- Write numbers as plain integers (
50000000). A value that is not a number gives the default, with a warning. - For
chunkLoading,mobSpawn,hopperandexplosion,dispersedZonesonly chooses between one zone and eight. Every profile run lays out 8 test zones far apart, as/bench startdoes. Such a test givendispersedZones: 1, or nodispersedZonesat all, runs in a single zone; given any value from 2 to 10, it runs in the run's 8 zones. Write8to make that explicit.lightingUpdateandtickingTileEntityuse the number of zones you write. chunkLoadingstays under a memory ceiling: 30 % of the maximum heap, counted at about 50 KB per chunk, shared by the zones the test actually walks (the run's 8 zones for anydispersedZonesabove 1, a single zone otherwise), and never cut below 200 chunks per zone. With 8 zones, each zone gets at most 0.75 chunk per MB of maximum heap:chunksToLoad: 1000is cut on a heap under about 1,330 MB. Before VoxelBench 2.0.3, the ceiling was divided by thedispersedZonesvalue written in the profile instead, so a profile withdispersedZones: 2could load up to four times the ceiling on a small heap: its earlier runs are not comparable with new ones there.- Tests added by extensions declare their own parameters: see the extension's documentation.
The bundled showcase.yml repeats every parameter with a short comment, and standard.yml holds the values of /bench start.
Test World
A benchmark profile picks its world like this:
- A world pinned with
/bench world setis always used, if it is loaded. If it is not loaded, you get a warning and the next rules apply. - Otherwise, when
options.auto-temp-worldistrue(its default is the value ofbenchmark.auto-temp-worldinconfig.yml), the run creates a temporary flat world,voxelbench_temp_<timestamp>, and deletes it at the end. This only happens whenbenchmark.auto-temp-worldis alsotrueinconfig.yml: a profile can turn the temporary world off, not on. If the world cannot be created, the run falls back to the main world and the console says so. - Otherwise the tests run in the server's main world (the first world it loads): they build and clear their test zones there.
playerWorldLoad only runs in the pinned world or in a voxelbench_* world. Anywhere else, the step is skipped with the reason benchmark_world_required and the rest of the run goes on. See also Configuration and Benchmarks.
Settings Read from config.yml
A profile does not hold every setting of its tests. A few are read from the server's config.yml at each run (see Configuration):
benchmark-tests.chunk-loading.limits,benchmark-tests.mob-spawn.limitsandbenchmark-tests.explosion.limitscapchunksToLoad,mobCountandtntCount, without a warning. With the shippedconfig.yml, that means at most 5,000 chunks, 1,000 mobs and 200 TNT, even if the table above allows more.benchmark-tests.chunk-loading.adaptive(chunk generation throttle),benchmark-tests.hopper.hopper-chain-length,benchmark-tests.mob-spawn.duration-secondsandbenchmark-tests.explosion.raise-host-tnt-quotaalso apply.benchmark-tests.hopper.limitsdoes not apply: it only bounds the/bench test hoppercommand. A profile runs the lines it asks for, up to 500 per zone.
Two servers running the same profile therefore run the same load only if these settings match.
Stress Limit Profiles
A stress limit profile runs the stress limit mode with your own recipe: which stress types to push, from which load, up to which ceiling, and when a tier counts as broken.
kind: stresslimit
name: "Farms Check"
description: "Hoppers and mobs, stricter break threshold"
author: "YourName"
version: 1
tags: [farms]
submit: false
stress:
types:
- id: hoppers
base: 100 # starting load
max: 4000 # soft ceiling
- id: mobs # no base or max: the type's nominal bounds
thresholds:
tpsBreak: 19.0
msptBreak: 50.0
timing:
palierDurationSec: 12
zones: 2
stress.types is the only required key. Each entry is a map with an id:, one of mobs, chunks, hoppers, tnt, redstone, entities or villagers (see Stress Types). The types run in the order written; a type listed twice runs once.
baseis the starting load andmaxthe soft ceiling. Leave one out and it keeps the type's nominal value. Abaseabovemaxis lowered tomax.- The soft ceiling still grows while the server has headroom, up to the type's hard cap of five times its nominal ceiling. A
maxabove that cap is lowered to it: no profile can raise it.
Every other key is optional and keeps the value used by /bench stresslimit:
| Key | Default | Effect |
|---|---|---|
stress.thresholds.tpsBreak | 18.0 | A tier breaks when the average TPS falls below this value |
stress.thresholds.msptBreak | 55.0 | A tier breaks when the average MSPT rises above this value, in milliseconds |
stress.ramp.stepMin | 1.15 | Load multiplier near the breaking point |
stress.ramp.stepMax | 2.5 | Load multiplier when the server is idle |
stress.ramp.curve | 2.0 | Above 1, big steps last longer while the server has headroom |
stress.ramp.plateauBoost | true | After three nearly idle tiers in a row, each further nearly idle tier multiplies the step by 1.5, cumulatively; false turns this off. Counted in the profile hash and recorded in its report. Before VoxelBench 2.0.3 the setting had no effect |
stress.refine.targetPrecision | 250 | The search at the breaking point stops when the gap is this many units |
stress.refine.maxIterations | 8 | Maximum attempts of that search |
stress.timing.palierDurationSec | 10 | Seconds per tier |
stress.timing.maxDurationMinutes | 10 | Time limit per type |
stress.timing.earlySkip | true | Lets a tier end early when the server is clearly comfortable |
stress.zones | 4 | Number of test zones |
The thresholds decide the result; the ramp, the search and the timing decide how fast and how precisely it is found. VoxelBench does not check the range of these numbers, so keep them close to the bundled profiles' values. How the tiers, the ramp and the search work is explained in Stress Limit.
A stress limit profile has no options:. It uses the pinned world, otherwise a temporary world when benchmark.auto-temp-world is true, otherwise the main world, like /bench stresslimit. It starts from the warm-start memory and updates it, as a full stress limit run does. Unlike /bench stresslimit, it starts without asking for confirmation.
Commands
| Command | Description |
|---|---|
/bench custom list (alias ls) | List the profiles that loaded: kind, name, display name, number of steps or types, and whether it submits |
/bench custom info <name> (alias show) | Show a profile: display name, description, author, version, tags, submit, the first 12 characters of its hash, then its steps and parameters as written, or its stress types and break thresholds |
/bench custom run <name> [force] [warmup] [<runs>] | Run a profile |
/bench custom reload | Read the folder again: new, changed and deleted files, and the bundled profiles (see above). Prints how many profiles loaded |
Every /bench custom command needs voxelbench.custom or voxelbench.start (operators by default); reload also needs voxelbench.reload, and running a stress limit profile also needs voxelbench.stresslimit, like /bench stresslimit. /bench reload does not re-read the profiles. Tab completion offers the loaded profile names after info, and after run only the profiles you can run: stress limit profiles only with voxelbench.stresslimit. The list of available profiles printed after an unknown name follows the same rule; list and info show every profile.
/bench custom run:
- must be typed by a player in-game, not from the console;
- needs a linked server, even when the profile has
submit: false; - is refused while another test or run is in progress, and
/bench stopstops it; - shares the cooldown of
/bench start(see Configuration).forceignores the cooldown if you havevoxelbench.start.force; - starts at once, without confirmation;
- runs a single iteration:
warmupand a run count are accepted, but a message tells you they are not applied to profiles.
A run is saved in your local reports (/bench reports), under Standard Benchmark for a benchmark profile and under Stress Limit for a stress limit profile, unless config.yml turns that report type off. See Reports.
See also Commands.
From the GUI
/bench gui, then the Tests tab, then Custom Profiles, lists the loaded profiles you can run: a nether star marks a benchmark profile, TNT a stress limit profile (shown only with voxelbench.stresslimit). The screen needs voxelbench.custom or voxelbench.start. Clicking a profile runs /bench custom run <name>, with the same checks. The refresh button runs /bench custom reload, which needs voxelbench.reload.
Validation
When Loading
VoxelBench reads the folder at startup, on /bench custom reload and with the GUI's refresh button. A file with a problem is left out of /bench custom list, and the console says why:
| Problem | Result |
|---|---|
| The file is not valid YAML | The server logs the parsing error, and the profile is skipped |
A benchmark profile has no tests: list, or an empty one | Skipped |
A step has no id: | The whole profile is rejected |
| A test ID is unknown | The whole profile is rejected; the message lists the valid IDs |
A stress limit profile has no stress.types list | Skipped |
A stress type has no id:, or an unknown one | The whole profile is rejected; the message lists the valid types |
Parameters are not checked at this point: /bench custom info shows them exactly as written.
Tests from extensions are only known once their plugin has started, which is after VoxelBench. A profile that uses one is therefore rejected at server startup: run /bench custom reload once the server is up.
A benchmark profile whose hopper step asks for more lines than benchmark-tests.hopper.limits.max also gets a console warning at load (the bundled hopper-heavy.yml excepted): before VoxelBench 2.0.0 such profiles silently ran at most that many lines (10 by default), so their earlier results are not comparable with new ones.
When a Run Starts
All steps are prepared before the first test starts:
- A number outside its allowed range is brought back within it, with a warning in the chat and the console, for example
mobCount=9000 clamped to 5000 (allowed range: 10..5000). The run goes on. - A value that is not a number is replaced by the default, with a warning.
- A parameter name the test does not know is ignored, without a warning: the test runs with its default value.
- If a test is no longer available (an extension was removed after the profile loaded), the run does not start, and a message names the test.
Sharing a Profile
A profile is a single, self-contained file. To share one, send the file: the other operator drops it into plugins/VoxelBench/custom_benchmarks/ and runs /bench custom reload. On their server, the file name becomes the profile name.
Profile Hash
When VoxelBench loads a profile, it computes a SHA-256 hash of its recipe. /bench custom info shows its first 12 characters, and a report sent to voxelbench.com carries the whole hash, so two runs with the same hash ran the same recipe.
For a benchmark profile, the hash covers:
- the kind,
name,description,author,version,tagsandsubmit; - every key under
options:; - the steps, in order: each test, with its parameters exactly as written.
It does not depend on the file name (except when there is no name:, since the file name is then the display name), comments, blank lines, the order of the keys under params: or options:, the order of the tags, or the way the test ID is spelled (chunk-loading and chunkLoading give the same hash). A parameter left out and the same parameter written with its default value give two different hashes, as do an out-of-range value and the bound it is brought back to.
For a stress limit profile, the hash covers the same metadata, the stress types in order with their bounds, and every stress setting as it applies to the run: a setting left out and the same setting written with its default value give the same hash.
Any other change, even to the description or to submit, makes a different hash, and therefore a different profile on voxelbench.com. The hash describes the recipe, not the server: results still depend on the machine and on the config.yml settings listed in Settings Read from config.yml.
On voxelbench.com
With submit: false, the default, nothing is sent: the run only stays in your local reports, where a benchmark profile's report is marked as not submitted.
With submit: true, a benchmark profile sends its report when the run ends, and the chat prints the link. On voxelbench.com, the report:
- belongs to the account your server is linked to, and is private at first. You can make it unlisted (anyone with the link can see it) or private again, but never public;
- gets no VoxelScore and no rank, and appears on no leaderboard;
- shows a Custom Profile card: display name, author, version, description, tags, the first 12 characters of the hash, and how many runs sent to voxelbench.com used the same hash.
The card leaves out a field that is too long: file name over 64 characters, display name or author over 255, description over 2,000, a tag over 32. Only the first 20 tags are kept. The run itself is still stored. For the reports of tests added by extensions, see Extending VoxelBench.
A stress limit profile with submit: true also sends its report when the run ends. voxelbench.com treats it like a benchmark profile's report: it belongs to the linked account, is private at first and can be made unlisted, never public, and it gets no score and no rank and appears on no leaderboard, since its break thresholds and ramp are the profile author's.
If your voxelbench.com plan includes auto-bench, an auto-bench job can run a benchmark profile of your server by its name. The name must then use only letters, digits, - and _, up to 64 characters. Such a run is always sent, whatever submit says. Stress limit profiles cannot be run this way.
Common Pitfalls
- A misspelled parameter name is ignored silently: the test runs with its default value. Compare the names printed by
/bench custom infowith the parameter table. - A step written
- diskinstead of- id: diskis ignored. Each step must be a map with anid:. kind: stress-limitis notkind: stresslimit: any other value makes a benchmark profile, which is then skipped because it has notests:.- A profile that uses an extension test disappears after a restart: run
/bench custom reloadonce the server is up. dispersedZones: 2or4does not mean 2 or 4 zones forchunkLoading,mobSpawn,hopperandexplosion: any value above 1 means the run's 8 zones.options.auto-temp-world: truecannot create a temporary world whenbenchmark.auto-temp-worldisfalseinconfig.yml: the run uses the main world.mobCountandtntCountstop at theconfig.ymllimits (1,000 mobs and 200 TNT by default), without a warning.- A new profile file does nothing until you run
/bench custom reloador restart the server. - A stress limit profile missing from the tab completion or the GUI is not broken: running one needs
voxelbench.stresslimit, and it is only offered to players who have it./bench custom listshows it.