# Host a server `horizon-server` hosts a Minecraft Java Edition 26.3 server with Horizon's own game as the engine: one native program, no Java, one settings file, a command line, and a web panel. Vanilla 26.3 clients and Horizon clients can join it. This page covers what works on `main` today and says plainly what doesn't yet. The design and the plan are in [docs/server.md](https://github.com/horizon-rust/horizon/blob/main/docs/server.md). ## Before you start - **Online mode checks accounts** (release v2026.10.17.07 and newer). With `online_mode = true`, the default, players who sign in with the official Minecraft launcher join with their own accounts, as on a vanilla server. - **Names you add are accounts** (release v2026.10.17.14 and newer). `init --player`, `horizon-server whitelist add` and `op`, and the console's `whitelist add`, `op` and `ban` look a name up the way vanilla does: players already seen (kept in `usercache.json`), then Mojang's profile service. In online mode a name with no account is refused. So you can whitelist friends before they first join. On an offline server, `horizon-server whitelist add` always writes the offline UUID, the one the player joins with, also while the server runs (it writes the entry and the game reloads the list): the console's own `whitelist add` takes the account's UUID when someone owns that name, as vanilla does, and that player is then refused. - **Horizon's own client can't sign in to a Microsoft account yet.** That needs an Azure app registration and Mojang's approval, which the project doesn't have ([docs/microsoft-account.md](https://github.com/horizon-rust/horizon/blob/main/docs/microsoft-account.md)). Until then Horizon players can join offline servers only (`online_mode = false`). An offline server never checks accounts: anyone who reaches it can join under any name. Keep it on your LAN or a tailnet (Tailscale, ZeroTier), or share it only with friends you trust. - **Secure chat** works as on vanilla's server: in online mode, vanilla clients sign their chat, and with `enforce-secure-profile=true` (the default) unsigned chat is refused. On an offline server, or for a player without a chat session (Horizon's own client today), chat stays unsigned and shows as not secure. - **Nether and End:** each player travels on their own through portals, and operators with `execute in run tp`. The players in the level they leave stop seeing them, and the ones in the level they arrive in start to, as on vanilla (checked against vanilla with two players, by command and through an End portal: `tools/test_server_dimension_players_parity.py`). The console can't send another player to a dimension yet (`execute in ... run tp NAME`). - **Movement checks and chunk pacing** (release v2026.10.17.16 and newer). The server pulls back players who move too fast ("moved too quickly"), as vanilla does, and sends chunks as fast as each client acknowledges them. - **No Docker image is published.** You build it yourself from the repository (below). Publishing an image needs the project owner. - Nothing of Mojang's is downloaded or shipped. The server reads the game files you prepare from your own copy of Minecraft 26.3. ## 1. Install Linux (x86-64) and macOS (Apple Silicon): ```sh curl -fsSL https://raw.githubusercontent.com/horizon-rust/horizon/main/packaging/server/install.sh | sh ``` Windows (x64), in PowerShell: ```powershell irm https://raw.githubusercontent.com/horizon-rust/horizon/main/packaging/server/install.ps1 | iex ``` The script downloads the latest Horizon release for your computer from GitHub, checks it against the SHA-256 GitHub publishes, and never asks for sudo or administrator rights. It installs: - the release in `~/.local/share/horizon/releases/` (Windows: `%LOCALAPPDATA%\Horizon`), with `current` pointing at the one in use; - `horizon-server` and `horizon-prepare` in `~/.local/bin` (Windows: added to your PATH); - a `game-files` folder for the files you prepare, kept across updates. Options: `--version vX.Y.Z`, `--dir FOLDER`, `--bin FOLDER` (Windows: `-Version`, `-Dir`). Other systems, such as Linux on ARM, build from source: `cargo build --release -p horizon-client --bin horizon-client -p horizon-server` and keep both programs in one folder. The Horizon launcher also installs releases. Every release folder holds `horizon-server`, so you can run it from there too. ## 2. Prepare the game files Horizon reads Minecraft's data from your own copy of the game. You need Minecraft Java Edition 26.3, played once from the official launcher on this computer, a Java 25 runtime, and the 26.3 `server.jar`: it's the `downloads.server.url` in your launcher's `versions/26.3/26.3.json`. Then: ```sh horizon-prepare --prepare --server-jar /path/to/server.jar ``` Add `--minecraft-dir PATH` if your launcher keeps the game elsewhere. The prepared files go to `game-files`, which updates keep. **On a headless machine** (a VPS, a home server), prepare on your own computer instead, with the launcher or `horizon-prepare`, and copy the prepared `26.3` folder to the server: into `~/.local/share/horizon/game-files/horizon-client/26.3`, or into `resources/` in the server folder, or anywhere `[engine] resources` in `server.toml` names. A folder copied this way needs release v2026.10.17.07 or newer: older ones don't keep `registries.json` and `commands.json` with the prepared files, and the server then stops with "registries.json not found next to the resources". Prepare again with a newer release. ## 3. First run ```sh mkdir my-server && cd my-server horizon-server init ``` `init` asks five things and writes `server.toml`: 1. whether you agree to the [Minecraft EULA](https://aka.ms/MinecraftEULA): only a yes writes `eula=true`; 2. the world's name and an optional seed (an existing world or vanilla server folder is kept and its `server.properties` imported); 3. the port, 25565 by default; 4. whether to use a whitelist (yes by default, as in vanilla) and who to add; 5. whether to make the first player an operator. It then tells you where it found the game and its files. Every question has a flag, so it also runs from a script: ```sh horizon-server init --eula --world survival --seed 12345 --port 25565 --whitelist --player Alex --op Alex --yes ``` `--no-whitelist` turns the whitelist off, and `--offline` turns online mode off (see "Before you start"). The players you add are looked up as Minecraft accounts in online mode, and get offline UUIDs on an offline server, as vanilla does. Start the server in the foreground, with the console on your terminal (Ctrl-C stops it cleanly): ```sh horizon-server run ``` or in the background: ```sh horizon-server start horizon-server status ``` ```text Running, up 29 s TPS 20.0, MSPT 1.0 ms average, 3.1 ms P95 0 of 20 players, 377 chunks loaded RAM 1.42 GB, CPU 2 % of a core ``` Players join at your computer's address and the port (`192.168.1.20:25565`, or a tailnet name). Every command takes `--dir FOLDER` to work on a server folder from elsewhere. ## 4. Settings: `server.toml` You edit one file. The server writes vanilla's `server.properties` from it before every start, so the game reads exactly what a vanilla server reads, and a vanilla server folder works as it is. ```toml [server] motd = "A Minecraft Server" port = 25565 bind = "" # listen on every address, or only on this one (server-ip) max_players = 20 online_mode = true # check accounts with Mojang (see "Before you start") whitelist = true enforce_whitelist = false view_distance = 10 simulation_distance = 10 [world] name = "world" seed = "" # empty: a random seed on the first start game_mode = "survival" difficulty = "easy" hardcore = false # Any other server.properties key, spelled as vanilla spells it: [properties] # spawn-protection = 16 # resource-pack = "https://example.com/pack.zip" # offered to players as they join; with # resource-pack-sha1, require-resource-pack and resource-pack-prompt, as on vanilla [engine] path = "" # the game program; empty: the one beside horizon-server resources = "" # the prepared game files; empty: resources/ here, else the usual places restart_on_crash = true [backups] every = "6h" # 30m, 6h, 1d...; "0" turns automatic backups off keep = 12 # automatic backups kept; backups made by hand are never deleted [panel] bind = "127.0.0.1:8130" # this computer only, or a tailnet address (100.x); "" turns it off allow_public = false ``` The defaults are vanilla 26.3's. Unknown keys and values vanilla would refuse are errors, so a typo stops the start instead of being ignored. When the game changes a setting itself (`/whitelist on`), the change is copied back into `server.toml` when it stops, and your comments are kept. Changes to the file apply at the next restart: there is no live reload yet. Already have a vanilla server? Run `horizon-server init` in its folder, or `horizon-server import server.properties` to write `server.toml` from it. The world, `ops.json`, `whitelist.json` and the ban lists keep vanilla's names and formats, so going back to vanilla works too. ## 5. Commands ```text horizon-server init first run horizon-server run in the foreground, with the console; Ctrl-C stops it horizon-server start | stop | restart in the background (its output goes to logs/supervisor.log) horizon-server status [--json] up or not, TPS, MSPT, players, chunks, RAM, CPU horizon-server cmd COMMAND... one console command, and what it answers horizon-server logs [-f] [-n N] the log; -f follows it horizon-server op|deop NAME horizon-server whitelist on|off|list horizon-server whitelist add|remove NAME horizon-server backup [NAME] back up now horizon-server backups [--json] list the backups horizon-server restore BACKUP [--yes] horizon-server service install|uninstall|print [--system] horizon-server panel a one-time login link for the web panel horizon-server import FILE server.toml from a vanilla server.properties horizon-server config [--json] the settings in server.toml horizon-server config set KEY=VALUE... change settings, checked first (below) horizon-server update [--check] install the newest release and restart on it (below) horizon-server rollback back to the release before ``` While the server runs, `op`, `whitelist` and `cmd` go to the game's console, so they act exactly as vanilla's console commands do. While it's stopped, they edit vanilla's files directly. Any console command works through `cmd`: `horizon-server cmd ban Griefer`, `horizon-server cmd say Restarting in 5 minutes`. To generate the world around spawn ahead of the players, Horizon has its own `pregen` command, off until `server.toml` has `horizon-pregen = true` under `[properties]`: `horizon-server cmd pregen start 2500` ([pregeneration](pregeneration.md)). `config set` changes `server.toml` without opening it: `horizon-server config set world.difficulty=hard server.max_players=10`. Each key is a section and a name from the file (`properties.spawn-protection=0` for any other vanilla key). The values are checked as the server checks them, and nothing is written if one is wrong. A running server takes the difficulty, the default game mode, the whitelist and the backup settings at once; the rest applies at its next start, and `config set` says which. `--json` prints that as `saved`, `applied` and `restart`. The server restarts the game by itself if it crashes: after 5 s, then waiting longer each time, up to 5 minutes. It keeps the crash report and gives up after 5 crashes in 10 minutes, and `status` says why. **Updates** (a server installed with the install script). The server checks for a newer release a minute after it starts and every 6 hours after, and says so in its log, in `status` and in the panel; it never updates by itself. `horizon-server update --check` asks once. `horizon-server update`: 1. downloads the newest release and checks it against the SHA-256 GitHub publishes for it; 2. stops the game, backs up the world (`backups/