Two Layers of Configuration

OpenHamClock separates server configuration (the .env file) from display preferences (the browser Settings panel). Understanding which is which saves a lot of confusion:

LayerWhereControlsRequires restart?
Server config .env file on the server Callsign, grid, ports, API keys, WSJT-X, security, auto-update Yes — restart the server after changes
Display prefs ⚙️ Settings panel in the browser Theme, layout, visible panels, map layers, units, filters No — take effect immediately
💡
Settings Sync

By default each browser saves its own display preferences independently. On a self-hosted or Pi install you can set SETTINGS_SYNC=true in .env to store all display preferences on the server — every browser that connects will share the same layout and filters. Do not enable this on a multi-user shared instance.

The .env File

On first run the server automatically copies .env.example to .env. The .env file is never overwritten by updates — your settings are safe across upgrades.

Edit it with any plain-text editor:

bash
# On Pi / Linux / macOS
$ nano ~/openhamclock/.env

# On Windows (PowerShell)
PS> notepad openhamclock\.env

Restart the server after every change to .env:

bash
# systemd (Pi / Linux)
$ sudo systemctl restart openhamclock

# Docker
$ docker compose restart

# Manual npm start — just Ctrl+C and npm start again

Callsign & Grid Locator

These two variables are the only ones that are truly required. Everything else has a sensible default.

.env
# Your amateur radio callsign
CALLSIGN=K0CJH

# Maidenhead grid locator — 4 or 6 characters
LOCATOR=DM79

# Optional: override the coordinates derived from LOCATOR
# LATITUDE=39.7392
# LONGITUDE=-104.9903
CALLSIGN Used for sunrise/sunset times, DXCC entity lookups (your home country), map centering, and the DE location panel. Include prefix and suffix (e.g. VK2ABC, OE1XYZ/P).
LOCATOR 4-character grids (e.g. IO91) give ±60 km accuracy. 6-character grids (e.g. IO91wm) give ±3 km. The extra precision matters for satellite pass predictions and sunrise/sunset times. Find your grid here.
LATITUDE / LONGITUDE Optional override. If set, these take precedence over coordinates derived from LOCATOR. Useful if you are right on the edge of a grid square.

Port & Network Settings

.env
# Web UI port (default 3000 in production, 3001 in dev)
PORT=3000

# localhost = only this machine
# 0.0.0.0  = accessible from any device on your network
HOST=localhost
⚠️
Exposing to your network

Setting HOST=0.0.0.0 makes OpenHamClock reachable by any device on your LAN — and potentially beyond, depending on your router/firewall. If you open it to the internet, also set API_WRITE_KEY to prevent unauthorised access to write endpoints (rotator control, settings sync).

Security Settings

.env
# Protects POST endpoints (rotator, settings sync, N3FJP QSO logging)
# Required for cloud / internet-facing installs. Any random string works.
API_WRITE_KEY=change-me-to-something-secret

# Additional allowed CORS origins (comma-separated)
# Only needed if you access OHC from a custom domain
# CORS_ORIGINS=https://yourdomain.com

# Trust X-Forwarded-For headers (set true behind nginx/Caddy/Railway)
TRUST_PROXY=false

API Keys (Optional)

Most of OpenHamClock's data sources are free and require no key. The following optional keys unlock additional features or lift rate limits:

VariableServiceWhat it unlocks
QRZ_USERNAME
QRZ_PASSWORD
QRZ.com Callsign lookup — name, QTH, photo for DX spots. Requires a free QRZ account.
VITE_AMBIENT_APP_KEY
VITE_AMBIENT_API_KEY
Ambient Weather Personal weather station data in the Weather panel.
WSJTX_RELAY_KEY Self-generated Authentication for the WSJT-X relay agent (cloud installs only). Any random string.
ℹ️
Weather data needs no key

Current conditions and forecasts come from Open-Meteo, which is completely free and requires no registration. The browser fetches this data directly, bypassing the server entirely to avoid rate-limit issues.

DX Cluster Source

.env
# auto | proxy | hamqth | dxspider
DX_CLUSTER_SOURCE=auto

# How long to keep spots in the panel (minutes)
SPOT_RETENTION_MINUTES=30

# If using a self-hosted DX Spider proxy:
# DXSPIDER_PROXY_URL=http://your-proxy:3002
auto Tries each source in priority order. Best choice for most users.
proxy Uses the bundled DX Spider telnet-to-HTTP proxy (most real-time, requires network access to a cluster node).
hamqth HamQTH cluster API — no telnet required, slightly higher latency (~30s).
dxspider Direct connection to a specific DX Spider node via the proxy URL.

WSJT-X UDP Integration

OpenHamClock listens for WSJT-X UDP packets on port 2237 by default — the same port WSJT-X uses for its "UDP Server" feature. No relay agent is needed if WSJT-X and OpenHamClock run on the same machine or LAN.

.env
# Enable WSJT-X integration
WSJTX_ENABLED=true

# UDP port to listen on (must match WSJT-X Settings → Reporting → UDP Server Port)
WSJTX_UDP_PORT=2237

# For multicast setups (advanced — leave blank for most installs)
# WSJTX_MULTICAST_ADDRESS=224.0.0.1

# For cloud installs only — authentication key for the relay agent
# WSJTX_RELAY_KEY=your-relay-secret

For the full WSJT-X setup walkthrough, including the relay agent for cloud installs, see WSJT-X / FT8.

Settings Sync

.env
# Save all display settings on the server instead of in each browser
# Enable for single-operator self-hosted / Pi installs
SETTINGS_SYNC=false

# Optional: custom path for the settings file
# SETTINGS_FILE=/path/to/settings.json

With SETTINGS_SYNC=true, your layout, visible panels, map layers, theme, and all filters are stored in data/settings.json on the server. Any browser that connects picks up the same configuration automatically — useful when you access your Pi from multiple devices.

Display Defaults

These set the initial values loaded by new browsers. Users can always override them in the Settings panel.

.env
# Theme: dark | light | legacy | retro | custom
THEME=dark

# Layout: modern | classic | dockable | tablet | compact | emcomm
LAYOUT=modern

# Units: imperial | metric
DISTUNITS=imperial
TEMPUNITS=imperial

# Clock format: 12 | 24
TIME_FORMAT=24

Auto-Update

.env
# Check for updates automatically (requires git clone install)
AUTO_UPDATE_ENABLED=false

# How often to check (minutes)
AUTO_UPDATE_INTERVAL_MINUTES=60

# Run a check shortly after startup
AUTO_UPDATE_ON_START=false

# Exit after updating so systemd/pm2 restarts the service
AUTO_UPDATE_EXIT_AFTER=true

Annotated .env Example

A typical configuration for a Raspberry Pi shack display with WSJT-X and rig control:

.env — Pi shack example
# ── Station ─────────────────────────────
CALLSIGN=K0CJH
LOCATOR=DM79ol

# ── Server ──────────────────────────────
PORT=3000
HOST=0.0.0.0          # accessible from tablets on the LAN

# ── Display ─────────────────────────────
THEME=dark
LAYOUT=modern
DISTUNITS=imperial
TEMPUNITS=imperial
TIME_FORMAT=24

# ── Settings sync (single-operator Pi) ──
SETTINGS_SYNC=true

# ── WSJT-X ──────────────────────────────
WSJTX_ENABLED=true
WSJTX_UDP_PORT=2237

# ── QRZ callsign lookups ─────────────────
QRZ_USERNAME=K0CJH
QRZ_PASSWORD=my-qrz-password

# ── Auto-update ──────────────────────────
AUTO_UPDATE_ENABLED=true
AUTO_UPDATE_INTERVAL_MINUTES=60
AUTO_UPDATE_EXIT_AFTER=true
ℹ️
Full variable reference

The complete list of every .env variable with defaults and valid values is in the Reference section.