Longhouse
v0.2.0
Documentation

A dashboard for your Valheim server.

I wanted one page my family and friends could pull up to see who's on our server, what day it is in-game, who's played the most and who keeps dying. Longhouse runs next to the dedicated server and builds that page from the server's logs.

Status: the dashboard, collector and Docker image work with a vanilla server today. The optional server plugin (tier 1 below) is in mods/ since 0.2.0; see mods/README.md to install them. The public preview runs on invented mock data.

Features

It works fine on a phone too.

Live server statusOnline state, player count, uptime, version, world day, time of day and weather.
Boss progressionAll seven forsaken, which day each fell and who was there.
Armory-style profilesEach Viking as a 3D voxel figure in their own hair, beard and gear that you can drag to turn, surrounded by their equipment slots, plus vitals, food, skills and lifetime stats.
Hall of recordsLeaderboards for kills, deaths, building and distance travelled.
World statsMost-eaten food, materials gathered and creatures slain, filterable by biome.
ActivityA "when we play" heatmap and a live feed of joins, deaths, raids and boss kills.

There's a dark mode, the frontend is plain static files, and there's no tracking or analytics.

Quick start

Longhouse runs as one container on the same machine as your Valheim server. The deploy/ folder has a compose file that adds a read-only Docker proxy (so Longhouse can read the game's logs) and a Cloudflare Tunnel (so your group can reach it without opening any ports).

  1. Copy the deploy folder to your game host
    git clone https://github.com/MrGuato/valheim-game-dashboard.git
    cd valheim-game-dashboard/deploy
    cp .env.example .env
  2. Fill in .env

    The defaults work for a container named valheim. Set LONGHOUSE_TIMEZONE, and list the bosses you've beaten in LONGHOUSE_BOSSES (Valheim 1.0 stores the world in a format Longhouse can't read them from).

  3. Start it
    docker compose up -d
    docker compose logs -f longhouse

    The dashboard listens on 127.0.0.1:8080 on the host. On first start it replays the game container's existing logs, so recent sessions and deaths show up straight away.

  4. Point a Cloudflare Tunnel at it

    If cloudflared already runs on the host, add a public hostname to that tunnel in Zero Trust (Networks → Tunnels), for example valheim.yourdomain.com, with service http://localhost:8080. Otherwise create a Docker tunnel, put its token in TUNNEL_TOKEN, point the hostname at http://longhouse:8080, and start with docker compose --profile tunnel up -d.

  5. Lock it to your group

    In Zero Trust, add an Access application for the same hostname with a policy that allows only your group's email addresses. See Keeping it private.

Not using Docker for the game server? Point VALHEIM_LOG_PATH at the server's log file instead and run npm start with Node 20 or newer.

How it works

Longhouse only uses what the server knows. Nothing is installed on players' PCs, so nobody's achievements are affected. There are two tiers: a vanilla server, and the same server with the optional Longhouse plugin.

Longhouse architecture The Valheim server's log, Steam query and optional server plugin feed the Longhouse collector. The collector writes a snapshot that the static dashboard reads, which your friends open through a password or tunnel. VALHEIM SERVER Server log joins, leaves, deaths, raids Steam query (A2S) online, player count, version Server plugin · optional biome, figure, gear, kills Longhouse collector tails · polls · ingests SQLite history Docker · port 8080 Dashboard static page + snapshot.json Your group via password, tunnel or VPN

Tier 0 · no mods Vanilla

The collector follows the game container's logs, polls the Steam query port (game port + 1, so 2457 by default) and reads the world save. That's enough for online status, who's connected, session lengths, deaths, raids, the activity heatmap, the world day and time, and boss progress. The log lines it matches look like this:

# a player spawned (or respawned)
Got character ZDOID from Astrid : 2084737421:1
# the same line with 0:0 means that player just died
Got character ZDOID from Leif : 0:0

Log formats can change between game updates, so patterns live in one file and are covered by tests.

Tier 1 · server plugin Optional

Available A small BepInEx plugin installed only on the server. It reads what the server already knows about connected players: which biome each player is in (or that they're in a dungeon or asleep), their appearance (body, skin, hair, beard), their visible equipment and their health, which is what the character figures are drawn from. It also counts kills per player and creature (Valheim marks every creature with the players who hit it) and how many building pieces each player has standing. It writes LH1 {json} lines to the server log, which the collector already follows. Players don't need to install anything, and it has no effect on their achievements.

Kills are counted from the day the plugin is installed. Skills, food buffs, crafting and lifetime totals live only in each player's character file on their own PC, so Longhouse doesn't show them. Reading those would need a mod in each player's game, and the Valheim mod loader turns achievements off.

What's tracked

DataWhere it comes fromNeeds
Online status, player count, versionSteam queryVanilla
Who's online, session length, last seenServer logVanilla
Deaths, raids, activity heatmapServer logVanilla
World day, time of dayServer log (1.0) or world saveVanilla
Boss progressionPre-1.0 world save, or set by handVanilla
Playtime, sessions, longest sessionServer logVanilla
Current biome, in a dungeon, asleep, healthServer pluginServer plugin
Character figure, equipped gearServer pluginServer plugin
Kills per creature, boss kills (since the plugin was installed)Server pluginServer plugin
Building pieces standing per playerServer pluginServer plugin
Skills, food, stamina, crafting, lifetime totalsOnly on each player's PCNot shown

Longhouse never stores chat, IP addresses or exact coordinates. Steam IDs are hashed before they're written to disk and are never sent to the browser.

Keeping it private

The code is public; your server isn't. Longhouse is built so you can publish (or fork) the repo without ever exposing your server address, passwords or your group's data.

What stays out of git

  • Server address and secrets live in .env, which is gitignored. The repo only has .env.example with placeholders.
  • Real data lives in ./data (gitignored). The repo only ships mock data.
  • The address on the page is hidden by default (LONGHOUSE_HIDE_ADDRESS=true) and shows "Address shared privately" instead.
# confirm git is ignoring your secrets before you push
git check-ignore .env data/

Don't host real data on GitHub Pages. Pages sites are public to anyone with the link. Use Pages only for the mock-data preview, and serve your group's dashboard from your own machine.

Sharing with your group

OptionGood forEffort
TailscaleSmall groups who don't mind installing an app. Nothing is exposed to the internet.Low
Cloudflare Tunnel + AccessA normal link that asks for an email code. Only addresses on your allow list get in.Medium
Reverse proxy + passwordYou already run Caddy or nginx. Set LONGHOUSE_PASSWORD or use the proxy's basic auth.Medium

Players can also be hidden entirely with LONGHOUSE_HIDDEN_PLAYERS.

Configuration

Everything is set with environment variables in .env. With the deploy compose nothing is required; the defaults fit a container named valheim.

VariableDefaultDescription
LONGHOUSE_SERVER_NAMEemptyDisplay name. Empty uses the name the server reports.
LONGHOUSE_BIND127.0.0.1:8080Host address the deploy compose publishes the dashboard on.
TUNNEL_TOKENemptyOnly with --profile tunnel: token for a cloudflared container.
VALHEIM_CONTAINERvalheimName of the game server container whose logs are read.
DOCKER_HOSTunix:///var/run/docker.sockDocker API endpoint. The deploy compose points this at the read-only proxy.
VALHEIM_HOSThost.docker.internalHost the Steam query is sent to. Never shown on the page.
VALHEIM_QUERY_PORT2457Steam query port (game port + 1).
VALHEIM_MAX_PLAYERS10Shown when the query port doesn't report a maximum.
VALHEIM_WORLDS_DIR/valheim/worlds_localWhere the world saves are mounted (read-only).
VALHEIM_WORLDnewest .dbWorld to read. Defaults to the world named in the logs.
VALHEIM_SAVE_INTERVAL1800Seconds between world saves, for the "next save" countdown.
VALHEIM_LOG_PATHemptyRead this log file instead of the Docker API.
LONGHOUSE_LOG_TAIL300000Log lines replayed from the end of the game container's log on start. Lines already counted are skipped.
LONGHOUSE_PORT8080Port the dashboard is served on.
LONGHOUSE_PASSWORDemptyIf set, the browser asks for this password (any username).
LONGHOUSE_HIDE_ADDRESStrueHide the join address on the page.
LONGHOUSE_HIDDEN_PLAYERSemptyComma-separated names to leave off the dashboard entirely.
LONGHOUSE_BOSSESemptyBoss ids to mark defeated by hand: eikthyr,elder,bonemass,moder,yagluth,queen,fader.
LONGHOUSE_REFRESH_SECONDS15How often the page refreshes and the server is polled.
LONGHOUSE_TIMEZONEUTCIANA timezone for the heatmap and "today", e.g. America/New_York.

Snapshot schema

The collector serves one JSON document at /api/snapshot.json, and the dashboard renders it. Writing your own collector (a Discord bot, a different log parser) only means producing this shape. The full example lives in assets/mock-data.js.

snapshot.json (abridged)

{
  "schema": 1,
  "generatedAt": "2026-10-21T19:40:00Z",
  "server": {
    "name": "Odin's Rest", "online": true, "version": "0.220.5",
    "players": { "online": 4, "max": 10 },
    "worldDay": 412, "timeOfDay": "Evening", "weather": "Mist",
    "address": null
  },
  "bosses": [ { "id": "eikthyr", "defeated": true, "day": 9, "by": ["Astrid"] } ],
  "players": [ {
    "name": "Astrid", "online": true, "biome": "mistlands",
    "vitals": { "hp": [198, 232] }, "topKill": "Greydwarf",
    "equipment": { "weapon": { "name": "Mistwalker", "q": 3 } },
    "stats": { "kills": 812, "deaths": 37, "piecesStanding": 2140 }
  } ],
  "mobs":      [ ["Greydwarf", 3104, "blackforest"] ],
  "heatmap":   [ [0, 0, 1, …] ],   // 7 days × 24 hours, avg players
  "activity":  [ { "m": 6, "type": "join", "text": "**Sigrid** joined the server" } ]
}

Fields that a tier doesn't provide are left out. Biome ids are meadows, blackforest, swamp, mountain, plains, mistlands, ashlands, deepnorth and ocean. Activity text supports **bold** only; everything else is escaped.

Customizing

Colors and theme

All colors are CSS custom properties at the top of assets/styles.css. Change --accent to re-skin buttons, bars and highlights; biome colors are the --b-* tokens. Light is the default. Visitors can switch to dark with the moon button, and the choice is remembered in their browser.

Panels

Every panel is rendered by its own function in assets/dashboard.js (renderBosses, renderLeaders, and so on) and hides itself when the snapshot has no data for it. Adding one means one function and one container in index.html.

Character figures

Profiles show a 3D voxel figure drawn by assets/voxel.js with plain WebGL (no libraries). It's built from a player's appearance (body type, skin, hair, beard) and equipment, shaded with baked ambient occlusion and a thin line along every voxel edge, which is what gives it the pixel-art look. Drag it or use the arrow keys to turn it, and double-click to reset. The small portraits, and browsers without WebGL, use the flat SVG figure from assets/figure.js. Both read gear the same way: armor colors are matched by keywords in the item name, so a new gear set only needs a line in the MATERIALS table in figure.js.

Local development

There's no build step and no npm dependencies. To work on the dashboard with mock data, serve the folder with any static server:

python -m http.server 5178

To run the real collector against a log file, use Node 20 or newer:

VALHEIM_LOG_PATH=./valheim.log LONGHOUSE_DATA_DIR=./data npm start

The page uses api/snapshot.json when the collector serves it and falls back to assets/mock-data.js everywhere else. Run the tests with npm test.

Repository layout

index.html              dashboard
docs.html               this page
assets/                 styles, renderer, SVG and 3D voxel figures, mock snapshot
server/                 collector: logs, Steam query, world save, HTTP
server/test/            log pattern and state tests
deploy/                 compose file with Docker proxy and Cloudflare Tunnel
Dockerfile              ghcr.io/mrguato/valheim-game-dashboard

Roadmap

  • DoneDashboard with armory-style profiles, light and dark themes, docs
  • DoneCollector: Docker log following, Steam query, world clock and boss keys, history
  • DoneMulti-arch Docker image on GitHub Container Registry, Cloudflare Tunnel deploy
  • DoneServer plugin (Tier 1) for biome, appearance and gear
  • DoneKills per creature and pieces standing, counted on the server
  • NextWeather per player
  • IdeaDiscord webhook for boss kills, deaths and raids

FAQ

Do I need mods?

No. Without mods you get status, who's online, sessions, deaths, raids and the heatmap. The character figure, gear, biome, health, kills and pieces need the server plugin, which goes on the server only.

Why voxel figures instead of Valheim's real character models?

Valheim's character models and textures are game assets that can't be redistributed, and there's no public model viewer for them the way there is for some other games. So Longhouse builds its own pixel-art Vikings out of voxels from the same appearance and gear data. They look the part, and no game files are involved.

Does it work with crossplay servers?

Log-based features work either way. Crossplay servers may not answer Steam queries, in which case online status and player count fall back to the log.

Will it slow down my server?

It shouldn't. The collector reads the log as it's written and sends one small query every refresh interval. The server plugin only reads state the server already has.

Can players fake their stats?

Not through Longhouse. Everything it shows comes from the server's own log and what the server already knows; players don't send anything.

Does it run on Windows?

Yes, through Docker Desktop. A standalone build without Docker is on the roadmap.

Is this affiliated with Valheim?

No, it's just a side project. Not affiliated with Iron Gate or Coffee Stain.

Contributing

Issues and PRs are welcome. A couple of things to keep in mind:

  • Never commit a real server address, password, token or player data. Use the mock snapshot for screenshots.
  • Keep the frontend dependency-free so it can be served from anywhere.
  • Log patterns and the snapshot schema are public contracts. Changes to either need a test and a note in the changelog.