Global Settings
The infrarust.toml file controls the proxy process itself: what address it listens on, how many threads it uses, and how it handles connections before they reach any backend server.
Every field has a default value. An empty file (or no file at all) starts the proxy on 0.0.0.0:25565 with sane defaults.
The config struct uses serde(deny_unknown_fields), so unrecognized keys are a hard parse error rather than a silent no-op.
Bind address and port
bind = "0.0.0.0:25565"The socket address the proxy listens on. The format is ip:port. Set the IP to 127.0.0.1 to accept connections only from localhost, or 0.0.0.0 to accept from any interface.
To run on a non-standard port:
bind = "0.0.0.0:25577"Worker threads
worker_threads = 0Number of Tokio async runtime threads. 0 (the default) lets the runtime pick a count based on available CPU cores. Set this explicitly if you want to cap CPU usage on a shared host.
Connection limits
max_connections = 0Maximum simultaneous client connections. 0 means unlimited. When the limit is reached, new connections are rejected until existing ones close.
Timeouts
connect_timeout = "5s"How long the proxy waits when opening a TCP connection to a backend server. If the backend doesn't respond within this window, the connection attempt fails and the player sees an error.
All duration fields accept human-readable strings: "5s", "30s", "1m", "2m30s".
Server and plugin directories
servers_dir = "./servers"
plugins_dir = "./plugins"2
servers_dir is the path to the directory containing per-server .toml files. plugins_dir is where Infrarust looks for WASM plugin files. Both are resolved from the working directory where Infrarust starts. See the Configuration Overview for the per-server config format.
Announce proxy commands
announce_proxy_commands = trueWhen true (the default), the proxy announces its built-in /ir command tree to clients via the Minecraft command graph packet. Set to false to hide the proxy command suggestions from the player's tab completion.
Proxy protocol
receive_proxy_protocol = falseWhen true, the proxy expects incoming connections to start with a HAProxy PROXY protocol header (v1 or v2). Enable this if Infrarust sits behind a load balancer that sends proxy protocol, such as HAProxy or AWS NLB.
WARNING
Only enable this if your upstream actually sends proxy protocol headers. Regular Minecraft clients do not, and connections will fail if this is on without a proxy protocol source.
SO_REUSEPORT
so_reuseport = falseEnables the SO_REUSEPORT socket option, which allows multiple processes to bind to the same port. This is a Linux-only option and has no effect on other platforms. Useful when running multiple Infrarust instances behind a kernel-level load balancer.
Unknown domain behavior
unknown_domain_behavior = "default_motd"What happens when a player connects with a domain that doesn't match any server definition.
| Value | Behavior |
|---|---|
default_motd | Respond with the MOTD defined in [default_motd] (default) |
drop | Close the connection silently |
Rate limiting
[rate_limit]
enabled = false
max_connections = 3
window = "10s"
status_max = 300
status_window = "10s"2
3
4
5
6
Controls how many connections a single IP can make within a sliding time window. Rate limiting is disabled by default; set enabled = true to activate it. Login attempts and status pings have separate limits.
max_connections is the number of login attempts allowed per IP within window. status_max and status_window do the same for server-list ping requests. The defaults allow 3 login attempts and 300 status pings per 10-second window per IP.
Status cache
[status_cache]
ttl = "5s"
max_entries = 10002
3
The proxy caches server-list ping responses to avoid hammering backend servers. ttl is how long a cached response stays valid. max_entries caps the cache size.
If you run many backend servers and see stale ping data, lower the ttl. If memory is a concern, lower max_entries.
TCP keepalive
[keepalive]
time = "30s"
interval = "10s"
retries = 32
3
4
TCP keepalive probes detect dead connections at the OS level. After a connection sits idle for time, the OS sends a probe every interval. After retries failed probes, the connection is closed.
These values apply to both player-to-proxy and proxy-to-backend connections.
Ban system
[ban]
file = "bans.json"
purge_interval = "300s"
enable_audit_log = true2
3
4
file is the path to the JSON file where bans are stored. purge_interval controls how often expired bans are removed from the file. When enable_audit_log is true, every ban and unban operation is logged.
Default MOTD
[default_motd.online]
text = "§cUnknown server"
version_name = "Infrarust"
max_players = 02
3
4
The MOTD shown when a player pings a domain that doesn't match any server. You can set different MOTDs for different states: online, offline, sleeping, starting, crashed, stopping, unreachable.
Each MOTD entry supports these fields:
| Field | Type | Description |
|---|---|---|
text | string | MOTD text, supports Minecraft § formatting codes |
favicon | string | Path to a 64x64 PNG, a base64 string, or a URL |
version_name | string | Version text shown in the client |
max_players | integer | Max player count displayed in the server list |
Telemetry
[telemetry]
enabled = true
endpoint = "http://localhost:4317"
protocol = "grpc"
[telemetry.metrics]
enabled = true
export_interval = "15s"
[telemetry.traces]
enabled = true
sampling_ratio = 0.1
[telemetry.resource]
service_name = "infrarust"2
3
4
5
6
7
8
9
10
11
12
13
14
15
Infrarust can export metrics and traces via OpenTelemetry. The [telemetry] section is absent by default (no telemetry); add it and set enabled = true to activate export. Point endpoint at your OTLP collector; when omitted, the OpenTelemetry SDK default is used.
protocol is either "grpc" or "http", matching the OTLP export protocol your collector expects.
sampling_ratio controls what fraction of status ping traces are sampled (0.0 to 1.0). Login traces are always sampled at 100% regardless of this value.
service_name is set as an OTEL resource attribute. service_version defaults to the Infrarust binary version and is usually left unset.
TIP
Omitting the [telemetry] section entirely disables telemetry. No collector connection is attempted.
Docker provider
[docker]
endpoint = "unix:///var/run/docker.sock"
poll_interval = "30s"
reconnect_delay = "5s"2
3
4
Enables automatic server discovery from Docker container labels. endpoint is the Docker daemon socket or HTTP API URL. network (optional) specifies which Docker network to use when resolving container addresses.
The provider uses Docker events for real-time updates and falls back to polling every poll_interval if the event stream disconnects. After a disconnect, it waits reconnect_delay before reconnecting.
INFO
The [docker] section is optional. Omit it entirely to disable Docker discovery.
IP filter
[ip_filter]
whitelist = ["10.0.0.0/8", "192.168.0.0/16"]
blacklist = ["10.6.6.0/24"]2
3
Global IP filtering using CIDR ranges. An IP is allowed when it is not in the blacklist and (the whitelist is empty or the IP is in the whitelist). The blacklist always wins: a blacklisted address inside a whitelisted range is still rejected.
If whitelist is empty, all IPs are allowed except those in blacklist. Both lists can be used together.
Individual servers can define their own [ip_filter] in addition to, or instead of, the global one.
Forwarding
[forwarding]
mode = "none"
secret_file = "forwarding.secret"
bungeecord_channel = true2
3
4
Player IP forwarding passes the real client IP and UUID to backend servers. The mode values are:
| Mode | Description |
|---|---|
none | No forwarding (default) |
bungeecord / legacy | BungeeCord-style legacy forwarding in the handshake |
bungeeguard | BungeeCord forwarding with a shared HMAC token |
velocity / modern | Velocity modern forwarding (recommended if your backends support it) |
secret_file is the path to the shared secret used by bungeeguard and velocity. The file is created automatically if it does not exist.
bungeecord_channel enables the BungeeCord plugin messaging channel. The [forwarding.channel_permissions] subtable controls which sub-channels are allowed; most are enabled by default, and connect_other, message, message_raw, kick_player, and kick_player_raw are disabled by default.
WARNING
BungeeCord legacy forwarding sends the real IP in plain text in the handshake. Anyone who can reach your backend port can spoof it. Use bungeeguard or velocity if you need IP forwarding and cannot fully firewall the backend.
Web admin API
[web]
enable_api = true
enable_webui = true
bind = "127.0.0.1:8080"
api_key = "your-api-key-here"
[web.rate_limit]
requests_per_minute = 602
3
4
5
6
7
8
Enables the HTTP admin API (and optional web UI) used by management tools and the CLI. The section is optional; omit it entirely to keep the web interface off.
bind defaults to 127.0.0.1:8080. If you bind to a non-loopback address, api_key is required and must be at least 16 characters. When bound to loopback without a key, Infrarust generates an ephemeral key and logs it at startup.
cors_origins accepts a list of allowed CORS origins (empty by default, meaning no cross-origin access).
DANGER
Never expose the admin API on a public interface without a strong api_key. There is no second authentication layer.
Permissions
[permissions]
admins = ["PlayerName", "AnotherPlayer"]
player_commands = []2
3
admins is a list of player names (or UUIDs) granted the Admin permission level. Admin players can run all /ir subcommands including broadcast, kick, reload, send, plugin, and plugins.
player_commands overrides which /ir subcommands non-admin players can run. By default, players can use help, version, list, find, and server.
Plugins can register custom permission checkers that extend or replace this list.
Plugins
[plugins.my_plugin]
path = "./plugins/my_plugin.wasm"
permissions = ["event_handler"]
enabled = true2
3
4
Plugin configurations are keyed by plugin ID. Each entry can specify a path to the plugin binary, a list of permissions, and whether the plugin is enabled (defaults to true when omitted). WASM plugins are loaded from plugins_dir by default; path overrides the location for that specific plugin.
Full example
A complete infrarust.toml showing all sections and their defaults:
bind = "0.0.0.0:25565"
max_connections = 0
connect_timeout = "5s"
receive_proxy_protocol = false
servers_dir = "./servers"
plugins_dir = "./plugins"
worker_threads = 0
unknown_domain_behavior = "default_motd"
so_reuseport = false
announce_proxy_commands = true
[rate_limit]
enabled = false
max_connections = 3
window = "10s"
status_max = 300
status_window = "10s"
[status_cache]
ttl = "5s"
max_entries = 1000
[keepalive]
time = "30s"
interval = "10s"
retries = 3
[ban]
file = "bans.json"
purge_interval = "300s"
enable_audit_log = true
# [telemetry]
# enabled = true
# endpoint = "http://localhost:4317"
# protocol = "grpc"
#
# [telemetry.metrics]
# enabled = true
# export_interval = "15s"
#
# [telemetry.traces]
# enabled = true
# sampling_ratio = 0.1
#
# [telemetry.resource]
# service_name = "infrarust"
# [ip_filter]
# whitelist = []
# blacklist = []
# [forwarding]
# mode = "none"
# secret_file = "forwarding.secret"
# [web]
# bind = "127.0.0.1:8080"
# api_key = "change-me-min-16-chars"
# [permissions]
# admins = []2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62