Hosting a Server

Run Open Historia on your own machine, play offline, or reach it from your phone on the same network.

The desktop app already runs a local server — that is how it works. This page is about running that server deliberately: to play entirely offline, to reach your campaign from another device, or to keep everything on hardware you control.

A server of your own is for you. It lets you reach your game from another device. It does not let two people play the same campaign. If you connect from your phone and your laptop at once, you are two windows onto one single-player game, and you will fight each other for it.

What you get

Running it

If you installed the desktop app, you are already running it. To run the server on its own from a source checkout:

git clone https://github.com/Open-Historia/open-historia.git
cd open-historia
npm ci
node scripts/fetch-map-assets.mjs
npm run build
node server/server.js

It listens on port 3000. Open http://localhost:3000. The server serves the game that npm run build made, so build again after you pull a newer version.

That is the stable build. For the beta, run git checkout beta before npm ci.

Node.js ^20.19.0 or >=22.12.0 is required on the server machine. Client devices need only a browser.

Connecting from another device

The server answers only the machine it runs on until you say otherwise. There are two ways to open it up:

Then open the address it gives you — http://192.168.x.x:3000 — from the other device.

On beta the server answers only to names it knows: localhost, IP addresses and this computer's own name. That stops a web page from reaching your server by pointing a name of its own at it. To reach it by any other name — one on your network, or one in front of a reverse proxy — list the name in OH_ALLOWED_HOSTS, comma-separated.

Writes from a page served by a different origin are blocked either way, which is what stops a web page you visit from editing your saves. To allow them:

OH_ALLOW_CROSS_ORIGIN=1 node server/server.js

Read this before you open it up

The game's API has no password. There is no login, no token and no permission model.

Once it is open, anyone who can reach port 3000 can read, change and delete your games and scenarios. On a home network behind a router that is generally fine. It is not fine on a shared, public or untrusted network, and it is emphatically not fine exposed directly to the internet. Turn the switch off again when you are done.

Two things soften it: requests from other devices are rate-limited (1,200 a minute by default), and the AI relay — which forwards requests to providers that refuse browser calls — still answers only this machine, so your computer cannot be used as an open proxy by anything on the network. Set OH_ALLOW_REMOTE_RELAY=1 if you do want a device on your network to relay its AI calls through this server.

If you want to reach your game from outside your home, use a private network overlay — Tailscale, ZeroTier or a WireGuard tunnel — rather than forwarding port 3000. Those give you the same access without publishing an unauthenticated API to the world.

If you must expose it, put it behind a reverse proxy such as nginx or Caddy with authentication in front.

Environment variables

Variable
PORT Port to listen on. Default 3000.
OH_HOST Address to bind. Unset: this machine only, unless the in-game switch says otherwise. 0.0.0.0 for every interface. Overrides the switch.
OH_DATA_DIR Where writable state lives — saves, scenarios, uploads. Relocate the whole library with this.
OH_ALLOW_CROSS_ORIGIN Set to 1 to accept writes from a page on another origin.
OH_ALLOW_REMOTE_RELAY Set to 1 to let other devices relay AI calls through this server.
OH_RATE_LIMIT Requests a minute allowed from each other device. Default 1200. This machine is exempt.
OH_RELAY_TIMEOUT_MS How long the AI relay waits for a generation to finish. Default ten minutes.
OH_ASSETS_DIR Where the map binaries are read from.
OH_VISIT_COUNT Set to 0 and the pages this server serves never load a visit counter and never ask.
OH_DISCORD_PRESENCE Set to 0 and the server never tells Discord what you are playing.
OH_ALLOWED_HOSTS Beta only. Extra names the server answers to, comma-separated; * answers every name.

Running as a service

Linux — systemd. Create /etc/systemd/system/open-historia.service:

[Unit]
Description=Open Historia Game Server
After=network.target

[Service]
Type=simple
User=your-user
WorkingDirectory=/path/to/open-historia
ExecStart=/usr/bin/node server/server.js
Restart=on-failure
Environment=PORT=3000
Environment=OH_HOST=0.0.0.0

[Install]
WantedBy=multi-user.target

Then sudo systemctl enable --now open-historia. Drop the OH_HOST line if only that machine should reach it — and read the warning above if you keep it.

macOS — a launchd plist in ~/Library/LaunchAgents/.

Windows — Task Scheduler, triggered at logon, running node server/server.js in the project directory.

The map data

The map binaries — the world's vector tiles, its regions and its cities, about 290 MB on the stable build and about 150 MB on beta — are not in the Git repository. They are release assets, fetched and checked against their checksums by:

node scripts/fetch-map-assets.mjs

Without them the map is blank. Run it again after pulling a newer version: it downloads only what changed. The desktop app does this for you on first launch.

Where your files are

Under the data directory: your games, your scenarios, uploaded basemaps and flags, map editor documents, and the manifests that index them. Back up the directory and you have backed up everything.

Do not edit save files by hand while the game is running. Use the cheats panel for changing a campaign — it goes through the same validation the game does.

Privacy

No accounts, and no game data leaves your machine. Your AI provider key is stored locally and sent only to the provider you configured.

Nothing counts your visits until you say yes. A game page opened on this computer asks whether it may count visits with Google Analytics and Umami, and loads neither unless you agree; Settings → General → Privacy → Count my visits changes the answer, and OH_VISIT_COUNT=0 stops the question altogether. A page opened from another device over the network is never counted. The count carries no game data.

If Discord is running on the same computer, the server tells it what you are playing, so your profile reads "Playing Open Historia" with your country, scenario and date. Show what I am playing on Discord, in the same Privacy section, turns it off.

Importing a scenario from the Community Hub downloads its checked copy from GitHub, and GitHub's count of those downloads is what the hub shows as installs — the game reports nothing of its own. (Versions before October 2026 pinged an anonymous counter on every import; OH_IMPORT_COUNTER_URL, which switched that off, no longer does anything.)

Next

Open Historia is under active development on two channels, and this wiki is written by hand — a page can fall behind a release. Last checked against the game on 2026-10-25 · main 6e6239c4 · beta d7e8fa85. If something here does not match what you see in the game, trust the game and say so on Discord.