Docker Deployment
Infrarust ships a minimal Docker image built from scratch. The image contains a statically linked binary, CA certificates, and nothing else. The published image supports x86_64 and aarch64; the Dockerfile also builds for armv7 when you build the image yourself.
The same image is published to two registries, with the same tags:
| Registry | Image |
|---|---|
| GitHub Container Registry | ghcr.io/shadowner/infrarust |
| Docker Hub | shadowner/infrarust |
Each release is tagged with its version (for example 2.0.0) and with latest. The examples below use the GitHub image; the Docker Hub one works the same.
Exposed ports
The image exposes two ports:
| Port | Purpose |
|---|---|
25565 | Minecraft proxy (players connect here) |
8080 | Web admin API and dashboard (optional, see below) |
Publish only the ports you need. For a basic proxy without the admin interface, -p 25565:25565 is enough.
Directory structure
Infrarust inside Docker expects a config volume mounted at /app/config. The layout:
config/
├── infrarust.toml
├── plugins/
└── servers/
└── survival.toml2
3
4
5
Set servers_dir and plugins_dir in your infrarust.toml to paths inside the container:
bind = "0.0.0.0:25565"
servers_dir = "/app/config/servers"
plugins_dir = "/app/config/plugins"2
3
WASM plugins are compiled once and cached in /app/cache/wasm, the default [wasm] cache_dir resolved from the image's working directory /app. The image creates that directory for the user it runs as (65532), readable and writable by that user only, so the cache needs no configuration. It lives in the container's writable layer: it survives a restart of the container, and a new container, after pulling a new image for example, compiles the plugins again at its first start. To keep the cache across containers, mount a named volume at /app/cache; Docker fills a new named volume with the directory from the image, owner included:
services:
infrarust:
volumes:
- ./config:/app/config
- wasm-cache:/app/cache
volumes:
wasm-cache:2
3
4
5
6
7
8
A bind mount at /app/cache needs a host directory that uid 65532 can write, ideally owned by it with mode 0700 to keep other users out; otherwise the proxy logs AOT cache directory cannot be written and compiles the plugins at each start. Do not place the cache under plugins_dir: the proxy refuses to start with cache_dir inside it.
Running with docker run
docker run -d \
--name infrarust \
-p 25565:25565 \
-v ./config:/app/config \
ghcr.io/shadowner/infrarust:latest \
--config /app/config/infrarust.toml2
3
4
5
6
The --config flag tells Infrarust where to find infrarust.toml inside the container. If you don't pass it, the binary looks for infrarust.toml in the working directory (/app).
To also expose the web admin API, add -p 8080:8080 and set bind = "0.0.0.0:8080" (or any non-loopback address) in the [web] section of your config. The API requires an api_key when bound to a non-loopback address.
Docker Compose
A minimal docker-compose.yml:
services:
infrarust:
image: ghcr.io/shadowner/infrarust:latest
command: ["--config", "/app/config/infrarust.toml"]
ports:
- "25565:25565"
volumes:
- ./config:/app/config
restart: unless-stopped
stop_grace_period: 45s2
3
4
5
6
7
8
9
10
docker stop sends SIGTERM and kills the container 10 seconds later by default. Infrarust drains connections for up to 30 seconds and then gives plugins up to 10 seconds, so stop_grace_period: 45s (or docker stop -t 45) lets it finish. A second SIGTERM while it is stopping makes it exit at once.
Environment variables
Infrarust reads these environment variables at runtime:
| Variable | Description |
|---|---|
RUST_LOG | Log level filter. Overrides the --log-level CLI flag. Accepts trace, debug, info, warn, error, or module-level filters like infrarust_core=debug. |
INFRARUST_COLOR | auto (default), always or never. Containers started without -t have no terminal, so auto prints plain text; use always for panels like Pterodactyl that display colors. See Colors. |
Set it in your Compose file or docker run:
docker run -d \
--name infrarust \
-p 25565:25565 \
-e RUST_LOG=debug \
-v ./config:/app/config \
ghcr.io/shadowner/infrarust:latest \
--config /app/config/infrarust.toml2
3
4
5
6
7
Or in Compose:
services:
infrarust:
image: ghcr.io/shadowner/infrarust:latest
command: ["--config", "/app/config/infrarust.toml"]
ports:
- "25565:25565"
volumes:
- ./config:/app/config
environment:
RUST_LOG: "info"
restart: unless-stopped2
3
4
5
6
7
8
9
10
11
CLI flags
These flags are passed after the image name in docker run, or in the command field of Compose:
| Flag | Default | Description |
|---|---|---|
-c, --config <PATH> | infrarust.toml | Path to the proxy configuration file |
-b, --bind <ADDR> | from config | Override the bind address |
-l, --log-level <LEVEL> | info | Log level filter (overridden by RUST_LOG) |
--plugins-dir <PATH> | from config | Override the plugins directory path |
Auto-discovery with Docker labels
When Infrarust runs alongside your Minecraft servers in Docker, it can discover them automatically through container labels. No static server config files needed.
Add a [docker] section to your infrarust.toml:
servers_dir = "/app/config/servers"
[docker]2
3
Then label your Minecraft containers:
services:
infrarust:
image: ghcr.io/shadowner/infrarust:latest
command: ["--config", "/app/config/infrarust.toml"]
ports:
- "25565:25565"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./config:/app/config
restart: unless-stopped
survival:
image: itzg/minecraft-server
environment:
EULA: "TRUE"
TYPE: "PAPER"
labels:
infrarust.enable: "true"
infrarust.domains: "survival.example.com"
restart: unless-stopped2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Infrarust scans for containers with infrarust.enable=true, reads their labels, resolves their network address, and registers them as backend servers. When containers start or stop, the routing table updates in real time.
WARNING
Mounting the Docker socket (/var/run/docker.sock) gives Infrarust read access to all containers on the host. Mount it read-only (:ro) and understand the security implications before deploying this in production.
The full set of container labels and provider options is documented in Docker Provider.
Networking
When Infrarust and your Minecraft servers share a Docker network, containers communicate by internal IP. This is the recommended setup because it avoids port publishing on the host for backend servers.
networks:
minecraft:
driver: bridge
services:
infrarust:
image: ghcr.io/shadowner/infrarust:latest
command: ["--config", "/app/config/infrarust.toml"]
ports:
- "25565:25565"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./config:/app/config
networks:
- minecraft
restart: unless-stopped
survival:
image: itzg/minecraft-server
environment:
EULA: "TRUE"
labels:
infrarust.enable: "true"
infrarust.domains: "survival.example.com"
networks:
- minecraft
restart: unless-stopped2
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
Tell Infrarust which Docker network to prefer for address resolution:
[docker]
network = "minecraft"2
Without this setting, Infrarust picks the first available network IP from each container. If your containers are on multiple networks, set network to avoid resolving to the wrong IP.
Building the image yourself
The included Dockerfile uses a three-stage build: Node on Alpine builds the admin frontend, Rust on Alpine compiles the statically linked binary, and the final stage copies only the binary and CA certificates into a scratch image.
docker build -t infrarust .The build detects the host architecture and targets it automatically. Cross-compilation is handled through Docker buildx:
docker buildx build --platform linux/amd64,linux/arm64 -t infrarust .Connecting to external servers
If your Minecraft servers run outside Docker (bare metal, VMs, another host), you don't need the Docker provider or socket mount. Use static server config files instead:
services:
infrarust:
image: ghcr.io/shadowner/infrarust:latest
command: ["--config", "/app/config/infrarust.toml"]
ports:
- "25565:25565"
volumes:
- ./config:/app/config
restart: unless-stopped2
3
4
5
6
7
8
9
Define your servers as .toml files in config/servers/:
# config/servers/survival.toml
domains = ["survival.example.com"]
addresses = ["192.168.1.50:25565"]2
3
Infrarust watches the servers/ directory and picks up new or changed files without a restart.