Install the collector from the Debian package
This guide puts one tally-openstack-collector on an OpenStack control node as a service systemd starts, restarts and logs. At the end the collector runs as the unprivileged tally user, reads its credentials from files only that user may read, and buffers into a directory that survives an upgrade. Which notifications it then consumes, and what the cloud has to publish for it, is connect the collector to an OpenStack cloud.
Before you start
- A Debian 12 or 13, or Ubuntu 24.04 or 26.04, host on
amd64, withsudo. - The package file, the
SHA256SUMSbeside it and the attestation bundleattestation.sigstore.json, all three from the releases page. Copy them to the host, or to the machine you check them on. - The GitHub CLI wherever you check them, for
gh attestation verify. - For a version that has no release yet, a build from a checkout with
make deb, which writesdist/tally-openstack-collector_<version>_amd64.deb. The build needs Go and nothing else and runs on macOS as well as on Linux, but it produces no checksum file and no attestation, so the next section does not apply to it. - The broker's AMQP URL, the cloud name and the base URL of the Reporting API. The account the AMQP URL names needs the permissions of create the broker account.
- The ingest credential this cloud reports under. Issue and revoke credentials has those steps.
- The collector settings page, which lists every variable with its default.
Verify the download
Check the package against the checksum file.
--ignore-missingis what lets you check the package alone against aSHA256SUMSthat also lists the SBOM:shsha256sum -c --ignore-missing SHA256SUMStexttally-openstack-collector_<version>_amd64.deb: OKCheck that the file came out of this repository's release workflow. That run signs what it publishes with a short-lived Sigstore certificate and stores the attestation on GitHub, so there is no key to fetch and none to import:
shgh attestation verify tally-openstack-collector_<version>_amd64.deb --repo B42Labs/tallytext✓ Verification succeeded!The lines above that one name the digest that was read and how many attestations were loaded.
On a host with no route to the attestations API, check against the bundle you downloaded beside the package instead:
shgh attestation verify tally-openstack-collector_<version>_amd64.deb \ --repo B42Labs/tally --bundle attestation.sigstore.jsonA file that did not come out of a release of this repository fails both forms, and a file that fails here is one you do not install.
Install the package
Install the file. The leading
./is what tellsaptthis is a path and not a package name:shsudo apt install ./tally-openstack-collector_<version>_amd64.debtextSetting up tally-openstack-collector (<version>) ...Read back what it installed. The
tallyuser and group are created by the package, and the state directory belongs to them:shdpkg-query -W -f='${Status}\n' tally-openstack-collector getent passwd tally stat -c '%n %U:%G %a' /var/lib/tally/collector /etc/tally/ingest-tokentextinstall ok installed tally:x:998:998::/var/lib/tally/collector:/usr/sbin/nologin /var/lib/tally/collector tally:tally 750 /etc/tally/ingest-token root:tally 640The service is installed stopped and disabled. It carries no credentials and no cloud yet, and the collector refuses that configuration before it listens, so nothing is started until you have configured it.
Put the credentials in place
Write the broker URL and the ingest token into the two files the package shipped empty. Writing into the existing file keeps its
0640 root:tallymode, which a new file created by a redirect would not have:shprintf '%s' 'amqp://tally:<password>@rabbitmq.example:5672/' | sudo tee /etc/tally/amqp-url > /dev/null printf '%s' 'tly_i_<token>' | sudo tee /etc/tally/ingest-token > /dev/nullCheck that both kept their mode and are no longer empty. One trailing newline is trimmed when the file is read, so an
echohere would have been fine too; an empty file is refused:shstat -c '%n %U:%G %a' /etc/tally/amqp-url /etc/tally/ingest-token test -s /etc/tally/amqp-url && test -s /etc/tally/ingest-token && echo filledtext/etc/tally/amqp-url root:tally 640 /etc/tally/ingest-token root:tally 640 filled
Configure the collector
Open
/etc/default/tally-openstack-collectorand fill the two empty settings. Every other variable is in that file, commented out with its default beside it:shsudoedit /etc/default/tally-openstack-collectorshTALLY_OSC_CLOUD=os-prod-eu1 TALLY_OSC_REPORTING_URL=https://tally-reporting.internalWhere the cloud runs octavia, or renamed an exchange, uncomment the exchanges line and list its own:
shTALLY_OSC_EXCHANGES=nova,neutron,openstack,glance,octaviaLeave
TALLY_OSC_BUFFER_PATHas it is. It points into/var/lib/tally/collector, the directory the package owns and the unit recreates; anywhere else the service may not write, because the unit runs underProtectSystem=strict.
Start the service
Enable the unit and start it:
shsudo systemctl enable --now tally-openstack-collectorCheck that it came up and read its first lines. The collector logs JSON to the journal:
shsystemctl is-active tally-openstack-collector journalctl -u tally-openstack-collector -n 2 -o cattextactive {"time":"2026-07-09T14:22:00.512Z","level":"INFO","msg":"listening","service":"tally-openstack-collector","port":8080} {"time":"2026-07-09T14:22:00.731Z","level":"INFO","msg":"the AMQP session is established, consuming","service":"tally-openstack-collector","queue":"tally-notifications","exchanges":["nova","neutron","openstack","glance"],"topics":["notifications.info"]}A second line that reads
the AMQP session ended, reconnectingcarries the broker's refusal inerror.A start that ends in
failednames the value to fix. An empty credential file reportsTALLY_OSC_AMQP_URL_FILE: file /etc/tally/amqp-url is empty, an unset cloud reportsTALLY_OSC_CLOUD: must be set, and a token set in the environment file beside its_FILEcompanion reportsset TALLY_OSC_TOKEN or TALLY_OSC_TOKEN_FILE, not both.
Upgrade, remove and purge
Coming from v0.2.0 with the exchanges line still commented out, check whether cinder sets
control_exchange = cinderbefore you upgrade. The default ofTALLY_OSC_EXCHANGESlistedcinderthen and listsopenstackin its place now, and where cinder publishes oncinderthe upgrade does not show what it drops: the queue keeps the binding the older version made, so volume notifications keep arriving, and they stop once the queue is recreated. The broker lists that binding:shrabbitmqctl list_bindings source_name destination_name | grep -E '^cinder[[:space:]]+tally-notifications'textcinder tally-notificationsWhere cinder sets the option, uncomment the exchanges line and list
cinderinstead ofopenstack:shTALLY_OSC_EXCHANGES=nova,neutron,glance,cinderComing from v0.2.0, set the queue type before you upgrade, whichever queue the collector ends on. v0.2.0 declared
tally-notificationswithout a type, and the newer version declares a quorum queue. The broker refuses that declare over the existing queue, so the upgraded collector logsthe queue exists with other arguments than TALLY_OSC_QUEUE_TYPE=quorum declaresand consumes nothing, while the queue keeps filling. The package does not restart the service, but Ubuntu's needrestart does at the end of theaptrun, and so does a crash or a reboot. Add this line to/etc/default/tally-openstack-collectorbefore the upgrade. v0.2.0 ignores a variable it does not know, and the file is a conffile, so the line is kept:shTALLY_OSC_QUEUE_TYPE=classicThe line keeps the queue. To get the quorum queue, move the queue in step 3, after the install. A broker older than RabbitMQ 4.0 keeps the line.
Upgrade by installing the newer file. Your edits to
/etc/default/tally-openstack-collectorand to the two credential files are kept, because all three are conffiles; a changed default arrives beside them as.dpkg-distfor you to compare. The outbox is untouched, so events that have not reached the Reporting API are delivered after the restart:shsudo apt install ./tally-openstack-collector_<newer-version>_amd64.debThe package neither stops nor restarts the service. Restart it and read the journal. Coming from v0.2.0 without the
classicline of step 2, the journal carries the queue error of that step; add the line and restart again:shsudo systemctl restart tally-openstack-collector journalctl -u tally-openstack-collector -n 5 -o catTo get the quorum queue, follow move the queue to another type. The upgraded collector with the
classicline is the one that fits the existing queue. Wait until the message count of that section's step 1 is 0 with it running, then stop the service, delete the queue, take the line out again and start the service:shsudo systemctl stop tally-openstack-collector rabbitmqctl delete_queue tally-notifications sudo sed -i '/^[[:space:]]*TALLY_OSC_QUEUE_TYPE[[:space:]]*=/d' /etc/default/tally-openstack-collector sudo systemctl start tally-openstack-collectorRead the type of the queue the collector declared:
shrabbitmqctl list_queues name type | grep -E '^tally-notifications[[:space:]]'texttally-notifications quorumA
classicthere means the queue was not deleted, or the collector was started withTALLY_OSC_QUEUE_TYPE=classicstill set.Remove the package to stop and disable the service while keeping its configuration and its outbox:
shsudo apt remove tally-openstack-collectorPurge it to drop the configuration and the credentials as well. The outbox is deliberately kept: between the acknowledgement on the bus and the delivery, an event lives in that file and nowhere else, so no purge destroys usage that exists in no other copy. Delete it by hand once you know it is empty:
shsudo apt purge tally-openstack-collectortexttally-openstack-collector: /var/lib/tally/collector/outbox.db was kept. It may still hold events that never reached the Reporting API. Delete it by hand once you know it is empty or no longer needed.The
tallyuser and group stay as well: an orphaned system account is harmless, and a later Tally package on this host uses the same one.
Check the result
Ask the running service for readiness. It answers 200 while the consumer holds the broker connection and the outbox answers:
shcurl -sS -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8080/readyztext200Read the two counters twice, a minute apart, while the cloud is in use. Both rise:
shcurl -sS http://127.0.0.1:8080/metrics | grep -E '^tally_collector_(consumed|delivered)_total'texttally_collector_consumed_total{cloud="os-prod-eu1",event_type="compute.instance.create.end",platform="openstack"} 14 tally_collector_delivered_total{cloud="os-prod-eu1",platform="openstack"} 12Confirm the service comes back on its own. Kill it and read the state again a few seconds later;
Restart=on-failurebrings it back, and the outbox it reopens is the one it was writing:shsudo systemctl kill -s KILL tally-openstack-collector sleep 10 systemctl is-active tally-openstack-collectortextactiveRead the last summary line. The collector logs one every
TALLY_OSC_SUMMARY_INTERVAL_Sseconds, 60 by default, with what it consumed and delivered since the previous one; the log lines section states every attribute:shjournalctl -u tally-openstack-collector -o cat | grep '"msg":"summary"' | tail -1json{"time":"2026-07-09T14:23:00.514Z","level":"INFO","msg":"summary","service":"tally-openstack-collector","interval_seconds":60,"connected":true,"consumed":14,"skipped":37,"unparseable":0,"delivered":12,"delivery_errors":0,"buffered":2,"oldest_buffered_seconds":3}