Status & Doctor
ironflock status shows what of IronFlock is on a machine and whether it runs. ironflock doctor checks whether a machine can run the device agent and whether it reaches the hosts the agent talks to. Use them before you connect a machine, and when a device does not come online.
Both commands change nothing and ask nothing. They run on Linux and macOS, without root, and read more as root. With --host they run on another machine, also on several at once (Remote Machines over SSH). There they run as root when the login is root or may use sudo without a password and without a terminal; otherwise they run as the login, without asking, and their hint names the command with --host root@machine.
ironflock status
Shows the device agent, the appliance and the operating system of this machine: what is installed, its version, whether its service runs, the device and its project, and the agent’s last self-update. The appliance is the complete IronFlock system on a machine on site (IronFlock Appliance); for an appliance the command also shows the address of its UI, its license, the health of its services and an update that waits (Appliance Commands). On IronFlockOS the command also shows the root slots and what needs attention.
ironflock status [flags]Runs on: Linux; on macOS it shows the system and that nothing is installed. Root: not required; a few facts need it. Asks: nothing.
The command has no flags of its own. It takes the global flags (Global Flags).
Without root, some facts cannot be read: the device and its project, as the agent’s .flock is root’s alone, and on an appliance, whose folder /opt/ironflock is root’s, also the versions, the appliance’s URL, its license and its update, and the agent’s update. The health of the appliance’s services needs root or the group docker. They say unknown (needs root), the JSON field needs_root lists them, and sudo ironflock status reads them.
# What is on this machine, read as your login
ironflock status
# Everything, as JSON
sudo ironflock status --json
# The status of another machine over SSH
ironflock --host pi@device-17 statusText Output
$ ironflock status
Agent: installed in /opt/ironflock/device-agent
Version: 1.6.9
Service: running (ironflock-device-agent.service)
Connection: unknown (this agent does not report it)
Device: unknown (needs root)
Swarm: unknown (needs root)
Update: none pending
Appliance: not installed
System: Debian GNU/Linux 12 (bookworm)
Run 'sudo ironflock status' to read what needs root.Swarm is the device’s project. Connection is unknown for a running agent: the agents released so far do not report their connection on the machine.
On IronFlockOS
The system line shows the image and its two root slots (Root Slots and the Data Partition):
System: IronFlockOS 0.13.0 (amd64-efi, base)
Image: ironflockos-amd64-efi-base (build 20261105T120000Z-g1a2b3c4)
Slot A: inactive, good
Slot B: booted, good, 0.13.0 installed 2026-11-05T14:00:00Z
Next boot: slot BThe agent of an IronFlockOS device is the one the image set up when the device enrolled, in /opt/ironflock/device-agent on /data, and is shown as on any other system. A device that has not enrolled yet has no agent; the image carries one, and the line says how the device enrolls (first line below). When /data could not be mounted at this start, the agent’s folder cannot be seen either, enrolled or not, and the line says so instead (second line):
Agent: not enrolled yet: the device enrolls with a .flock on its IRONFLOCKOS partition at its next start, or now with 'sudo ironflock agent configure -c <file>'
Agent: not to be seen: /data, which holds the agent and its .flock, could not be mounted (below); a .flock on the IRONFLOCKOS partition enrolls the device at its next start for as long as /data is missingBelow the system, one line each appears only when IronFlockOS has something to say:
| Line | Shown when |
|---|---|
Last reset: | the last factory reset failed or gave up. os reset says what a record means |
Reset hold: | a reset is open (wait: a .flock or proxy.conf on the IRONFLOCKOS partition is not taken while the old owner’s data may be on /data; copy: the reset has begun to erase), or it gave up and could not remove the .flock and proxy.conf that came with it, which stay unused (gave-up) |
/data: | /data could not be mounted: the device runs on the root slot’s own folders, without Docker and the apps |
/apps: | /apps is not the data partition’s folder: the apps run, but lose their data at the next OS update |
Last reset: gave up after 3 attempts at 2026-11-11T09:00:00Z: the data partition is not the partition named data on the disk of the root slot; /data holds the old data; left of the old owner: the data partition, the booted root slot, the other root slot
Reset hold: wait: a reset is open; a .flock or proxy.conf on the IRONFLOCKOS partition is not taken while the old owner's data may be on /data
/data: NOT MOUNTED: the device runs on the root slot's own folders; Docker and the apps do not run, and what is written now is lost with the next update
/apps: not the data partition's folder: the apps run, but lose their data at the next OS updateA failed attempt of a reset reads attempt 1 of 3 failed at <time>: <reason>; /data holds the old data; the next start tries again while the hold is wait or copy, and no request is open now without a hold. Without root, the reset record shown is its copy in the running slot, as the IRONFLOCKOS partition is root’s.
The Last os install
On any Linux with EFI variables, a line OS install: shows what the last ironflock os install recorded in IronFlock’s EFI variable IronFlockInstall, when it is there. Every user may read the variable. Where it cannot be read, or the machine has no EFI variables, there is no line and no warning; a value that does not parse is a warning.
System: Debian GNU/Linux 13 (trixie)
OS install: 'ironflock os install' stopped at 2026-11-11T10:00:00Z before it wrote the disk: the disk /dev/sda is not the plan's: its size changed from 64.0 GB to 32.0 GB; nothing was changed, and this system runs as before| Stage | The line says |
|---|---|
stopped | it stopped before it wrote the disk, the reason, and that nothing was changed |
handed-over | on the old system: the installer did not run at the restart, and nothing was changed. On IronFlockOS: it wrote this system but could not record how the install ended |
writing | it began to write the disk and did not record that it finished |
done | on IronFlockOS: this system was installed by os install. Elsewhere: it wrote IronFlockOS onto a disk of the machine, and the running system is another one |
failed | it wrote the disk, and what it wrote did not read back, with the reason |
The record is never cleared. The line stays until the next os install replaces it, and on a converted device it stays as the note that the system was installed this way.
JSON Result
--json writes the same as one JSON object, shown here on several lines; the command writes it on one line.
{
"agent": {
"installed": true,
"layout": "current",
"dir": "/opt/ironflock/device-agent",
"version": "1.6.9",
"service": {"unit": "ironflock-device-agent.service", "state": "running"},
"connection": "unknown",
"device": "demo-device",
"swarm": "demo_swarm",
"update": {"state": "none"}
},
"appliance": {"installed": false},
"os": {"name": "Debian GNU/Linux 12 (bookworm)", "id": "debian", "version": "12", "ironflockos": false},
"needs_root": []
}A fact that cannot be read is unknown; when root is not the reason, a warning on stderr says why.
| Field | Values |
|---|---|
agent.installed | true when an agent is installed. The fields from layout to update are there then, and only then. On IronFlockOS, false means that the device has not enrolled yet |
agent.unfinished_install | true: no agent, but an earlier install stopped before it got to the agent; agent install undoes it first. Absent otherwise |
agent.layout | current (/opt/ironflock/device-agent), legacy (/opt/reagent, from an earlier installer), migrating (the agent moves the legacy layout to the current one right now) |
agent.dir | the agent’s folder |
agent.version | what the agent’s binary says with -version |
agent.service | unit and state: running, starting, stopping, stopped, failed, not_found (systemd does not know the unit), unknown |
agent.connection | disconnected when the agent does not run. A running agent’s connection is unknown; connected comes from agents that report it |
agent.device, agent.swarm | the device’s name and its project, from the .flock. Nothing else of the .flock is read |
agent.update | the last self-update. state: none (nothing in progress, nothing failed), activating, probation (the new version runs and has to connect to prove itself), rolled_back (it failed, the previous version runs again, the new one is tried again later), blocked (it failed for good; the agent waits for a newer release), unknown. With version, previous_version, reason and updated_at |
appliance.installed | true when the IronFlock appliance is installed (its unit ironflock.service is there); then dir, version, channel, url, license, service, health and update are there |
appliance.data_left | only when the appliance is not installed and /opt/ironflock still holds its data, left by appliance remove without --purge or by an install that stopped: that folder |
appliance.version, appliance.channel | the files VERSION and CHANNEL in its folder |
appliance.url | the address of its UI: HOME_URL of its .env. Nothing else of .env, which holds its secrets, is read |
appliance.license | present when its license.env holds a license key, missing when not, invalid when its LICENSE_KEY is not a license key (a UUID): every command of the appliance ignores such a key, and the text says so. The key is never shown |
appliance.service | ironflock.service, with the states of the agent’s. running means that the unit started the containers, not that each of them is healthy |
appliance.health | how its services are, from docker ps: state healthy (each runs and passes its health check, or has none, or ran once and ended with 0), starting (a health check is pending), unhealthy, down (no container), unknown; services, how many containers; problems, each service that is not healthy with its state (unhealthy, starting, restarting, exited with exit_code, …) |
appliance.update | an update that waits, from the status file that its UI and its retry timer write: state none (none asked for, or the last one is done), running, failed (the retry timer tries it again), unknown; with version, since and message. cut_short (root only) names the release whose files an update that was cut short kept: the next update puts them back first. No release is looked up |
appliance.ironflock | only when the ironflock that the appliance’s units run at every start (path, /usr/local/bin/ironflock) is missing or older (state; version, what it says) than the one that last installed or updated the appliance (installed). Root only, and only for an appliance that ironflock installed. The text has an ironflock: line then, which says what to install |
os.name, os.id, os.version | PRETTY_NAME, ID and VERSION_ID of /etc/os-release; on a Mac macOS 15.5, macos, 15.5 |
os.ironflockos | true on IronFlockOS. Then os.version is its IMAGE_VERSION, and the fields below follow |
os.image, os.build | IMAGE_ID and BUILD_ID of the image |
os.booted_slot, os.primary_slot | the slot the system runs from, and the one the next boot starts: A or B |
os.slots | the root slots: name (A), rauc_slot (rootfs.0), state (booted, active, inactive), boot_status (good, bad), and version and installed_at of the release an update installed there (absent for a slot written with the image). Absent when the slots cannot be read |
os.last_reset | the last factory reset’s record, only when it is failed or gave-up: result, time, version, attempt, attempts_max, reason, data (old, new, none: what /data holds now), and for gave-up left (of data, booted-slot, other-slot: what of the old owner may still be there) |
os.reset_hold | wait, copy or gave-up, only while a reset hold is there (see the table above) |
os.data_missing | true, only when /data could not be mounted |
os.install | what the last os install recorded, on any Linux with EFI variables, only when it is there and can be read: stage, time (RFC 3339, UTC) and reason (with stopped and failed) |
os.apps_not_persistent | true, only when /apps is not the data partition’s folder |
needs_root | the fields that say unknown because the command did not run as root, such as agent.device and agent.swarm. Empty when nothing was left out |
Exit Code
The exit code is 0 whenever the overview is written, also when it shows a stopped agent, a rolled-back update or a bad slot: status reports and does not judge, and a script reads the fields. It is 1 when the overview cannot be written. For the other exit codes, see Exit Codes.
ironflock doctor
Checks whether this machine can run the device agent, and whether it reaches the hosts the agent talks to: the system, systemd, Docker, the free disk space, the clock, DNS, the proxy, the download server, and the hosts the device’s .flock names.
ironflock doctor [flags]Runs on: Linux and macOS. Root: not required; a check that cannot look without root is skipped and says so. Asks: nothing.
| Flag | Meaning |
|---|---|
-c, --config <file> | the device’s .flock file, whose hosts are checked (default: the installed agent’s) |
agent install runs these checks, all but privileges and docker, before it changes anything, and stops when system, systemd, disk_space or download_tree fails.
# Check this machine, as your login
ironflock doctor
# Check everything as root, as JSON
sudo ironflock doctor --json
# Check the hosts of a .flock before you install it
ironflock doctor -c device.flockResults
Each check has an id, a result and a finding of one line. A result other than ok comes with a hint that says what to do.
| Result | Means |
|---|---|
ok | nothing to do |
warning | it may work, but not well |
failed | the agent cannot be installed, or does not work, as the machine is |
skipped | the check could not look; the finding says why |
Checks
The checks, in the order they are shown. A check keeps its id; a new check gets a new one.
| Id | Looks at | When it is not ok |
|---|---|---|
privileges | whether the command runs as root | warning without root when a check could not look; the finding names those checks. Run sudo ironflock doctor |
system | Linux, Debian or Ubuntu based (the install takes its packages from apt) or IronFlockOS, and the architecture the agent is downloaded for | failed on another system, warning on another Linux with apt. agent install stops on failed |
systemd | systemd runs the system (/run/systemd/system is there) | failed when systemd does not run the system; agent install stops then. warning while the system is degraded: a unit failed |
docker | Docker runs (docker info) | ok without Docker, as agent install brings it, but failed under an installed agent. skipped without root when the user is not in the group docker |
disk_space | the free space for /opt/ironflock and for /var (Docker, apt, the logs) | failed below 128 MiB in /opt: an install or an update of the agent keeps its binary there twice. warning below 2 GiB in either |
clock | this machine’s clock against the Date of the download server’s answer, and its Age when a cache kept it | ok within 10 s, warning up to 5 minutes, failed beyond: signed requests and short-lived tokens are refused, and the apps’ data carry the wrong time. A clock far off breaks TLS itself: failed, with the time the machine has. See Time Synchronization (NTP) |
dns | the names this machine resolves itself: the hosts it reaches directly, and the proxy | a name does not resolve. A host reached through the proxy is the proxy’s to resolve |
proxy | the proxy the network checks go through, and where the setting comes from: the install’s record (/etc/ironflock/proxy.env), else the environment | failed when it is not the URL of a proxy. skipped without root when the record is in the file, which only root reads |
download_tree | the agent at the download server that agent install takes: its channel map, as agent install reads it (device-agent/manifest-v2.json, else manifest.json), and the agent of the production channel for this architecture. With an appliance’s mirror, what the mirror does not give is looked for in the IronFlock cloud, as agent install takes it: the whole agent when the mirror gives no channel map, the agent’s file when it gives the map without the file | failed when there is no agent to download, with the appliance’s mirror when neither the mirror nor the cloud has it; agent install stops then. A server error (5xx) is a warning, and so is an agent that the cloud gives where the mirror does not |
device_endpoint | the .flock’s device_endpoint_url answers | no answer: the agent connects to IronFlock there |
docker_registry | the .flock’s docker_registry_url answers (https://<host>/v2/; with a port, plain HTTP, as an appliance’s registry on the LAN) | no answer: the device pulls its apps’ images from there |
update_server | the agent’s update server has its channel map: the .flock’s update_url (an appliance’s mirror), else the IronFlock cloud | no answer or no channel map: the agent finds its updates there |
The download server is the one agent install takes: --download-base, else the environment variable IRONFLOCK_DL_BASE, else the download mirror of the appliance that the .flock names (its update_url, when that is not a host of the IronFlock cloud), with the cloud for what the mirror does not give, else the IronFlock cloud (Where the Agent Comes From). The hosts a device must reach are listed in Network Requirements.
With and Without -c and Root
The last three checks need a .flock: the one of -c, else the installed agent’s.
| As root | Without root | |
|---|---|---|
Without -c | the hosts of the installed agent’s .flock | the last three checks are skipped: the installed agent’s .flock is root’s alone |
With -c | the hosts of the .flock given | the hosts of the .flock given |
Without root, also docker is skipped outside the group docker, and proxy is skipped when the install recorded the proxy. A network check whose host gives no answer is then skipped too (for dns, a name that does not resolve), as it may get one through the recorded proxy. privileges names every check that could not look, and sudo ironflock doctor runs them.
How the Network Checks Decide
- A host answers when an HTTP answer comes, whatever its status. A 502, 503 or 504 to a plain HTTP request through the proxy is no answer: the proxy gives it when it cannot reach the host.
- A certificate that TLS refuses is no answer. The one exception is the certificate of an appliance’s own CA, when the
.flocknames anappliance_domain: the agent trusts it. - The checks go through the proxy of the check
proxy, by the rules ofHTTPS_PROXY,HTTP_PROXYandNO_PROXYthat the downloads follow, also for every request of a redirect, and wait 20 s for an answer. - A host that an entry of
NO_PROXYkeeps off the proxy goes direct. When it gives no answer that way, the finding names the entry, for example(directly: instance-registry.ironflock.com is in NO_PROXY), and the hint says to take the entry out, with theagent configure --no-proxythat does it when the setting is the install’s record.
Output
$ sudo ironflock doctor
Download base: https://instance-registry.ironflock.com/dl
Device file: /opt/ironflock/device-agent/device-config.flock
CHECK RESULT FINDING
privileges ok runs as root
system ok Debian GNU/Linux 12 (bookworm), amd64
systemd ok systemd runs this system
docker ok Docker 27.3.1 runs
disk_space ok 50.0 GB free for /opt and /var
clock ok within 10 s of the download server's clock
dns ok resolves instance-registry.ironflock.com, cbw.ironflock.com, europe-docker.pkg.dev
proxy ok none: the connections go direct
download_tree ok https://instance-registry.ironflock.com/dl has the agent 1.6.9 for amd64
device_endpoint ok cbw.ironflock.com answers
docker_registry failed europe-docker.pkg.dev cannot be reached: no answer within 20 s
update_server ok https://instance-registry.ironflock.com/dl has the agent's updates
What to do:
docker_registry: the device pulls its apps' images from there: let this machine reach europe-docker.pkg.dev on port 443, directly or through the proxy
Failed: docker_registry.With the appliance’s mirror of the .flock, the first line says so:
Download base: http://10.0.0.5:15002/dl (the appliance's mirror of the .flock; the IronFlock cloud for what it does not give)With --json, shortened here and shown on several lines; the command writes one line.
{
"result": "failed",
"checks": [
{"id": "privileges", "result": "ok", "finding": "runs as root"},
{"id": "docker_registry", "result": "failed", "finding": "europe-docker.pkg.dev cannot be reached: no answer within 20 s", "hint": "the device pulls its apps' images from there: let this machine reach europe-docker.pkg.dev on port 443, directly or through the proxy"}
],
"download_base": "https://instance-registry.ironflock.com/dl",
"flock": "/opt/ironflock/device-agent/device-config.flock"
}| Field | Values |
|---|---|
result | failed when a check failed, warning when one warned, else ok. A skipped check fails nothing |
checks | the checks, in the order above: id, result, finding, and hint with every result but ok |
download_base | the download server checked. With the appliance’s mirror, the mirror alone; the findings name the cloud when they look there |
flock | the .flock whose hosts were checked; null without one |
No output shows a secret: of a .flock the checks read the hosts alone, and a proxy’s password shows as xxxxx.
Exit Code
The exit code is 0 when no check failed; a warning does not fail. It is 1 when a check failed: the result says which, and no error line follows it, and with --json stdout has the result, not an error object. It is 1 with an error when the .flock of -c cannot be read. For the other exit codes, see Exit Codes.