Skip to content

Configuration Basics

This guide covers the basics of configuring Passage for your Minecraft network. For the complete field-by-field reference, see the Configuration Reference.

Passage uses a layered configuration system. Higher layers override lower ones:

  1. Environment Variables — Override any setting at runtime
  2. Auth Secret File — Dedicated file for the authentication cookie secret
  3. Config File — Your deployment-specific settings (YAML, TOML, or JSON)
  4. Built-in Defaults — Sensible defaults compiled into the binary
  • Directoryconfig/
    • config.yaml Your configuration file
    • auth_secret Authentication cookie secret (optional)

Both the config file and auth secret file paths can be customized via environment variables:

config/auth_secret
# Default: config/config (auto-detects .yaml, .toml, .json extension)
CONFIG_FILE=config/config
AUTH_SECRET_FILE=config/auth_secret

Unlike traditional Minecraft proxies that use a flat configuration, Passage organizes everything into routes. Each route matches a hostname pattern and defines its own set of adapters:

config/config.yaml
routes:
- hostname: "mc.example.net"
status:
type: fixed
name: "My Network"
authentication:
type: mojang
discovery:
type: fixed_discovery
targets:
- identifier: "lobby-1"
address: "10.0.1.10:25565"

This is conceptually similar to virtual hosts in a web server — each hostname gets its own independent configuration. See Adapters for a full overview of the adapter system.

The simplest working configuration:

config/config.yaml
routes:
- hostname: ".*"
discovery:
type: fixed_discovery
targets:
- identifier: "server-1"
address: "127.0.0.1:25566"

This configuration:

  • Listens on 0.0.0.0:25565 (the default)
  • Matches any hostname (.* is a regex)
  • Uses Mojang authentication (the default)
  • Returns the default status response
  • Routes all players to 127.0.0.1:25566

Most settings have sensible defaults, so you only need to configure what you want to change.

All configuration fields can be overridden with environment variables using the format PASSAGE_<FIELD>:

Terminal window
export PASSAGE_ADDRESS="0.0.0.0:25565"
export PASSAGE_TIMEOUT=120

Change the default PASSAGE prefix:

Terminal window
ENV_PREFIX=MYNETWORK
MYNETWORK_ADDRESS="0.0.0.0:25565" passage
# Bind address for incoming connections (default: "0.0.0.0:25565")
address: "0.0.0.0:25565"
# Connection timeout in seconds (default: 120)
timeout: 120
# Maximum packet size in bytes (default: 2097152 / 2 MiB)
max_packet_length: 2097152
# Authentication cookie expiry in seconds (default: 30)
auth_cookie_expiry: 30

Prevent connection floods by limiting connections per IP:

rate_limiter:
duration: 60 # Time window in seconds
limit: 60 # Max connections per IP in that window

The rate limiter is disabled by default. Adding the rate_limiter section enables it — there is no separate enabled field.

If Passage is behind a load balancer (HAProxy, AWS NLB, etc.), enable PROXY protocol to preserve client IP addresses:

proxy_protocol:
allow_v1: true # Accept PROXY protocol v1 headers
allow_v2: true # Accept PROXY protocol v2 headers

Like the rate limiter, PROXY protocol is disabled by default and enabled by adding the section.

Each route configures four adapter categories that control different aspects of the connection:

routes:
- hostname: "mc.example.net"
# 1. Status: What players see in the server list
status:
type: fixed
name: "My Network"
# 2. Authentication: How players are verified
authentication:
type: mojang
# 3. Localization: Disconnect messages in multiple languages
localization:
type: fixed
default_locale: "en_US"
# 4. Discovery: How backend servers are found and selected
discovery:
type: dns_discovery
domain: "servers.example.net"
record_type: srv
actions:
- type: meta_filter
rules:
- key: "status"
op: equals
value: "online"
- type: player_fill_strategy
field: "players"
max_players: 50
AdapterPurposeDefaultOptions
StatusServer list response (MOTD, players, favicon)fixedfixed, http, grpc
AuthenticationPlayer identity verificationmojangmojang, disabled, fixed, grpc
LocalizationDisconnect message translationsfixedfixed, grpc
DiscoveryBackend server selectionfixed_discoveryfixed_discovery, dns_discovery, agones_discovery, grpc_discovery

The discovery adapter also supports an actions pipeline that filters, reorders, or customizes the target list. See Discovery Actions for details.

Send traces, metrics, and logs to any OTLP-compatible backend (Grafana Cloud, Datadog, etc.):

otel:
environment: "production"
traces:
address: "https://otlp.example.com/v1/traces"
token: "base64_auth_token"
metrics:
address: "https://otlp.example.com/v1/metrics"
token: "base64_auth_token"
logs:
address: "https://otlp.example.com/v1/logs"
token: "base64_auth_token"

Each endpoint (traces, metrics, logs) is optional and independently configured.

Enable error tracking by adding a sentry section:

sentry:
address: "https://your-key@sentry.io/project-id"
environment: "production"
debug: false

Like other optional sections, Sentry is enabled by its presence — there is no enabled field.

A single backend server with default settings:

config/config.yaml
routes:
- hostname: "mc.example.net"
status:
type: fixed
name: "My Network"
description: "\"Welcome to my server!\""
discovery:
type: fixed_discovery
targets:
- identifier: "lobby"
address: "10.0.0.10:25565"

Automatically discover servers via DNS and fill the fullest available:

config/config.yaml
address: "0.0.0.0:25565"
timeout: 120
rate_limiter:
duration: 60
limit: 60
routes:
- hostname: "mc.example.net"
status:
type: http
address: "https://example.net/status"
cache_duration: 30
discovery:
type: dns_discovery
domain: "servers.example.net"
record_type: srv
actions:
- type: meta_filter
name: "online-filter"
rules:
- key: "status"
op: equals
value: "online"
- type: player_fill_strategy
name: "fill-strategy"
field: "players"
max_players: 50

Delegate all logic to external services:

config/config.yaml
address: "0.0.0.0:25565"
routes:
- hostname: "mc.example.net"
status:
type: grpc
address: "http://status-service:50051"
authentication:
type: grpc
address: "http://auth-service:50051"
localization:
type: grpc
address: "http://localization-service:50051"
discovery:
type: grpc_discovery
address: "http://discovery-service:50051"
actions:
- type: grpc
name: "custom-router"
address: "http://router-service:50051"

Passage validates the configuration on startup. Run it to check for errors:

Terminal window
# Run with debug logging to see configuration details
RUST_LOG=debug passage

Common issues:

  • Invalid regex in hostname field
  • Missing required fields (e.g., address for gRPC adapters)
  • Unknown adapter type values
  • Malformed YAML syntax
  • Store secrets separately: Use config/auth_secret for the authentication secret, not the main config file
  • Use environment variables for sensitive values in CI/CD pipelines
  • Don’t commit your config file to version control if it contains secrets
  • Enable rate limiting in production to prevent connection floods
  • Set reasonable timeouts (60-300 seconds depending on your network)
  • Use descriptive names for discovery actions to simplify debugging