Procedure for deploying Animus on a host, with two independent decision axes: deployment mode (full instance vs external node) and installation method (Docker vs direct install). Includes federation of full instances into a synchronized network.
| Full instance | External node | |
|---|---|---|
| What it is | The whole daemon: admin UI, LLM providers, agents, scheduler, channels | A stateless satellite daemon (animusd --node) |
| Needs a model backend? | Yes — an Ollama server (see setup-ollama-server) or cloud provider keys |
No — no LLM anywhere on it |
| Needs another Animus? | No — this is a hub | Yes — connects to an existing instance |
| State | Persistent (state DB, config, vault) | None — reconnects and re-registers |
| Network | Listens on :8080 (admin + API) | Outbound-only WebSocket, no open ports |
| Effort | ~1–2 h with provider wiring | ~15 min |
| Use when | You're standing up an agent host, or the first Animus in a topology | You want an agent to run tools on this machine from elsewhere |
Rule of thumb: if in doubt, you want a full instance. An external node only makes sense when a hub already exists and this host is being added as tool surface.
A full instance can also be a member of a network. Instances can peer with each other (see Federation under Mode A): state converges across peers, so agents on any member see the same agent-global memory, and if one instance drops, consumers can be pointed at another member that already holds the converged state — and lease-based scheduler failover keeps scheduled work running on a surviving member. The federation phases are merged and tested (sync transport, anti-entropy healing, scheduler leases); the tracking issue remains open for remaining scope such as config replication.
| Docker | Direct install | |
|---|---|---|
| Best when | Container fleet, shared hosts, isolation matters | Hardware integration; edge and IoT hosts |
| Hardware access | Needs explicit --device / /dev passthrough |
Native — GPIO, USB, cameras, sensors just work |
| State | Named volumes | --config-dir / --data-dir on the host |
| Upgrades | Pull new image, recreate | git pull + rebuild |
| Prerequisites | Docker (+ toolkit for GPU) | Build toolchain; builds from source (no binary releases yet) |
For hosts whose purpose is touching hardware — cameras, sensors, test benches — direct install avoids the container boundary entirely and is usually the right call. For everything else, Docker's reproducibility wins.
Quick map: Instance+Docker → A1 · Instance+Direct → A2 · Node+Docker → B1 · Node+Direct → B2.
setup-ollama-server first) or cloud provider credentialsanimus-data
and animus-home volumes)docker volume create animus-data
docker volume create animus-home
docker run -d --name animus \
-p 8080:8080 \
-v animus-data:/data \
-v animus-home:/home/animus \
--memory=4g --memory-swap=4g \
--restart unless-stopped \
mrsommer/animus-sudo:dev
mrsommer/animus-sudo (animus user has passwordless
sudo — for agents that manage their host OS) and the standard image
without sudo. Prefer the standard image unless the agent genuinely
needs host modification; sudo variants must never be publicly exposed.:dev builds from dev (via
PUSH=1 ./scripts/build-animus-docker-sudo.sh dev), release tags from
releases.--device /dev/video0 (etc.) per device; direct
install (A2) avoids this friction entirely.Verify: docker logs animus shows the daemon booting with no ERROR
lines; curl -s http://<host>:8080/ returns the admin UI.
git clone https://github.com/railstracks/animus.git
cd animus && ./scripts/build.sh
Produces dist/bin/animusd (plus the admin UI). Requires a C++
toolchain and CMake; the build fetches its dependencies.
dist/run, logs under
dist/log, env-driven: ANIMUS_ADMIN_HOST, ANIMUS_ADMIN_PORT): ./scripts/start-daemon.sh
or the daemon directly with flags:
./dist/bin/animusd --daemon \
--admin-host 0.0.0.0 \
--admin-port 8080 \
--config-dir /etc/animus \
--data-dir /var/lib/animus
--sqlite-path, default under
the data dir); or PostgreSQL for shared/robust storage: --storage-backend postgresql --pg-host <host> --pg-database <name> \
--pg-username <user> --pg-password <pass>
[Unit]
Description=Animus daemon
After=network-online.target
[Service]
User=animus
ExecStart=/opt/animus/dist/bin/animusd --daemon --admin-port 8080 \
--config-dir /etc/animus --data-dir /var/lib/animus
Restart=on-failure
[Install]
WantedBy=multi-user.target
Verify: same as A1 — daemon log clean, admin UI on :8080.
On fresh state, set the initial admin credentials via the admin UI's
first-run flow or --admin-password on first start.
⚠ If the instance is already initialized, --admin-password is silently
ignored — check the Users page in the admin UI rather than trusting the
flag.
Then create an API key for programmatic access: admin UI → API keys, or
POST /api/users/{id}/tokens — note it requires the account password
re-entered in the request body.
Verify: log into the admin UI; the API key works on
GET /api/v1/agents with Authorization: Bearer <key>.
Admin UI → Providers:
http://<ollama-host>:11434, model tag copied
verbatim from that server's ollama list (quant suffixes included),
context window set to what the model actually supports. Verify
connectivity from the daemon's network position.Verify: the Providers page shows the provider healthy; a test completion returns.
Create or pick a test agent, send a turn from the admin UI's session
view, and watch a full round trip — prompt → provider → response
rendered. If channels are planned, follow the configure-channel SOP
from here.
Full instances can join a synchronized network. Configuration lives in
the kernel config's node section (⚠ verify exact file format for your
version — the keys are):
"node": {
"id": 1,
"peers": ["https://animus-b.example.com", "https://animus-c.example.com"],
"sync_token": "<shared-secret>"
}
How it behaves:
schedule_leases) coordinate scheduled work across members —
schedule takeovers when a member drops.Status: multi-phase work under issue #78 ("Node federation: multi-master Animus network"). Landed and merged: node identity + node-scoped id ranges (P1a, #87); replication outbox with LWW apply on every node (P1b, #88 — single-node deployments are federation-ready by default); pull-based sync transport with membership heartbeat (P1c,
recovery (P1d, #90); and the scheduler line — scheduler tables replicate
(P2a, #91) plus schedule leases with epochs and two-phase fenced
dispatch (P2b, #92). The issue tracks remaining scope (e.g. config
replication); verify against your version. Inspect state via the sync
admin routes (AdminServerRoutesSync: outbox, handshake, status).
git pull + ./scripts/build.sh + restart via
scripts/stop-daemon.sh / start-daemon.sh. State compatibility is
generally forward-safe within a minor line; ⚠ snapshot config + data
dirs first.--log-level values are UPPERCASE only (DEBUG, not debug — the
lowercase enum crash-loops the daemon).--log-file (or ANIMUS_LOG_FILE) redirects all
output to a file; the systemd unit's journal captures it otherwise.docker run -d --name animus-node \
-e SERVER_URL=ws://<hub>:8080/ws/node \
-e NODE_TOKEN=*** \
-e NODE_SIGNING_KEY=*** \
-e NODE_NAME=workstation \
-e NODE_TOOLS=exec,file \
--restart unless-stopped \
mrsommer/animus-node:latest
Stateless — no volumes, outbound-only. wss:// when the hub is behind
TLS. The node reconnects automatically (exponential backoff, 1s→60s cap).
Hardware hosts: add --device flags per device — but for a host whose
job is hardware, prefer B2.
Same binary as a full instance — node mode is a command-line flag, not a separate build. Build per A2 step 1, then:
./dist/bin/animusd --node \
--server-url ws://<hub>:8080/ws/node \
--token *** \
--signing-key *** \
--node-name bench-camera \
--node-tools exec,file
This is the natural fit for edge hardware (cameras, sensors, GPIO, USB instruments): no container boundary, native device access, and the node loads only the tool handlers in its allowlist — a small footprint for small machines.
Run it under systemd with the same pattern as A2 step 4 (Restart=always
suits satellites).
Verify: hub admin UI → Nodes shows the node connected with
hostname, OS, uptime, and its tool list.
curl -X POST http://<hub>:8080/api/v1/nodes/tokens \
-H "Content-Type: application/json" \
-d '{"description": "bench-camera"}'
Returns token (nt_...) and signing_key (sk_...) — shown exactly
once, store both securely. (Admin UI → Nodes → Generate Token works
too.)
Agents see no nodes until granted:
curl -X PUT http://<hub>:8080/api/v1/agents/<agent>/allowed_nodes \
-H "Content-Type: application/json" \
-d '{"allowed_nodes": ["bench-camera"]}'
Verify end-to-end: from an allowed agent, run any tool with
"__node": "bench-camera" added to its arguments (e.g. shell_exec of
uname -a). The call executes on the remote host and returns. An agent
without the grant must get an error referencing the disallowed node.
DELETE /api/v1/nodes/tokens/{id}) disconnects its
nodes immediately.Instance grows to multi-GB RSS and the host thrashes/OOMs. (Production incident: an email-channel reconnect storm ballooned RSS to 7.3 GB in ~40 min on a zero-swap host; the OOM kill was the cure.) Always set memory limits on instance containers, and prefer hosts with swap or watch RSS after enabling email channels.
Provider "healthy" but every turn silently fails.
Provider type vs provider instance name confusion: some config paths
resolve types (openai, zai, ollama), others expect the instance
name. A mismatch yields empty provider responses, not an error. Check
the provider entry's type field and the daemon log's LLM lines.
Node shows connected but agents can't use it.
Default deny — the agent lacks an allowed_nodes grant, or the __node
name doesn't match NODE_NAME exactly.
Token authentication fails on a new node. Token copied incompletely (they're long), or the token was revoked. Tokens are stored hashed server-side; regenerate rather than debugging a transcription.
Direct build fails on missing libraries.
./scripts/build.sh fetches its dependencies but needs the C++ toolchain
and CMake present first. Check the first error, install the named
package, re-run — the build resumes where it left off.
Federation peers not syncing.
Check node.id uniqueness across members, identical sync_token, and
peer URLs reachable on the admin API. A peer reporting a different
protocol major is incompatible by design — align versions. Status and
per-peer cursors are exposed via the sync admin routes.
setup-ollama-server — the model backend a full instance consumesconfigure-channel — channels on a running instance| Version | Date | Author | Description |
|---|---|---|---|
| 2 | 2026-10-14 | Animus | Procedure for deploying Animus on a host, with two independent decision axes: deployment mode (full instance vs external node) and installation method (Docker vs direct install). Includes federation of full instances into a synchronized network. |
| 1 | 2026-10-14 | Animus | Procedure for deploying Animus on a host, with two independent decision axes: deployment mode (full instance vs external node) and installation method (Docker vs direct install). Includes federation of full instances into a synchronized network. |