Magnemite
Operating it

Troubleshooting

Android's missing resolv.conf, small /data partitions, and 35 GB of egress.

DNS on Android

Symptom

The agent never connects, and its log repeats one line:

lookup magnemite.example.com: no such host

Meanwhile the box browses the web perfectly, and ping from adb shell resolves the same name without trouble.

Why it happens

The agent is built with CGO_ENABLED=0 so it is one static binary that needs no NDK to cross-compile and no shared libraries on the box. The cost is that Go stops calling the system resolver and uses its own, written in Go — and that resolver learns which nameservers to use by reading /etc/resolv.conf.

Android does not have that file. Bionic, Android's libc, asks the netd daemon over a socket instead, and publishes the current servers as system properties:

adb shell getprop net.dns1

So everything on the box resolves names except a CGO-free Go program, which finds no config, has no servers to ask, and fails every lookup.

The fix

It lives in agent/internal/netfix. At startup the agent checks for /etc/resolv.conf; if the file is missing it installs a net.Resolver whose dialer walks this list, rotating to the next entry whenever a server refuses a connection — common on a LAN where the router's resolver black-holes.

OrderNameserverWhere it comes from
1-4net.dns1 … net.dns4getprop, i.e. what netd is using
51.1.1.1Fallback
68.8.8.8Fallback

Consequences worth knowing

On a box whose DNS is genuinely broken, set serverUrl to a bare IP and skip resolution entirely.

And if you ever rebuild the agent with CGO enabled, this code becomes dead weight — but so does the single-binary cross-compile.

What version is actually running

The Status page carries the version of every piece.

CardReports
DashboardIts own web= version, Next.js and Node
HubIts own hub= version, Node, and the agent= it is handing out
DatabaseThe Postgres server version and the Prisma client's
Public edgeWhatever answers on the boxes' URL, from its Server header
RotomThe scanner versions across the fleet, counted

Each number comes from the matching line of the repo's VERSION file.

Reading what the box actually printed

The ⋯ menu on a device page has Logcat (.zip) for a snapshot you can keep and Live logs for logcat as it happens. What each contains, and what to look for, is in reading a box's logs.

An agent that will not update

The hub only sends agent_update to a box whose reported version differs from its target, that is not mid-job, and whose ABI it has a build for. Check the Status page for the target version and AGENT_AUTO_UPDATE, the device page for the version that box reports, and the box's own log for self-update: lines.

Check the full flow in updating the agent.

A MITM installed without its config

The app is on the box and starts, then says its config is missing: Aegis prints The configuration file aegis_config.json is invalid or inexistent. The MITM's job on Rollouts says why the hub sent none, as a warning:

Line on the jobMeans
config not sent: {{device.…}} is empty on <box>A placeholder had nothing to put in on that box.
config not written: this box's agent is too oldThe agent does not claim write_config. It is told to update itself on its next hello.
neitherThe group's MITM package is another package, the group has no config path or JSON, or the deploy had the config switched off.

A box already in that state: Execute → Rewrite MITM config on its device page writes the group's config and restarts the MITM, with nothing reinstalled (an agent of 1.0.5 or later). On an older agent, a Manual deploy of the same MITM build with the config switch left on does it by reinstalling in place.

Small /data

Boxes need roughly 450 MB free: a 172 MB bundle unpacks to ~250 MB and both sit on disk at once. The agent refuses to start an install below that rather than filling the partition. Devices table shows free space per device and warns under 500 MB, when things are already tight.

Egress

A full rollout to 200 boxes is ~35 GB off your server. Settings → Tuning → Max concurrent jobs paces it

Check your traffic allowance

If your ISP or cloud provider has a cap, check it before you start a rollout.

A box enrolled but never appears online?

I know the feeling, such a headache. Check the following:

CheckHow
Right URL?serverUrl must be the hub domain (MAGNEMITE_PUBLIC_URL), not the dashboard's.
Right port on Coolify?The edge domain's port in the Domains tab must be 8090, or the proxy misses Caddy.
Approved?With auto-approve off, a box sits as pending approval and receives nothing.
Token still valid?A revoked enrollment token blocks new enrollments; a revoked device is disconnected.
DNS?See DNS on Android above.

When the table settles nothing, read the agent's own log. It is written to /data/adb/magnemite/agent.log — by the Magisk service on a box, by magnemite-start.sh in a Redroid container — rotated to agent.log.1 at 2 MB, and it is where enrollment and connection failures actually print:

# the whole log, and the rotated previous one
adb shell su -c 'cat /data/adb/magnemite/agent.log'
adb shell su -c 'cat /data/adb/magnemite/agent.log.1'

# follow it live while you retry
adb shell su -c 'tail -f /data/adb/magnemite/agent.log'

# just the enrollment and connection lines
adb shell su -c 'grep -E "enroll|connect|reconnecting|connection lost" /data/adb/magnemite/agent.log'
LineMeans
enrolling with <url>It read serverUrl — check it is the hub's, not the dashboard
enrolled as "<name>"Enrollment worked; the device row exists
connected to <ws url>The socket is up — the box should be online
connection lost / reconnecting inIt reaches the hub but the socket keeps dropping, usually the edge port or proxy

No log file at all means the module never started. Check it is enabled and that the binary is there:

adb shell su -c 'ls -l /data/adb/modules/magnemite-agent/'
adb shell su -c 'ps -A | grep magnemite'
adb logcat -d | grep -i magnemite

Redroid boxes

Three failures particular to containers:

A new device row on every restart is the serial. A container has no ro.serialno of its own, so the agent falls back to a MAC that Docker reassigns on recreate, and the hub — which keys a device on its serial alone — sees a box it has never met. Give each container an explicit, unique ro.serialno.

No agent process means init never started the service. Redroid normally runs SELinux permissive and magnemite.rc sets no seclabel for that reason; an image that enforces denies the service, and the denial is in logcat:

adb shell 'ps -A | grep magnemite-agent'
adb shell logcat -b all | grep -i magnemite
adb shell 'ls -l /system/etc/init/magnemite.rc /data/adb/magnemite/'

Identical load and temperature across boxes is correct. Linux has no per-container load average and a container has no thermal sensor, so both are the host's. Uptime and storage are the box's own; memory is too, but only when the container has a mem_limit.

Downloads 401 at the edge

/files/* is gated by forward_auth against the hub's /internal/authz. A 401 means the device token the box presented is not one the hub recognises, most often a box restored from a backup image whose device row was deleted. Delete its config.json device token and let it enroll again.

On this page