Edge Agent Installation
Prerequisites
Before you install, make sure the following are in place on the machine that will run the agent.
| Requirement | Details |
|---|---|
| Operating system | Linux (Ubuntu 20.04+, Debian 11+, or RHEL 8+). Windows via WSL2 works but is not recommended for production. |
| Python | 3.12 or later. Earlier versions are not supported. |
| Package manager | uv, a fast Python package manager used to create the agent's isolated environment. |
| Outbound network | The machine must be able to reach api.haltless.io on port 443 (HTTPS). No inbound ports are required. |
| Disk | ~200 MB for the agent and its dependencies, plus space for the local buffer. Budget generously if you expect long offline periods. |
| Memory | 64 MB is enough for most deployments. |
You will also need two things from Haltless before the agent can send data:
- A System for every connection the agent will read: each PLC, OPC-UA server, MQTT broker, or export folder. Create it on the portal's Systems page, or register a machine on its own, which creates a System of the same identifier. The
system_identifierof each source in your config must match its System exactly (it is case-sensitive). See Systems. - An API key for your account, with the default
ingestandagentscopes. See API keys to create one. The key is shown only once, so store it securely.
If a device is already reachable over the internet and you'd prefer not to run local software, you can skip the agent entirely and push readings straight to the ingestion API. See Direct ingestion.
Install with Docker (recommended)
On a host that runs Docker (Docker Engine 20.10 or later, with docker compose), the published image is the simplest way to run the agent. It carries every protocol driver, starts again by itself after a crash or a reboot, and updates with one command, or by itself in a maintenance window. It needs no Python on the host; the API key and the Systems from the prerequisites above still apply.
The image, ghcr.io/haltless/haltless-edge, brings the files it is run with. Write them into the folder the agent will live in:
sudo mkdir -p /opt/haltless-agent && sudo chown "$USER" /opt/haltless-agent
cd /opt/haltless-agent
docker run --rm --user "$(id -u):$(id -g)" -v "$PWD:/kit" \
ghcr.io/haltless/haltless-edge:latest python -m src.kit /kit
| File | What it holds |
|---|---|
.env | Your API key, the agent version this host runs, and how it takes updates. Only its owner can read it. |
config.yaml | cloud_api_url and your sources, as on the Configuration page. Its api_key is read from .env. |
docker-compose.yml | How the agent's container runs. Replaced by each update: do not edit it. |
haltless-agent | Starts, stops and updates the agent. |
Put your API key in .env (HALTLESS_API_KEY=...) and your sources in config.yaml, then start the agent and check on it:
./haltless-agent start
./haltless-agent status
./haltless-agent logs
The account running these must be able to use Docker (be in the docker group). If the agent's container cannot reach your controllers on the plant network, run it on the host's network: create docker-compose.override.yml beside the other files, which updates never touch, with
services:
agent:
network_mode: host
and run ./haltless-agent start again. Then verify it's connected.
The steps below install the agent from source instead, on a host without Docker.
Step 1: Install Python and uv
Install Python 3.12 (Debian/Ubuntu shown; use your distribution's package manager otherwise):
sudo apt-get update
sudo apt-get install -y python3.12 python3.12-venv
python3.12 --version # expect: Python 3.12.x
Install uv and make sure it is on your PATH:
curl -LsSf https://astral.sh/uv/install.sh | sh
uv --version
Step 2: Place the agent files
Copy the edge-agent directory provided by Haltless to a stable location the service will own, for example /opt/haltless-agent. The directory contains the agent source, a pyproject.toml, and a config.yaml.example you will use as a starting point.
Step 3: Install dependencies
The core dependencies are always installed. They cover the CSV, JSON, Mitsubishi MELSEC, Omron FINS, and AVEVA PI Web API collectors, which need nothing more. The Modbus (TCP and RTU), OPC-UA, Siemens S7, EtherNet/IP, MQTT (and Sparkplug B), Beckhoff ADS, and BACnet/IP collectors are optional extras: install only what you need. The Docker image built from the agent's Dockerfile includes all of them.
cd /opt/haltless-agent
# Core only (CSV, JSON, MELSEC, FINS, PI Web API)
uv sync
# Add Modbus TCP and RTU support
uv sync --extra modbus
# Add OPC-UA support
uv sync --extra opcua
# Add Siemens S7 support
uv sync --extra s7
# Add EtherNet/IP support (Rockwell / Allen-Bradley)
uv sync --extra ethernetip
# Add MQTT and Sparkplug B support (subscribe to a broker)
uv sync --extra mqtt
# Add Beckhoff ADS support (TwinCAT)
uv sync --extra ads
# Add BACnet/IP support (building controllers)
uv sync --extra bacnet
# Everything
uv sync --extra modbus --extra opcua --extra s7 --extra ethernetip --extra mqtt --extra ads --extra bacnet
If a source names a protocol whose extra is not installed, the agent refuses to start and says which extra to add.
uv sync creates an isolated virtual environment in .venv/ inside the project directory. No system-wide packages are changed.
Step 4: Create the configuration file
Copy the example and edit it for your environment:
cp config.yaml.example config.yaml
At minimum, set cloud_api_url, api_key, and one or more sources. The Configuration page documents every key, and Protocols covers the per-source settings.
Lock down the file so only the agent's user can read it (it contains your API key):
chmod 600 config.yaml
Step 5: Run it once by hand
Before installing it as a service, run the agent in the foreground to confirm it loads its config, authenticates, and starts collecting:
uv run python -m src.main
By default the agent reads config.yaml from the working directory. You can also pass a path explicitly:
uv run python -m src.main /opt/haltless-agent/config.yaml
On a healthy start you'll see the agent initialize each collector, confirm the connection to Haltless, report a heartbeat status of active, and begin sending batches. Press Ctrl+C to stop.
Step 6: Run as a service (systemd)
For production, run the agent under a process supervisor so it starts on boot and restarts on failure. A minimal systemd unit:
[Unit]
Description=Haltless Edge Agent
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=haltless
Group=haltless
WorkingDirectory=/opt/haltless-agent
ExecStart=/opt/haltless-agent/.venv/bin/python -m src.main
Restart=always
RestartSec=10
NoNewPrivileges=true
PrivateTmp=true
[Install]
WantedBy=multi-user.target
Running as a dedicated low-privilege user (haltless above) is recommended. Create it, hand it ownership of the directory, then enable the service:
sudo useradd --system --no-create-home --shell /usr/sbin/nologin haltless
sudo chown -R haltless:haltless /opt/haltless-agent
sudo systemctl daemon-reload
sudo systemctl enable haltless-agent
sudo systemctl start haltless-agent
sudo systemctl status haltless-agent
Verify it's connected
Check outbound reachability. From the agent host, confirm it can reach Haltless. A successful (2xx) response means the network path is open:
curl -sS -o /dev/null -w "%{http_code}\n" https://api.haltless.io/health
Watch the agent logs. Under systemd, logs go to the journal:
sudo journalctl -u haltless-agent -f
Look for the heartbeat line reporting status=active and lines confirming batches were sent. The heartbeat is the agent's own health check: when it reports active, the agent has reached Haltless and your company's account is active.
Check the dashboard. Open your System on the Systems page, or your machine in the Haltless dashboard. The agent sends a request once it has collected batch_size readings (100 by default), so new readings appear after that many have been read. With only a few tags this can take a few minutes; lower batch_size to see them sooner. Tags not yet assigned to a machine show up as unassigned on the System's page.
Keeping the agent up to date
Each time the agent checks in, Haltless tells it which version is current. When a newer one is out, you hear about it three ways:
- the Systems page lists every agent with its version and how it is updated, and an admin's notifications say an update is available;
- the agent's own log says so once a day, with what to run;
./haltless-agent statussays so on the agent's host.
With Docker, each host chooses how updates arrive.
By hand. In the agent's folder on its host:
./haltless-agent update
It downloads the new version, restarts the agent on it and waits to see it stay up and reach Haltless. If it does not, it goes back to the version that was running, so a failed update never leaves the agent down. ./haltless-agent update --to 0.6.0 takes a version you name, which is also how to go back.
Automatically, in a maintenance window.
./haltless-agent auto-update on "Sun 02:00" 2h
From then on the agent updates itself each Sunday, at a random moment between 02:00 and 04:00, and rolls back as above if an update fails. The time uses systemd's calendar format, for example "Mon..Fri 03:00" or "*-*-01 02:00" for the first of each month. ./haltless-agent auto-update off goes back to updating by hand. The Systems page shows which hosts update by themselves, and when.
Every published image is signed by Haltless. If cosign is installed on the host, each update is checked against that signature before it runs; add HALTLESS_REQUIRE_SIGNATURE=1 to .env to refuse an update that cannot be checked.
Installed from source? Stop the service, replace the agent's files with the new version's, run uv sync with the extras you use, and start the service again. Or move the agent onto the Docker image, which updates with one command: install it as above, and copy your sources into its config.yaml.
Next steps
- Configuration: every configuration key, with a full example
- Protocols: set up each kind of source
- Troubleshooting: what to do when something doesn't work