All chapters
Deployment
Huntress is one self-contained program. The web server, the database, the listeners, the Pulse app, the video tools and the detection model are all inside the executable, so there is nothing else to install: no database server, no message broker, no runtime. You decide where it runs.
Platforms#
| Platform | Runs as | Typical use |
|---|---|---|
| Linux x64 | A systemd service | Servers, virtual machines, mini PCs |
| Linux arm64 | A systemd service | Raspberry Pi-class computers, mostly as a Huntress Edge |
| Windows x64 | A Windows service | Servers and workstations; PowerShell 7 for the installer |
There is no official container image. The program is self-contained and keeps all state under one directory (HUNTRESS_DATA_DIR), so it can be run in a container if you provide the image and a persistent volume; this is not tested or supported.
Installing#
The installers verify the release before installing anything: the channel manifest carries an ECDSA signature and every archive a SHA-256 hash. They set up the program as a service, the updater as a second service, and a versioned folder layout that makes updates and rollbacks safe.
Linux#
sudo ./install.sh # a server, from the stable channel sudo ./install.sh --role edge # a Huntress Edge sudo ./install.sh --channel beta # another release channel sudo ./install.sh --bundle /path/to/dir # offline: a folder with manifest.json, manifest.json.sig and the archive
It needs systemd, curl (unless offline), openssl, tar, python3 and sha256sum. It creates a huntress user that is a member of the serial-port group so a Zigbee dongle can be used, enables huntress (or huntress-edge) and huntress-updater, and waits for the health check. An older flat install is migrated.
Windows#
./Install-Huntress.ps1 # a server ./Install-Huntress.ps1 -Role edge # a Huntress Edge ./Install-Huntress.ps1 -Bundle D:\huntress-1.4.0 # offline
Run it in PowerShell 7 as administrator. The services are Huntress (or HuntressEdge) and HuntressUpdater. Releases are not yet code-signed for Windows, so SmartScreen may warn the first time.
Folders#
| Item | Linux | Windows |
|---|---|---|
| Releases | /opt/huntress/releases/<version>/ | C:\Program Files\Huntress\releases\<version>\ |
| Active release | /opt/huntress/current (a link) | C:\Program Files\Huntress\current (a junction) |
| Configuration | /etc/huntress/ | C:\ProgramData\Huntress\config\ |
| Data | /var/lib/huntress/ | C:\ProgramData\Huntress\data\ |
Updates replace only releases and the link. Your configuration and data are never touched. HUNTRESS_CONFIG_DIR and HUNTRESS_DATA_DIR move them elsewhere.
First start#
- Copy your public address into
AppBaseUrl(for examplehttps://huntress.example.com). Password-reset links, Pulse links and passkeys are built from it, never from the request. - Set
Registration:InvitationKeyto a long random value. Registration is closed while it is empty. - Put a TLS reverse proxy in front (next section).
- Open the site, register, and sign in. The first organisation is the system organisation (
System:AdminTenantId, 1 by default): its Owners and Admins approve updates. - Configure outbound mail (
Notifications:Smtp) if you want rule e-mails and password resets.
Put these in appsettings.Production.json in the configuration folder, or in environment variables with __ for each level (Registration__InvitationKey). Do not put secrets in the shipped appsettings.json. See Configuration.
Network and ports#
| Port | What | Notes |
|---|---|---|
| 5000 | Web interface, Hunt API, edge links | Listens on 127.0.0.1 in the shipped service, for the reverse proxy. |
| 443 | The same, through your reverse proxy | TLS ends here. |
| 1883 | MQTT broker | Plain TCP. HuntressMqtt:Enabled. |
| 25 | SMTP receiver | Plain TCP. Needs CAP_NET_BIND_SERVICE on Linux (the unit grants it). HuntressSmtp:Enabled. |
| 2000 | SIA DC-09 | Plain TCP. HuntressTcp:Enabled, HuntressTcp:SiaPort. |
| 2001 | Contact ID | Plain TCP. HuntressTcp:CidPort. |
A listener that cannot bind its port (in use, or no permission for port 25) is reported on /health and does not stop the server.
Reverse proxy and TLS#
Put a proxy that terminates TLS in front of the web port. It is not optional in practice: the sign-in cookie is Secure, API keys require HTTPS, and browsers allow passkeys, installing Pulse, location and service workers only on HTTPS.
- A ready Caddy configuration is in
deploy/Caddyfile. It terminates TLS with automatic certificates and holds requests for up to 30 seconds while Huntress restarts, so an update is a short pause instead of an error page. - Other proxies must pass WebSocket upgrades and allow connections that stay open for hours: the interface, edge links and live video all use them.
- Set
AppBaseUrlto the public address andAllowedHoststo its host name. - The proxy on the same machine is trusted to set
X-Forwarded-Forand-Proto. A proxy on another machine must be listed inForwardedHeaders:KnownProxiesorKnownNetworks; headers from anyone else are ignored, so clients cannot spoof their address. Real client addresses matter for logs and rate limits.
Huntress Edge#
A Huntress Edge is the same program, started with --edge on a small computer at a remote site. It makes only outbound connections to your server, over WebSocket (wss when the server address is https), so nothing needs opening on the site's firewall. It is how Huntress reaches cameras, Zigbee dongles and devices on a private network.
| Job | How it works |
|---|---|
| Cameras | Pulls each camera's RTSP stream locally and sends the server the live video and snapshots. The server tells the edge each camera's address and settings after it connects. |
| Object detection and clips | Analyses snapshots on the edge when the server asks and the edge can, and keeps the clip ring locally, uploading only the clip. See Object detection. |
| Zigbee | Runs a coordinator dongle at the site and relays it to the server. |
| HTTP relay | Makes the web calls of HTTP outputs on the site's own network for the server. |
| Updates | Follows its own server, never ahead of it; by default it updates itself between 02:00 and 05:00 and waits while someone is watching a camera. |
Setting up an edge#
- On the server, create the camera (a Live Camera sensor with its RTSP URL) and generate its edge secret on its settings page. For Zigbee or an HTTP relay, create the coordinator or relay on the Devices page.
- On the edge computer, install with the edge role (see above).
- Write the connection in
appsettings.Production.jsonin the edge's configuration folder (below) and restarthuntress-edge. - Open the camera in the interface: it should go from Connecting to Live.
The edge's configuration; keep only the sections you need
{
"HuntressEdge": {
"HuntressUrl": "https://huntress.example.com",
"Cameras": [
{ "Token": "<camera sensor token>", "Secret": "<edge secret>" }
],
"ZigbeeGateways": [
{ "Token": "<coordinator token>", "Secret": "<edge secret>", "SerialPort": "/dev/ttyUSB0" }
],
"HttpRelays": [
{ "Token": "<relay token>", "Secret": "<edge secret>", "AllowedHosts": [ "192.168.1.50" ] }
]
}
}- Each camera is served by one edge at a time; a second connection for the same camera is refused. The secret is sent as
X-Edge-Secretand checked in constant time before the connection opens. - Limits.
HuntressEdge:Snapshotsshapes the pictures (MaxWidth1280,JpegQuality4);HuntressEdge:Clipscaps the memory of clip rings (MaxRingMegabytes24,MaxRingSeconds60,MaxConcurrentUploads2). - When the link drops the edge reconnects by itself, waiting 2 seconds, then longer, up to 60. It does not queue events: live video is dropped while it is disconnected, only the newest snapshot is kept, and Zigbee reports during an outage are lost. The radio and the cameras keep running.
- Health. On the edge,
http://127.0.0.1:5080/edge-healthis Unhealthy when no camera has been connected for five minutes, and/edge-statusshows version, viewers and the detector. - An edge that serves only Zigbee or HTTP relays and no camera cannot yet update itself through its server; update it with the installer.
One server, many sites#
The usual shape for a service provider or a multi-site operator is one central server and an edge at each site that has cameras or devices behind NAT. Set LocalEdge:Enabled=false on a server in a data centre so it never opens RTSP streams itself; a camera with an edge secret is always left to its edge anyway. Each site's people and operators use the one server's web address. There is no replication between servers.
Running without internet#
The server and its interface run entirely without internet access: every script, style and font is bundled, and nothing phones home. Four things are worth planning for:
- Maps draw their base layer from OpenStreetMap, loaded by the viewer's browser. Offline the base map is blank; markers and zones still draw. Camera-image regions need no map.
- Updates poll a release channel on GitHub. Set
Updates:Mode=Off, pointUpdates:ManifestUrlat an internal location, or install from an offline bundle: putmanifest.json,manifest.json.sigand the archive in<DataDir>/update/inbox/. The signature is still verified. - E-mail needs an SMTP server you can reach, and password resets and rule e-mails need it.
- Clocks must agree. Encrypted DC-09 frames must be stamped within seconds of the server's time, schedules and rules use it, and an edge refuses requests past their expiry.
Storage planning#
- Pictures. At the default archive interval (every 30 seconds) a camera stores roughly 0.3 GB a day, about 10 GB over the default 30 days of retention. Every second would be about 30 times that.
- Clips. Capped by
Clips:MaxTotalGigabytes(20 by default); no new clip is started belowClips:MinFreeDiskMegabytes(2,048). - Readings and events. Small, kept 90 days by default.
- Detection uses processor time rather than storage; it is capped at half of a processor by default.
Command-line modes#
| Command | What it does |
|---|---|
Huntress | Runs the server. |
Huntress --edge | Runs a Huntress Edge: cameras, Zigbee and HTTP relays, no interface and no database. |
Huntress --updater | Runs the privileged update service. |
Huntress --backup [dir] [--include-blobs] | Writes a consistent copy of the database. See Operations. |
Huntress --preflight | Migrates a copy of the database, health-checks, and exits. The updater uses it before switching versions. |
Huntress --version, --version-json | Prints the version, the schema level and the edge protocol. |