W1
Gaming Azure

Wesnoth Dedicated Multiplayer Server on Ubuntu 24.04 on Azure User Guide

| Product: Wesnoth 1.18 Compatible Dedicated Multiplayer Game Server on Ubuntu 24.04 on Azure

Overview

This guide covers the deployment and use of the Wesnoth 1.18 dedicated multiplayer game server on Ubuntu 24.04 on Azure using cloudimg Azure Marketplace images.

wesnothd is the dedicated multiplayer server of The Battle for Wesnoth, the open source turn based fantasy strategy game. It is the headless companion to the desktop game: your players keep using their normal Wesnoth client, change the server address from the public one to your VM, and get their own lobby where they can chat, create games, join each other and play a full match relayed through your host. The server relays the game between clients and gives whoever runs it moderator control over that lobby.

This image ships the SERVER, not the game. There is no desktop client and no graphical interface on this VM, and there is no web interface either. Everything in this guide is done over SSH, plus the in game admin commands you type from a connected Wesnoth client. Your players supply their own copy of the game.

No game content is redistributed. wesnothd is a WML relay and needs no campaigns, maps, artwork or music, so none are installed. The only Wesnoth bytes on this VM are the server binary itself and its GNU General Public License version 2 licence text under /usr/share/doc/wesnothd/.

Version compatibility matters, and it is not optional. Wesnoth's network protocol is version gated. This server is built from the 1.18.7 upstream source release and accepts the 1.18.* client series only. A 1.16 client, or a 1.19 development build, is cleanly refused with the accepted version list rather than half connecting. Ubuntu 24.04's own repository only carries the older 1.16 line, which is exactly why this image builds 1.18 from source instead.

Security by design. A public game port is the whole point of a game server, so everything else is locked down:

  • No administrator password exists in the image. The captured image contains no server configuration file at all. The moderator password is generated uniquely on your VM at first boot and written only to /etc/wesnothd/server.cfg and a root only credentials file. Nothing is shared between customers.

  • The server physically cannot start before that happens. wesnothd.service is enabled so it survives reboots, but it carries two start up conditions: the first boot ready marker and the server configuration. Neither exists in the shipped image, so the game port cannot be bound by a server that has no moderator password.

  • One port, and nothing else. TCP 15000 is the only listener this product opens. The moderator control channel is a FIFO inside a directory readable only by root and the service account, and the configuration file holding the password is not world readable.

  • The server runs unprivileged as the dedicated wesnothd system account under a hardened systemd unit.

What is included:

  • wesnothd 1.18.7, built from the official upstream source release with the server target only, verified against a pinned SHA 256 checksum before extraction, running under systemd as wesnothd.service

  • A per VM moderator password generated on first boot and written to a root only credentials file

  • wesnothd-selfcheck, a bundled command that proves the server end to end by driving the real Wesnoth wire protocol as two independent clients, creating a game and relaying a lobby message

  • Ubuntu 24.04 LTS, fully patched, with unattended security upgrades enabled

Key facts

Item Value
Platform Ubuntu 24.04 LTS on Azure
Default administrative user azureuser
Game port 15000/tcp
Accepted client versions 1.18.*
Server binary /usr/local/bin/wesnothd
Server configuration /etc/wesnothd/server.cfg
Per VM credentials /root/wesnothd-credentials.txt

Prerequisites

  • Active Azure subscription, an SSH public key, and a VNet and subnet in the target region

  • Subscription to this listing on Azure Marketplace

  • A Network Security Group allowing TCP 22 for administration and TCP 15000 for the game itself. Your players connect to 15000, so it must be reachable from wherever they are. No other port needs to be opened.

  • A copy of Wesnoth 1.18.x on each player's machine. The server accepts that series only.

Recommended virtual machine size: Standard_B2s with 2 vCPU and 4 GB RAM comfortably hosts a community lobby with several concurrent games. wesnothd relays turns rather than simulating them, so it is light on CPU. For a large public server or a tournament with many simultaneous matches, choose Standard_D2s_v5 or above.

Deploy the virtual machine

Deploy from the Azure Portal by selecting the image from Azure Marketplace, choosing your VM size, and supplying your SSH public key for the azureuser account. Or deploy from the Azure CLI.

These commands run on your own workstation, not on the VM:

az vm create \
  --resource-group my-resource-group \
  --name my-wesnoth-vm \
  --image <this-marketplace-image> \
  --size Standard_B2s \
  --admin-username azureuser \
  --generate-ssh-keys \
  --public-ip-sku Standard

az vm open-port --resource-group my-resource-group --name my-wesnoth-vm --port 15000

Connect over SSH once the VM is running:

ssh azureuser@<vm-ip>

Confirm the server is healthy

The server starts automatically once first boot has generated your moderator password. Check it and confirm the version:

systemctl is-active wesnothd
wesnothd --version

Expected output

active
Battle for Wesnoth server 1.18.7

Now confirm the network posture. Exactly one port belongs to this product, and it is the game port:

ss -tln | grep 15000

Expected output

LISTEN 0      4096         0.0.0.0:15000      0.0.0.0:*
LISTEN 0      4096            [::]:15000         [::]:*

That is the shape you want: 15000 reachable by your players, and nothing else added to the machine.

The dedicated multiplayer server active under systemd, reporting its version, with the game port as its only listener

Retrieve your per VM credentials

The moderator password is generated on this VM's first boot and exists nowhere else. Read it as root:

sudo cat /root/wesnothd-credentials.txt

Expected output

# Wesnoth 1.18.7 dedicated multiplayer server (wesnothd) — generated on
# first boot. The administrator password below is UNIQUE to this VM. Store it
# somewhere safe; it is shown in plain text only here and in the server config.

# --- The game server your players connect to ---
WESNOTHD_URL=wesnoth://<vm-ip>:15000
wesnothd.host=<vm-ip>
wesnothd.port=15000
wesnothd.versions_accepted=1.18.*

# --- Administrator credential ---
# In a connected Wesnoth 1.18 client, type this in the lobby chat:
#     /query admin <WESNOTHD_ADMIN_PASSWORD>
# The server replies "You are now recognized as an administrator." and you can
# then use /query kick, /query ban, /query motd and the rest.
wesnothd.admin_password=<WESNOTHD_ADMIN_PASSWORD>

# Players join with:  Multiplayer -> Join Official Server -> change the address to
#     <vm-ip>:15000
# Only Wesnoth 1.18.x clients are admitted; anything else is cleanly refused.
#
# Re-verify the whole server at any time (drives a real two-client session):
#     sudo wesnothd-selfcheck

wesnothd.admin_password is the moderator password for this server and this server only. Store it somewhere safe. Anyone who has it can moderate your lobby, so treat it like a root password.

The address recorded in WESNOTHD_URL and wesnothd.host is the address of the VM's own network interface as the VM itself sees it. Azure's instance metadata service returns an empty body for Standard SKU public IP addresses, so first boot deliberately falls back to that interface address rather than writing an empty host. Your players connect on the VM's public address, which you can read at any time with az vm show ... --query publicIps, shown further down this guide. The port is 15000 either way.

The per VM credentials file with this server's own moderator password and the address players join

Prove the server really hosts games

A listening socket is not proof that a game server works. The bundled self check drives the real Wesnoth wire protocol as two independent clients: it logs both into your lobby, confirms a client on an unsupported version is refused, relays a lobby chat message from one to the other, has the first create a multiplayer game, confirms the second sees that game advertised live in the lobby game list, round trips your moderator password against a wrong one and the real one, and checks that the control channel and the configuration file are not readable by other users on the VM.

sudo wesnothd-selfcheck

Expected output

[wesnothd-selfcheck] 1. wesnothd binary version
    OK: Battle for Wesnoth server 1.18.7
[wesnothd-selfcheck] 2. game port 15000 reachable on this VM's routable address <vm-ip>
    OK: TCP <vm-ip>:15000 accepts connections
[wesnothd-selfcheck] 3. host client logs in and joins the lobby
    OK: cimg-host-f80d79 joined the lobby (handshake 42, version 1.18.7 accepted, [join_lobby] received)
[wesnothd-selfcheck] 4. second, independent client logs in and joins the same lobby
    OK: cimg-obs-f80d79 joined the lobby alongside cimg-host-f80d79
[wesnothd-selfcheck] 5. client on an unsupported version is refused
    OK: server returned [reject] with its accepted_versions list
[wesnothd-selfcheck] 6. lobby chat is relayed live from one client to the other
    OK: message relayed cimg-host-f80d79 -> cimg-obs-f80d79 through the server
[wesnothd-selfcheck] 7. host creates a multiplayer game
    OK: [create_game] and its [scenario] accepted for 'cloudimg selfcheck f80d79'
[wesnothd-selfcheck] 8. the second client sees the new game advertised in the lobby game list
    OK: cimg-obs-f80d79 sees 'cloudimg selfcheck f80d79' advertised by the server (broadcast live)
[wesnothd-selfcheck] 9. per-VM administrator credential is accepted, a wrong one is refused
    OK: wrong password refused; the per-VM password granted administrator rights
[wesnothd-selfcheck] 10. control FIFO and server configuration are not world-accessible
    OK: /run/wesnothd/socket mode 640 - not world-writable
    OK: /etc/wesnothd/server.cfg mode 640 - not world-readable

WESNOTHD_SELFCHECK_OK

Run this any time you want to confirm the server is genuinely serving, for example after changing the configuration or resizing the VM.

A real two client protocol session: login, game creation, lobby advertisement and live relay through the server

Point your players at the server

Each player needs Wesnoth 1.18.x installed. In the game:

  1. Choose Multiplayer from the title screen.
  2. Choose Join Official Server, then edit the server address, or use Connect to Server if your client offers it.
  3. Enter your VM's public address and port, for example 20.0.0.1:15000.
  4. Pick any username and connect. There is no registration on this server, so any name that is not already in use is accepted.

Players land in your lobby, where they can create a game, join one another and start playing. The WESNOTHD_URL line in the credentials file is the same address in the wesnoth:// form that recent clients accept directly.

Find the public address of the VM at any time from your own workstation:

az vm show --resource-group my-resource-group --name my-wesnoth-vm \
  --show-details --query publicIps --output tsv

Moderate the lobby from inside the game

Connect with any Wesnoth 1.18 client, then type this in the lobby chat, substituting the password from your credentials file:

/query admin <WESNOTHD_ADMIN_PASSWORD>

The server replies You are now recognized as an administrator. and your client gains moderator rights. A wrong password is refused with Error: wrong password, and the attempt is logged on the server.

Once recognised you can use the moderation commands from the lobby chat:

Command What it does
/query status Lists connected players and their addresses
/query games Lists the games currently running on the server
/query kick <nick> Disconnects a player
/query kban <nick> <time> <reason> Kicks and bans a player for a period
/query bans Shows the current ban list
/query unban <mask> Lifts a ban
/query motd <text> Sets the message of the day players see on joining
/query metrics Shows server traffic and performance counters
/query help Lists every command the server accepts

The per VM moderator password accepted by the in game admin query, and a wrong password refused

Moderate from the server shell

The same commands are available without a game client through the server's control FIFO. This is useful for scripting or for an emergency when you cannot get a client connected. Run as root:

sudo timeout 5 sh -c 'echo "status" > /run/wesnothd/socket'
sudo journalctl -u wesnothd --since "1 minute ago" --no-pager | tail -20

The server executes the command and writes the answer to its journal. The FIFO lives in a directory owned by root and the wesnothd service account with mode 0750, so ordinary users on the VM cannot drive it.

Change the server settings

The configuration lives at /etc/wesnothd/server.cfg and is read at start up. It is owned root:wesnothd with mode 0640, because it holds your moderator password.

sudo cat /etc/wesnothd/server.cfg

Useful keys:

Key Meaning
versions_accepted Which client series may connect. 1.18.* by default. Widening this to a series the binary does not speak will break games, not enable them
passwd The moderator password checked by /query admin
motd The message of the day shown when a player joins
max_messages and messages_time_period Chat flood control: how many messages a player may send in that many seconds
save_replays Whether finished games are written to disk. Off by default so the appliance does not grow its disk unattended
fifo_path The shell control channel described above

After editing, restart the server. Connected players are disconnected, so pick a quiet moment:

sudo systemctl restart wesnothd
systemctl is-active wesnothd

Turn on replay saving

If you want finished games kept for later viewing, give the server a directory it can write to and enable the option:

sudo install -d -o wesnothd -g wesnothd -m 0750 /var/lib/wesnothd/replays
sudo sed -i 's|^save_replays=.*|save_replays="yes"|' /etc/wesnothd/server.cfg
sudo sh -c 'grep -q ^replay_save_path /etc/wesnothd/server.cfg || echo "replay_save_path=\"/var/lib/wesnothd/replays/\"" >> /etc/wesnothd/server.cfg'
sudo systemctl restart wesnothd

Replays accumulate indefinitely, so keep an eye on du -sh /var/lib/wesnothd/replays or prune them on a schedule.

Change the moderator password

Rotate the password whenever someone leaves your moderator group:

NEW_PASSWORD=$(openssl rand -hex 24)
sudo sed -i "s|^passwd=.*|passwd=\"${NEW_PASSWORD}\"|" /etc/wesnothd/server.cfg
sudo sed -i "s|^wesnothd.admin_password=.*|wesnothd.admin_password=${NEW_PASSWORD}|" /root/wesnothd-credentials.txt
sudo systemctl restart wesnothd
sudo wesnothd-selfcheck

The self check at the end re-proves the new password authenticates, so you never rotate into a server you cannot moderate.

Server components

Component Version Purpose
wesnothd 1.18.7 The dedicated multiplayer game server, built from the official upstream source release, server target only
Ubuntu Server 24.04 LTS Base operating system, fully patched with unattended security upgrades enabled
systemd unit wesnothd.service Runs the server unprivileged, gated so it cannot start before first boot has minted your password
systemd unit wesnothd-firstboot.service Runs once on your first boot to generate the per VM moderator password and configuration

Filesystem layout

Path Purpose
/usr/local/bin/wesnothd The server binary
/etc/wesnothd/server.cfg Server configuration, including the moderator password. 0640 root:wesnothd
/run/wesnothd/socket Moderator control FIFO, inside a 0750 directory
/root/wesnothd-credentials.txt Per VM credentials. 0600 root:root
/usr/share/doc/wesnothd/COPYING The upstream GNU General Public License version 2 text
/usr/local/sbin/wesnothd-selfcheck End to end server self check
/var/lib/cloudimg/ First boot markers and sentinel
/etc/cloudimg-build-versions What was built into this image, and from which pinned source

Network ports

Port Bound to Purpose
15000/tcp All interfaces The Wesnoth game port. Your players connect here
22/tcp All interfaces SSH administration

Managing the service

sudo systemctl status wesnothd --no-pager
sudo systemctl restart wesnothd
sudo systemctl stop wesnothd
sudo journalctl -u wesnothd --no-pager -n 50

Scripts and log files

Path Purpose
/usr/local/sbin/wesnothd-firstboot.sh Generates the per VM moderator password and configuration on first boot
/usr/local/sbin/wesnothd-selfcheck Drives a real two client protocol session against this server
/var/log/cloudimg-firstboot.log What first boot did on this VM
/stage/scripts/initial_boot_update.sh Applies operating system updates on first boot
/stage/scripts/initial_boot_update.log Output of that update run

On startup

On the very first boot, wesnothd-firstboot.service generates a moderator password unique to this VM, writes /etc/wesnothd/server.cfg, releases the two start up conditions on wesnothd.service, starts the server, waits for the game port to bind, and writes your credentials file. It then disables itself, so it never runs again. Its log is /var/log/cloudimg-firstboot.log.

/stage/scripts/initial_boot_update.sh applies operating system updates on first boot from a root crontab entry. List it with:

sudo crontab -l

To disable it, edit the root crontab with sudo crontab -e and remove the initial_boot_update.sh line.

Troubleshooting

Players get "The server responded with an unknown version" or are refused on connect

The client series does not match. This server accepts 1.18.* only. Check what the server thinks:

sudo grep versions_accepted /etc/wesnothd/server.cfg
wesnothd --version

Ask the player to install Wesnoth 1.18.x. Do not widen versions_accepted to a series this binary does not speak: the handshake would succeed and the game would then desynchronise.

Nothing answers on port 15000

First check the service and the two start up conditions:

systemctl is-active wesnothd
systemctl status wesnothd --no-pager | head -20
sudo ls -l /var/lib/cloudimg/wesnothd-ready /etc/wesnothd/server.cfg

If systemctl status reports the unit was skipped because a condition failed, first boot did not complete. Look at what it did:

sudo cat /var/log/cloudimg-firstboot.log

If the service is active locally but players still cannot reach it, the port is not open in Azure. From your workstation:

az vm open-port --resource-group my-resource-group --name my-wesnoth-vm --port 15000

/query admin says "Error: wrong password"

You are using a password from somewhere other than this VM. Every VM gets its own:

sudo grep '^wesnothd.admin_password' /root/wesnothd-credentials.txt

If the credentials file and /etc/wesnothd/server.cfg have drifted apart, server.cfg is what the server checks against.

/query admin says "No password set"

server.cfg has no passwd key, usually because it was edited by hand. Restore it and restart:

sudo grep '^passwd' /etc/wesnothd/server.cfg || echo "no passwd key present"
sudo systemctl restart wesnothd

The self check reports an error

Read the step it stopped on, then look at the server's own view:

sudo journalctl -u wesnothd --no-pager -n 60

A failure at the login step usually means the service is not running. A failure at the game creation step means the server is up but rejecting the session, which the journal will explain.

Security recommendations

  1. Restrict SSH. Limit TCP 22 in your Network Security Group to your own address range, and use SSH keys rather than passwords.

  2. Leave the game port open only as wide as you need. TCP 15000 has to be reachable by your players, but if your community is on a known network you can scope the rule to it rather than to the whole internet.

  3. Guard the moderator password. It is the only privileged boundary on this server. Keep /root/wesnothd-credentials.txt root only, rotate the password when someone leaves your moderator group, and never paste it into a public lobby.

  4. Set a message of the day with /query motd so players can see they are on your server and know your rules.

  5. Use the ban list. /query kban and /query bans are the tools for dealing with disruptive players; the server keeps bans across reconnects.

  6. Keep the operating system patched. Unattended security upgrades are enabled. Check them with systemctl status unattended-upgrades --no-pager.

  7. Watch disk if you enable replay saving. Replays are off by default for exactly this reason.

Support

cloudimg provides 24/7 support for this image. Contact support@cloudimg.co.uk with your VM's region, size and the output of sudo wesnothd-selfcheck.

Upstream project documentation for the multiplayer server is at wiki.wesnoth.org/MultiplayerServers. BATTLE FOR WESNOTH and WESNOTH are names and marks of The Battle for Wesnoth project and its contributors. cloudimg is not affiliated with, endorsed by, or sponsored by that project; this image packages the project's GNU General Public License version 2 licensed dedicated server unmodified.