Podman¶
On a Linux host that runs containers under systemd without Docker, a
Quadlet unit makes the agent a normal service: systemctl start, the
journal, restart on failure, boot start. The unit and its environment file
live in packaging/podman/ of the repository. This page is the deployment
runbook; the full description of the unit, rootless mode and the SELinux
notes is Running the agent with Podman.
Install¶
The unit monitors the host (host scope), which makes it a rootful setup, and it needs Podman 4.6 or later.
# The unit and its environment file, from the release that carries them.
TAG=<tag that carries packaging/podman>
BASE=https://raw.githubusercontent.com/senhub-io/senhub-agent/$TAG/packaging/podman
curl -fsSLO "$BASE/senhub-agent.container"
curl -fsSLO "$BASE/senhub-agent.env"
curl -fsSLO "$BASE/check-quadlet.sh"
# The bearer token, as a Podman secret read from standard input.
printf '%s' "$OTLP_TOKEN" | sudo podman secret create senhub-otlp-token -
# Your settings, in the environment file (one KEY=value per line, no quotes).
cat >> senhub-agent.env <<'EOF'
SENHUB_OTLP_ENDPOINT=collector.example.com:4317
SENHUB_TAGS=site=paris,env=prod
EOF
sudo install -m 0644 senhub-agent.container senhub-agent.env /etc/containers/systemd/
sudo systemctl daemon-reload
sudo systemctl start senhub-agent
A Quadlet unit has no enable step: its [Install] section starts it at
boot once the file is in place.
SENHUB_OTLP_ENDPOINT points the export at your own collector. The
unit's Secret=senhub-otlp-token line supplies the bearer token sent to it;
delete that line, and the OTLP_BEARER_TOKEN_FILE line under it, if the
collector takes none.
To watch the container instead of the host (also the setup for a rootless
user), delete the lines between # >>> host scope and # <<< host scope
in the unit; see Container scope.
A probe from the environment¶
A probe can be declared by variables, one per parameter, in the same environment file. A password is read from a Podman secret mounted as a file:
printf '%s' "$PG_PASSWORD" | sudo podman secret create senhub-pg-password -
cat >> senhub-agent.env <<'EOF'
SENHUB_PROBE_ORDERSDB_TYPE=postgresql
SENHUB_PROBE_ORDERSDB_HOST=db.example.com
SENHUB_PROBE_ORDERSDB_USERNAME=monitor
SENHUB_PROBE_ORDERSDB_PASSWORD_FILE=/run/secrets/senhub-pg-password
EOF
and, in the [Container] section of the unit, next to the token's line:
The environment file is read when the configuration volume holds no
agent.yaml, that is at the first start. To rebuild the configuration
from a changed file, stop the unit and remove the configuration volume (the
identity lives in the other volume and survives):
sudo systemctl stop senhub-agent
sudo podman volume rm senhub-agent-config
sudo systemctl start senhub-agent
The SENHUB_PROBE_<NAME>_* variables are the exception: the agent itself
reads them at every start, so a change to them needs only a restart. See
Probes from environment variables.
Pinning¶
The Image= line is the pin:
Only exact version tags are published, there is no latest. The shipped
unit carries AutoUpdate=registry, which pulls the same tag again and
restarts the unit only if the registry serves a different image under it;
it never moves to a new version. To pin what is run to a build you have
reviewed, use the digest and delete the AutoUpdate= line:
sudo podman pull ghcr.io/senhub-io/senhub-agent:0.6.3
sudo podman image inspect ghcr.io/senhub-io/senhub-agent:0.6.3 --format '{{.Digest}}'
The -oss image carries only the free probes.
Licence¶
The free tier needs none. For the paid probes, create a Podman secret and uncomment the two lines the unit ships for it:
Secret=senhub-license,type=mount,target=/run/secrets/senhub-license,mode=0400,uid=10001
Environment=SENHUB_LICENSE_FILE=/run/secrets/senhub-license
then sudo systemctl daemon-reload && sudo systemctl restart senhub-agent.
The agent keeps the licence in the configuration volume. To replace it
later, activate the new one inside the running container, then restart:
printf '%s' "$NEW_LICENSE" | sudo podman exec -i senhub-agent \
senhub-agent license activate --config-path /etc/senhub-agent/agent.yaml -
sudo systemctl restart senhub-agent
Secrets¶
Secrets are mounted as files, never given as environment values:
podman inspect prints every environment variable in clear, and that
includes a secret given with type=env. The mounted file is owned by the
agent's uid 10001 and readable by it alone, and podman inspect shows the
mount, not its content.
Rotating one is replacing the secret and restarting the unit:
sudo podman secret rm senhub-otlp-token
printf '%s' "$NEW_TOKEN" | sudo podman secret create senhub-otlp-token -
sudo systemctl restart senhub-agent
Validation¶
Two checks before the unit runs, one after.
sh check-quadlet.sh # from packaging/podman: asks the Quadlet generator, installs nothing
sudo /usr/lib/systemd/system-generators/podman-system-generator --dryrun
check-quadlet.sh hands the unit to the generator in dry-run mode, rootful
and rootless, as shipped and with every optional line enabled, and fails on
any key or value the installed Podman does not accept. If systemctl start
later answers that the unit does not exist, the generator refused the file,
and the second command says why.
After the start, the entrypoint has checked the configuration it wrote and stops the container on an error, with the reason in the journal. By hand:
Exit 0 is clean, 1 warnings only, 2 an error.
Upgrade¶
Edit the Image= line, then:
The first start of a new image pulls it, and the unit allows it fifteen minutes. Both volumes survive, so the agent keeps its identity and its configuration. The agent's own auto-update is off in a container.
Removal¶
sudo systemctl stop senhub-agent
sudo rm /etc/containers/systemd/senhub-agent.container /etc/containers/systemd/senhub-agent.env
sudo systemctl daemon-reload
sudo podman secret rm senhub-otlp-token senhub-pg-password senhub-license
sudo podman volume rm senhub-agent-config senhub-agent-state # the identity goes with the state
Leave the volumes if the host may come back: the agent then returns as the
same agent. The two secrets that were never created make secret rm print
an error you can ignore.
Verify¶
sudo systemctl is-active senhub-agent
sudo podman ps --filter name=senhub-agent --format '{{.Image}} {{.Status}}'
sudo podman healthcheck run senhub-agent && echo healthy
sudo journalctl -u senhub-agent -n 20 --no-pager | grep -E "configuration|checked"
sudo podman exec senhub-agent senhub-agent --version
sudo podman exec senhub-agent senhub-agent config check; echo "exit $?"
sudo podman exec senhub-agent senhub-agent license show
curl -fsS http://127.0.0.1:8080/health
sudo podman inspect senhub-agent --format '{{range .Config.Env}}{{println .}}{{end}}' | grep -E 'TOKEN|PASSWORD|LICENSE'
sudo podman exec senhub-agent senhub-agent key show
sudo systemctl restart senhub-agent && sleep 20 && sudo podman exec senhub-agent senhub-agent key show
Expected: the unit active, the pinned image, healthy, the checked
line, config check exit 0, the licence tier you provisioned, /health
answering, only paths under /run/secrets in the last environment filter
(never a value), and the same agent key before and after a restart.