Skip to content

About

Minecraft Server Status (python3 httpd with json output)

Topics

Resources

Stars

0 stars

Watchers

1 watching

Forks

Repository files navigation

mcstatus-httpd

mcstatus-httpd shows any information of a Minecraft Server via HTTP-Server as JSON. It uses mcstatus.

Features:

  • Minecraft Java Edition and/or Bedrock Edition (a server that is not configured is omitted from the output)
  • Java: full player list and plugins via Query, automatic fallback to the normal status ping if Query is disabled
  • Bedrock: NetherNet (default since Bedrock 1.26.50) and RakNet (legacy), both are tried at the same time by default
  • Both servers are queried in parallel, results are cached (protects the Minecraft server)
  • Optional player avatars (Java, head or body), loaded via mcstatus-httpd with cache and provider fallback
  • Optional sub path (HTTP_PREFIX), e.g. for a reverse proxy
  • Optional HTTPS
  • Built-in health check command --healthcheck for docker-compose (also works with the distroless image)
  • Configuration via environment variables and/or command line arguments

INFO

This is (currently) written for my benefit only.
You can use it, but I won't give support if it doesn't work for you. (I'm also not a Python expert and it was written with the help of AI!).
But you are welcome to work on it. ;-)

Minecraft Server

I use Minecraft Server with Docker:

Examples for docker-compose (minecraft.yml, minecraft-bedrock.yml) you can find here: click

Requirements

  • Python >= 3.13
  • mcstatus >= 14.2.0
  • Minecraft Java: Query should be enabled (enable-query=true in server.properties).
    Without Query the status ping is used as fallback, which only provides a player sample (max. ~12 names) and no plugins and map name.
  • Minecraft Bedrock with NetherNet: the server port must be reachable via TCP (status via GET /v1/join).
    With RakNet (Bedrock server setting transport=raknet): the server port must be reachable via UDP.

Configuration

Every setting can be set as environment variable or as command line argument (arguments take precedence). See python3 mcstatus-httpd.py --help.

Environment variable Argument Default Description
MINECRAFT_JAVA_SERVER --java-server (disabled) Java server address
MINECRAFT_JAVA_SERVER_PORT --java-port 25565 Java game port (TCP)
MINECRAFT_JAVA_SERVER_PORT_QUERY --java-query-port same as game port Java query port (UDP)
MINECRAFT_JAVA_SERVER_OVERWRITE --java-overwrite same as server Hostname shown in the output
MINECRAFT_BEDROCK_SERVER --bedrock-server (disabled) Bedrock server address
MINECRAFT_BEDROCK_SERVER_PORT --bedrock-port 19132 Bedrock game port (NetherNet: TCP, RakNet: UDP)
MINECRAFT_BEDROCK_SERVER_OVERWRITE --bedrock-overwrite same as server Hostname shown in the output
MINECRAFT_BEDROCK_TRANSPORT --bedrock-transport auto nethernet (TCP, default since Bedrock 1.26.50), raknet (UDP, legacy) or auto (both at the same time)
MINECRAFT_TIMEOUT --timeout 3 Seconds until a server counts as offline
CACHE_TTL --cache-ttl 10 Seconds a result is reused, 0 = query on every request
HTTP_HOST --http-host 0.0.0.0 Listen address, 0.0.0.0 = IPv4 only, :: = IPv4 + IPv6
HTTP_PORT --http-port 8080 Listen port
HTTP_PREFIX --http-prefix (none) Sub path, e.g. mcstatus → /mcstatus
HTTP_CORS_ORIGIN --http-cors-origin * (all) Websites allowed to load the status (CORS), e.g. https://forum.example.com,https://example.com
HTTP_TRUSTED_PROXIES --http-trusted-proxies (none) Reverse proxies (IPs/networks, e.g. 172.18.0.0/16) whose X-Forwarded-For/X-Real-IP header is used for the client IP in the log
HTTPD_SSL_ENABLE --ssl / --no-ssl 0 Enable HTTPS (1/true/yes/on)
SSL_CERTFILE --ssl-certfile ssl.crt TLS certificate file
SSL_KEYFILE --ssl-keyfile ssl.key TLS private key file
AVATAR_ENABLE --avatar / --no-avatar 0 Player avatars (Java), see Player avatars
AVATAR_TYPE --avatar-type head Default type: head or body
AVATAR_IMAGE_SIZE --avatar-image-size 64 Default image width in pixels requested from the providers (8-300), body images are twice as high
AVATAR_ALLOWED_TYPES --avatar-allowed-types head,body Types that can be requested with ?type= (the default type is always allowed)
AVATAR_ALLOWED_SIZES --avatar-allowed-sizes 16,32,48,64,96,128,192,256,300 Sizes that can be requested with ?size= (8-300, the default size is always allowed)
AVATAR_PROVIDERS --avatar-providers mc-heads,minotar Avatar providers in order of preference (fallback), built-in names or own URL templates
AVATAR_CACHE_TTL --avatar-cache-ttl 3600 Seconds an avatar is used before it is loaded again (the old one is kept if loading fails)
AVATAR_CACHE_TYPE --avatar-cache-type memory memory (lost on restart) or file
AVATAR_CACHE_DIR --avatar-cache-dir /tmp/mcstatus-httpd/avatars Directory of the file cache
AVATAR_CACHE_MAX_ENTRIES --avatar-cache-max-entries 500 Maximum number of cached avatars (every type and size counts), 0 = unlimited
AVATAR_TLS_VERIFY --[no-]avatar-tls-verify 1 0 = accept self-signed HTTPS certificates of own URL templates (built-in providers are always verified)
AVATAR_ONLINE_ONLY --[no-]avatar-online-only 1 Only serve avatars of online players, 0 = any valid player name (see below)
LOGLEVEL --loglevel INFO DEBUG, INFO, WARNING, ERROR, CRITICAL

At least one server (Java or Bedrock) must be configured.
With HTTPS enabled, HTTP_PORT only serves HTTPS (no plain HTTP), e.g. use HTTP_PORT=8443.
If HTTPS is enabled but the certificate files are missing, the service does not start.

IPv4 + IPv6 behind a reverse proxy (e.g. Docker network with --ipv6):

      HTTP_HOST: "::"
      HTTP_TRUSTED_PROXIES: "172.18.0.0/16, 10.0.0.5, fd00:dead:beef::/48, ::1"

HTTP_TRUSTED_PROXIES accepts any number of IPv4/IPv6 networks and single addresses (comma separated).
Only for requests from these proxies the client IP is taken from X-Forwarded-For/X-Real-IP (shown in the log with LOGLEVEL=DEBUG).

Endpoints

Without prefix With HTTP_PREFIX=mcstatus Description
/ /mcstatus Minecraft server status (JSON)
/healthcheck /mcstatus/healthcheck Health check of this service (JSON)
/healthcheck Health check, always available without prefix
/avatar/<name> /mcstatus/avatar/<name> Avatar of a Java player who is online (PNG, any name with AVATAR_ONLINE_ONLY=0), optional ?type=body&size=128, only with AVATAR_ENABLE

GET and HEAD are supported, a trailing slash and query strings (e.g. ?_=123) are ignored (except type and size of the avatar endpoint).
The status endpoint always answers with HTTP 200, an unreachable server is shown with "online": false.

Player avatars

With AVATAR_ENABLE=1 the website examples show avatars instead of the names of Java players (Bedrock provides no player names).
The name is shown as tooltip on mouse over and on tap.

  • The avatars are loaded by mcstatus-httpd and cached, visitors only talk to your server. The avatar providers never see the IP addresses of your visitors.
  • Only players who are currently online are served, other names return 404.
    With AVATAR_ONLINE_ONLY=0 any valid Java player name is served, e.g. as profile picture in your forum. Be aware that anybody can then use your service as avatar proxy: random names can push your players out of the cache, delay avatar downloads and cause rate limits at the providers.
  • AVATAR_TYPE: default type, head (e.g. 64x64 px) or body (e.g. 64x128 px, front view).
  • AVATAR_IMAGE_SIZE: default image width loaded from the providers ({size} in URL templates). The website examples show the avatars in their own size (AVATAR_SIZE in the HTML) and scale them sharp (pixel art), so 64 is enough for most pages. Own URL templates without {size} always deliver their own size.
  • Type and size can be chosen per request, other values return 400:
    /avatar/Notch                        -> AVATAR_TYPE, AVATAR_IMAGE_SIZE
    /avatar/Notch.png?type=body&size=128 -> body, 128x256 px
    
    Allowed are the values of AVATAR_ALLOWED_TYPES and AVATAR_ALLOWED_SIZES (a fixed list, so the number of cached variants stays small).
  • AVATAR_PROVIDERS: providers in order of preference. If a provider fails (timeout, error, rate limit), the next one is used and the failing one is paused for a while (rate limits: as long as the provider asks for via Retry-After).
    • Built-in: mc-heads (mc-heads.net), minotar (minotar.net), both deliver all types and sizes.
    • Own URL templates with {name} and optional {size}, e.g. of a server plugin (must deliver PNG). A template is used for the default type, or with a prefix like head= or body= for this type:
      AVATAR_PROVIDERS: "head=http://minecraft:8100/tiles/faces/32x32/{name}.png, mc-heads, minotar"
      If the plugin uses HTTPS with a self-signed certificate, set AVATAR_TLS_VERIFY: 0 (only for own URL templates, prefer http:// inside a Docker network).
  • Every Java account has a skin (the default skins Steve, Alex etc. if none was chosen). Names without Mojang account (offline-mode servers, Geyser/Floodgate players like .Name, Anonymous Player of hidden player lists):
    • mc-heads delivers the default head (Steve), minotar answers 404 and the next provider is tried.
    • Names that are no valid Java names (e.g. .Name) are not requested at all.
    • If no provider has an avatar, the website shows the name as text, so every player is listed.
  • Cache:
    • Avatars are loaded when they are requested (not on status requests) and used for AVATAR_CACHE_TTL seconds. After that the next request loads it again from the providers. If none of them delivers it, the old avatar is kept and used (next attempt after 5 minutes).
    • AVATAR_CACHE_TYPE=memory (default): in memory, lost on restart.
    • AVATAR_CACHE_TYPE=file: PNG files in AVATAR_CACHE_DIR (<type>/<size>/<name>.png), kept across restarts. Without a volume the files are kept on docker restart, but lost when the container is recreated (e.g. image update). To keep them, mount a volume:
          environment:
            AVATAR_CACHE_TYPE: file
          volumes:
            - mcstatus-avatars:/tmp/mcstatus-httpd/avatars
      A named volume gets the right owner automatically. A bind mount (host directory) must be writable for the user of the container (distroless: UID 65532, alpine: nobody = UID 65534). With read_only: true use a volume or tmpfs for the directory.
    • AVATAR_CACHE_MAX_ENTRIES: the least recently used avatars are removed above the limit (every type and size counts). 0 = unlimited, recommended only with AVATAR_ONLINE_ONLY=1: with any name allowed, random names (mc-heads delivers the default skin for every name) can fill the cache.

Website examples

  • examples/index.html: status page for any website (same box code as the WoltLab box, incl. the needed CSS)
  • examples/wsc-box.html: status box for WoltLab Suite (ACP → Appearance → Boxes → box type "HTML" *), shows status, players, version, MOTD etc. Remove the lines you do not need.

Tip: Serve mcstatus-httpd on the same domain as your website via reverse proxy and HTTP_PREFIX (e.g. https://example.com/mcstatus).
A website served via HTTPS cannot load data from a plain http:// URL.
On a different domain this works too (CORS), by default every website is allowed, see HTTP_CORS_ORIGIN to restrict it.
Settings at the top of the script: MCSTATUS_URL, REFRESH_SECONDS, DATE_FORMAT ('' = page/browser language, e.g. de-DE or iso), SHOW_AVATARS, AVATAR_TYPE ('' = default of mcstatus-httpd, head or body), AVATAR_SIZE (height of a head in px, default 20) and TEXT.

Use

with docker-compose (see docker-compose.yml):

services:

  mcstatus-httpd:
    image: ghcr.io/tob1as/mcstatus-httpd:latest
    container_name: mcstatus-httpd
    restart: unless-stopped
    ports:
      - 8080:8080/tcp
    environment:
      #TZ: Europe/Berlin
      # Demo servers of https://mcstatus.io - replace with your own servers.
      MINECRAFT_JAVA_SERVER: demo.mcstatus.io
      MINECRAFT_JAVA_SERVER_PORT: 25565
      #MINECRAFT_JAVA_SERVER_PORT_QUERY: 25565  # set, if UDP (query) is not equal to TCP port
      #MINECRAFT_JAVA_SERVER_OVERWRITE: minecraft.example.com
      MINECRAFT_BEDROCK_SERVER: demo.mcstatus.io
      MINECRAFT_BEDROCK_SERVER_PORT: 19132
      #MINECRAFT_BEDROCK_SERVER_OVERWRITE: minecraft-bedrock.example.com
      #MINECRAFT_BEDROCK_TRANSPORT: auto  # nethernet, raknet or auto
      #HTTP_PREFIX: mcstatus
      #AVATAR_ENABLE: 1                   # player avatars (Java), see "Player avatars"
    healthcheck:
      test: ["CMD", "/venv/bin/python3", "/app/mcstatus-httpd.py", "--healthcheck"]
      interval: 60s
      timeout: 5s
      start_period: 10s
      retries: 3

The health check uses python3 mcstatus-httpd.py --healthcheck (available since version 2), so no wget/curl is needed (works with the distroless image).
For the alpine image use /usr/local/bin/python3 instead of /venv/bin/python3.

local:

  • install: pip3 install -r requirements.txt
  • set variables/env or use arguments
  • start: python3 -u ./mcstatus-httpd.py --java-server demo.mcstatus.io --bedrock-server demo.mcstatus.io

Build

docker build --target=production --build-arg VERSION="$(date -u +'%Y%m%d-%H%M%S').$(git rev-parse --short HEAD)" -t ghcr.io/tob1as/mcstatus-httpd:latest -f Dockerfile .

The build argument VERSION is shown by --version, /healthcheck and in the logs (env MCSTATUS_HTTPD_VERSION in the image), without it the version is dev.

Releases & Docker image tags

The GitHub workflow builds the Docker image (ghcr.io/tob1as/mcstatus-httpd):

Trigger Version Image tags
Push of a version tag, e.g. v2.0.0 2.0.0 2.0.0, 2.0, 2, latest, <build date>.<commit hash>, <commit hash>
Manual start in the GitHub GUI or schedule <build date>.<commit hash> dev, <build date>.<commit hash>, <commit hash>

A version tag additionally creates a GitHub release with mcstatus-httpd-<version>.zip and .tar.gz (script, requirements.txt, README.md, LICENSE).
Pre-releases like v2.0.0-rc1 only get the tag 2.0.0-rc1 and are marked as pre-release.

git tag v2.0.0
git push origin v2.0.0

Example Output

URL: http://localhost:8080

{
	"date": "2026-01-01T12:00:00+00:00",
	"java": {
		"hostname": "minecraft.example.com",
		"port": 25565,
		"online": true,
		"software": {
			"version": "1.21.4",
			"brand": "Paper on 1.21.4",
			"plugins": ["LuckPerms 5.4.141", "EssentialsX 2.20.1"]
		},
		"players": {
			"online": 3,
			"max": 20,
			"list": ["Player1", "Player2", "Player3"]
		},
		"map": "world",
		"motd": "A Minecraft Server"
	},
	"bedrock": {
		"hostname": "minecraft-bedrock.example.com",
		"port": 19132,
		"online": true,
		"software": {
			"version": "1.21.50",
			"brand": "MCPE",
			"protocol": 766
		},
		"players": {
			"online": 0,
			"max": 20
		},
		"map": "Bedrock level",
		"motd": "Minecraft Bedrock Server",
		"gamemode": "Survival"
	}
}
  • date is the time of the query (UTC, ISO 8601). With caching, multiple requests can return the same value.
  • motd is plain text (formatting codes removed).
  • Java without Query (status fallback): brand and map are null, plugins is empty and list only contains the player sample.
  • Bedrock via NetherNet: brand is null (not provided by NetherNet), motd is the server name.

Unreachable server:

{
	"date": "2026-01-01T12:00:00+00:00",
	"java": {
		"hostname": "minecraft.example.com",
		"port": 25565,
		"online": false,
		"error": "Server unreachable"
	}
}

Health check (/healthcheck):

{"status": "ok", "timestamp": "2026-01-01T12:00:00+00:00", "version": "20260101120000.a1b2c3d4"}

Changes to version 1.x

  • date is now ISO 8601 in UTC (before: local time YYYY-MM-DD HH:MM:SS)
  • plugins and players.list are JSON arrays (before: comma separated string)
  • motd is plain text (before: raw text with § formatting codes)
  • New field online, unreachable servers no longer break the response
  • /healthcheck returns JSON (before: empty text/plain), unknown paths return 404
  • Bedrock: support for NetherNet (MINECRAFT_BEDROCK_TRANSPORT), brand is null via NetherNet
  • New: optional player avatars (AVATAR_ENABLE)

About

Minecraft Server Status (python3 httpd with json output)

Topics

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages