Network & remote access
OpenTraderWorld controls who can reach the app through four network modes. After install it is localhost-only: nothing on your network can connect until you change this.
The supported way to switch modes is in-app: Settings → Network. Saving shows the exact restart command to run on the host (the app can't restart its own containers); the stack is briefly offline while containers recreate.
The four modes
| Mode | Reachable by | Protocol | Use when |
|---|---|---|---|
| Localhost only | this machine | HTTP | Default. Safest, nothing on the network can connect. |
| Local network (LAN) | devices on your network, via the machine's IP | plain HTTP | Quick LAN access; fine for trusted home networks. Browsers may warn or force-upgrade to HTTPS, and they refuse the microphone, so voice control does not work from other devices. |
| LAN + HTTPS | devices on your network, via a real domain | HTTPS, trusted certificate | LAN access without browser warnings. Nothing exposed to the internet. |
| Public (Web) | anyone, at your domain | HTTPS (Let's Encrypt) | You want access from anywhere and accept public exposure. |
LAN + HTTPS (no browser warnings)
Browsers increasingly refuse or warn on plain-HTTP sites. This mode serves the app to every device on your network with a real, publicly trusted certificate: no warnings, nothing installed on client devices, and nothing exposed to the internet. Domain ownership is proven with a DNS record (ACME DNS-01 challenge), not an inbound connection, and the domain resolves to your machine's private LAN IP.
Set it up at install time (./setup.sh, mode 3) or later in Settings → Network → Local network (LAN) + HTTPS:
- Sign in at duckdns.org (free), add a subdomain (e.g.
myotw.duckdns.org) and copy your account token, or use your own domain on Cloudflare with a DNS-edit API token. - Enter the domain + token, plus your machine's LAN IP (with DuckDNS the record is pointed at it automatically; on Cloudflare create the A record yourself).
- Apply with the restart command shown. The first request can take ~30 s while the certificate is issued.
Then open https://myotw.duckdns.org from any device on your network.
Notes
- Issued certificates appear in public Certificate Transparency logs, so the domain name is publicly visible (the app itself stays LAN-only).
- The DNS token is stored in
deploy/dns.env: never commit or share that file. - This mode uses ports 80 + 443 instead of the custom port.
If the domain doesn't resolve on some devices
Some ISP routers/resolvers silently drop DNS answers pointing to a private IP ("DNS rebind protection"). Fixes, best first:
Allow the domain in your router/DNS settings.
Enable secure DNS (DNS over HTTPS) in the browser. Chrome: Settings → Privacy and security → Security → Use secure DNS → Cloudflare; Firefox: Settings → Privacy → DNS over HTTPS → Max Protection.
Hosts-file workaround (per machine, and phones can't do this). Map the domain to the server's LAN IP:
bash# macOS / Linux, then flush the cache (macOS only): echo "192.168.1.50 myotw.duckdns.org" | sudo tee -a /etc/hosts sudo dscacheutil -flushcache && sudo killall -HUP mDNSResponderOn Windows, edit
C:\Windows\System32\drivers\etc\hostsas Administrator, add the same line, then runipconfig /flushdns. The hosts file always wins over DNS, so remove the line if the server's IP changes.
Public (Web)
Exposes the app on your own domain with automatic HTTPS.
Prerequisites:
- A domain with a public A / AAAA record pointing at your server's public IP.
- Inbound TCP 80 and 443 reaching the server: open them in the router/cloud firewall and port-forward if behind NAT. Port 80 is required for the certificate (HTTP-01 challenge) and redirects to HTTPS.
Pick mode 4 in ./setup.sh or switch in Settings → Network. Caddy obtains and auto-renews a Let's Encrypt certificate on the first request (~30 s).
Anyone can reach the login page once this is on
Only enable it after your admin account exists and with a strong password. Turn on two-factor authentication before you do. Keep deploy/.env secret. Switch back to a private mode any time in Settings → Network.
The setup token
On LAN + HTTPS and Public, the first-run wizard asks for a setup token before it will create the first account.
The reason is a race: those modes answer the network, and the wizard is open until an account exists. Without a token, whoever loads the page first becomes the administrator — which is a real risk in the gap while DNS propagates, and again if a database volume is ever recreated.
Core mints the token at startup and prints it to its log:
docker compose -f deploy/docker-compose.yml \
--env-file deploy/.env --env-file deploy/network.env logs core | grep "setup token"Paste it into the extra field the wizard shows. A fresh one is minted on every restart, so an abandoned token stops working as soon as the container restarts.
You will not see this if you installed with ./setup.sh: it creates the account from deploy/.env on first boot, before anything can reach the wizard. The token only appears when no account exists yet.
Changing the mode from the CLI
If you picked the wrong mode at install and can't reach the app at all (e.g. chose localhost on a headless server), edit deploy/network.env directly and restart:
cd deploy
# make it reachable on your LAN over plain HTTP (mode 2):
# OTW_BIND=0.0.0.0 (was 127.0.0.1)
# OTW_HTTP_PORT=5454 (or your chosen port)
$EDITOR network.env
docker compose --env-file .env --env-file network.env up -dnetwork.env is secret-free: it holds the bind interface (127.0.0.1 = this machine only, 0.0.0.0 = all interfaces) and ports, interpolated by Compose. LAN + HTTPS needs more than one line (certificate + DNS token), so set that one up from Settings or ./setup.sh mode 3. Once you can open the app, use Settings → Network.