Server Setup
Install
From a pre-built binary
Download the latest release from GitHub Releases:
# Linux amd64
curl -Lo openme https://github.com/openme/openme/releases/latest/download/openme-linux-amd64
chmod +x openme
sudo mv openme /usr/local/bin/Build from source
Requires Go 1.21+.
git clone https://github.com/openme/openme
cd openme/cli
go build -o openme ./cmd/openme
sudo mv openme /usr/local/bin/Initialise the Server
openme init generates a fresh Curve25519 keypair and writes /etc/openme/config.yaml with sensible defaults.
sudo openme init --server myserver.example.comOptions:
| Flag | Default | Description |
|---|---|---|
--server |
(required) | Public hostname or IP — used in generated client configs |
--port |
7777 |
UDP knock port and TCP health port |
--firewall |
nft |
Firewall backend: nft or iptables |
--force |
false |
Overwrite an existing config |
The command prints the server’s Curve25519 public key — you’ll need this when provisioning clients, but openme add handles it automatically.
Open the Knock Port
The UDP knock port must be reachable (the point is that TCP ports are not). The examples below assume you have already applied a default-deny firewall policy — if not, see Harden the Firewall first.
# nftables
sudo nft add rule inet filter input udp dport 7777 accept
# iptables
sudo iptables -A INPUT -p udp --dport 7777 -j ACCEPTFor IPv6, add an equivalent ip6tables or nft inet rule.
Start the Server
sudo openme serveFor production, install as a systemd service:
# /etc/systemd/system/openme.service
[Unit]
Description=openme SPA server
After=network.target
[Service]
ExecStart=/usr/local/bin/openme serve
Restart=on-failure
AmbientCapabilities=CAP_NET_ADMIN
[Install]
WantedBy=multi-user.targetsudo systemctl enable --now openme
sudo systemctl status openmeHarden the Firewall
The whole point of openme is that no TCP ports are permanently open.
Apply a default-deny policy before opening anything, then only allow the UDP knock port.
nftables
# Flush and rebuild a minimal ruleset
sudo nft flush ruleset
sudo nft add table inet filter
# Base chains
sudo nft add chain inet filter input '{ type filter hook input priority 0; policy drop; }'
sudo nft add chain inet filter forward '{ type filter hook forward priority 0; policy drop; }'
sudo nft add chain inet filter output '{ type filter hook output priority 0; policy accept; }'
# Allow established / related traffic and loopback
sudo nft add rule inet filter input ct state established,related accept
sudo nft add rule inet filter input iifname "lo" accept
# Allow the UDP knock port (openme)
sudo nft add rule inet filter input udp dport 7777 accept
# Create the openme chain (openme manages rules inside it)
sudo nft add chain inet filter openme
sudo nft add rule inet filter input jump openmePersist the ruleset:
sudo nft list ruleset | sudo tee /etc/nftables.conf
sudo systemctl enable --now nftablesiptables / ip6tables
# ── IPv4 ──────────────────────────────────────────────────────────────────
# Flush existing rules and set default DROP on INPUT and FORWARD
sudo iptables -F
sudo iptables -X
sudo iptables -P INPUT DROP
sudo iptables -P FORWARD DROP
sudo iptables -P OUTPUT ACCEPT
# Allow established / related traffic and loopback
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
sudo iptables -A INPUT -i lo -j ACCEPT
# Allow the UDP knock port (openme)
sudo iptables -A INPUT -p udp --dport 7777 -j ACCEPT
# ── IPv6 ──────────────────────────────────────────────────────────────────
sudo ip6tables -F
sudo ip6tables -X
sudo ip6tables -P INPUT DROP
sudo ip6tables -P FORWARD DROP
sudo ip6tables -P OUTPUT ACCEPT
sudo ip6tables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
sudo ip6tables -A INPUT -i lo -j ACCEPT
sudo ip6tables -A INPUT -p udp --dport 7777 -j ACCEPTPersist across reboots (Debian/Ubuntu):
sudo apt install iptables-persistent
sudo netfilter-persistent saveThese rules drop all incoming TCP connections, including SSH.
Run them from the console or a recovery environment, or add a temporary SSH allowance first:
# Temporary SSH rule — remove it once openme clients are verified
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT
sudo ip6tables -A INPUT -p tcp --dport 22 -j ACCEPTOnce your openme clients are working, remove the temporary SSH rules:
sudo iptables -D INPUT -p tcp --dport 22 -j ACCEPT
sudo ip6tables -D INPUT -p tcp --dport 22 -j ACCEPTFor nftables, if you added a temporary SSH rule:
# Find the rule handle
sudo nft -a list chain inet filter input
# Delete by handle (replace <handle> with the number shown)
sudo nft delete rule inet filter input handle <handle>Verify
Once a client is configured and has knocked, you can verify the full round trip:
# Knock and immediately check the health port (end-to-end test)
openme status --knock
# Check health port only (requires a prior knock within knock_timeout)
openme statusThe health port is never permanently open. It is opened by the firewall only after a valid knock, for the knock_timeout duration (default 30s). A closed health port means either no knock has been sent yet, or the knock_timeout has expired.
Next: Add a client →