Since v0.3.13 every release publishes nevoflux/agent to Docker Hub, built
for amd64 and arm64, so an Apple Silicon or Graviton host pulls a native
image:
docker pull nevoflux/agent:0.3.16 # a specific releasedocker pull nevoflux/agent:latest # the newest published release
Each published tag pairs a browser release with the agent binary that shipped
alongside it, and is only pushed after that release goes public — so a tag
that exists always corresponds to a release you can download.
Pin the version in production.latest moves under you at the next
release; 0.3.16 does not.
You only need this for a custom build — extra bundled packs, a specific
agent/browser pairing, or unreleased binaries. The image is built from the agent
repo's deploy/headless/ directory and downloads the official prebuilt releases
from GitHub — there is no local compilation:
docker build -t nevoflux/agent:custom deploy/headless # amd64 or arm64
TARGETARCH selects the x86_64 / aarch64 download. On an arm64 host — or with
docker buildx build --platform linux/arm64 — it fetches the arm build
automatically.
To test locally-built or unreleased daemon/browser changes (a feature not yet in a
release), stage the four artifacts into deploy/headless/ and build the
COPY-based variant:
installs bun and bun add <GBRAIN_PIN> into ~/.nevoflux/brain-tool, and creates the brain dir ~/.gbrain;
writes ~/.config/nevoflux/config.toml with [knowledge_base.brain] enabled = true and [llm] provider = <LLM_PROVIDER>.
GBrain's LLM calls route through the in-process gateway to that provider, so no
separate GBrain key is needed — just the provider's *_API_KEY (e.g.
ANTHROPIC_API_KEY) at runtime.
# default: gbrain on, provider anthropic (needs ANTHROPIC_API_KEY at run time)docker build -t nevoflux/agent:latest deploy/headless# another provider, a pin, or disabled:docker build \ --build-arg LLM_PROVIDER=openai \ # then set OPENAI_API_KEY at run time --build-arg GBRAIN_PIN=github:garrytan/gbrain#<sha> \ --build-arg INSTALL_GBRAIN=0 \ # skip gbrain entirely -t nevoflux/agent:latest deploy/headless
If you mount your ownconfig.toml, keep [knowledge_base.brain] enabled = true
and a working [llm] provider, or GBrain won't start. Without a working provider,
browser tasks and brain storage still work, but GBrain's LLM synthesis
(brain_think) won't.
Pack install goes over the daemon's RPC (there is no offline installer), so packs
are cloned into the image at build time and installed at startup once the
daemon is up. This works even with an ephemeral (tmpfs) data dir — packs are
re-applied on each start from the baked-in clones.
docker build -t nevoflux/agent:latest \ --build-arg PACK_REPOS="owner/pack-repo https://github.com/x/y.git" \ deploy/headless# cloned to /opt/packs/<name>; the entrypoint runs `pack install` after the daemon is listening.
PACK_REPOS is space-separated — a GitHub owner/repo shorthand or a full git
URL. No packs are installed if it is empty. See Packs for the pack
format.
/work is the image's working directory and a declared volume. Note that task
results (result.json, debug-bundle/, artifacts) are written to the task
workspace under NEVOFLUX_PROFILE_WORK, which the image leaves at its default
/tmp/nevoflux-profiles — see
Task API → status / result.
base-profiles (read-only) holds pre-authenticated login templates, cloned per task.
The container runs read-only; the daemon writes only to the tmpfs mounts above.
Since v0.3.14 the image declares /var/nevoflux/data as a VOLUME as well
(the remote-control head needs it
persistent). For the task service keep mounting it as a tmpfs, as above —
otherwise docker run creates an anonymous volume for it.
The default command is --daemon --headless --http-addr 0.0.0.0:8080. That one
port serves the task API and the OpenAI (Chat Completions, Responses,
models), Anthropic Messages and A2A endpoints — all unauthenticated unless you set
NEVOFLUX_A2A_TOKEN, which covers only the A2A routes. Publish it only where
every caller is trusted. To add MCP, ACP, the admin API or dedicated ports,
override the command — see
Task API → interfaces and ports.
Since v0.3.15 the linux/amd64 release binary — and therefore the amd64
image — is built with an in-process browser engine (nevoflux-skiff) and uses
it by default: browser tools are served in the daemon process first, and a task
escalates to the real headless browser only when the engine cannot do what was
asked. The linux/arm64 binary is built without it and always uses the real
browser.
NEVOFLUX_BROWSER_BACKEND chooses explicitly:
Value
Engine
unset / auto
In-process engine first, real browser on escalation (amd64 default)
browser (or nevoflux)
The real headless browser, without trying the in-process engine
extension
The browser extension path, as before v0.3.15 (arm64 default)
skiff
In-process engine only — what it cannot do fails (for measuring coverage)
A value the build cannot honour (e.g. skiff on arm64) or a misspelling is
logged as an error and falls back to extension. Practical consequences of the
in-process engine: a task it serves launches no browser, so there is nothing to
watch over VNC; and MCP lists browser_* tools without
session mode.
Set NEVOFLUX_BROWSER_BACKEND=browser if you need the same behaviour on both
architectures.
Set NEVOFLUX_VNC=1 (and provide a password file) to watch a run live. The image
then runs x11vnc (raw VNC on :5900) andwebsockify serving noVNC on
:6080:
docker run --rm -p 8080:8080 -p 6080:6080 -p 5900:5900 \ -e NEVOFLUX_VNC=1 -e NEVOFLUX_VNC_PASSWD=/etc/nevoflux/vncpasswd \ -e ANTHROPIC_API_KEY="$ANTHROPIC_API_KEY" \ nevoflux/agent:latest# watch in any browser: http://localhost:6080/vnc.html# or a native VNC client: localhost:5900
Env
Effect
NEVOFLUX_VNC
1 enables raw VNC (:5900) + noVNC (:6080). Default off.
NEVOFLUX_VNC_PASSWD
Path to the VNC password file (default /etc/nevoflux/vncpasswd).
NEVOFLUX_VNC_VIEWONLY
1 = watch only, no input — so you can't disturb the automation.
Keep VNC off in production. A raw VNC / noVNC port is a remote-control
surface. Only expose it behind an authenticated HTTPS proxy or an SSH tunnel.
These are baked into the image (you rarely change them):
Env
Value
Purpose
NEVOFLUX_DATA_DIR
/var/nevoflux/data
Daemon lock / port / db (mount as tmpfs).
NEVOFLUX_BROWSER_BIN
/opt/nevoflux/nevoflux
The bundled browser binary.
NEVOFLUX_BASE_PROFILES
/base-profiles
Where base login profiles are read from.
DISPLAY
:99
The Xvfb virtual display.
HOME
/home/nevo
Non-root home (GBrain + caches live here).
HF_HUB_OFFLINE
1
Embeddings run offline (model is bundled).
MOZ_DISABLE_CONTENT_SANDBOX
1
Required for the browser inside the container.
Runtime switches you may set yourself:
Env
Effect
NEVOFLUX_BRAIN_ENABLED
(v0.3.14)0 / false / no turns GBrain off for this container without rebuilding: the entrypoint skips the brain init (which can otherwise wait up to 40 s at startup), the daemon does not start gbrain serve, and the brain tools are not offered to the agent. 1 / true / yes forces it on; unset follows config.toml.
NEVOFLUX_CONFIG
(v0.3.16) Path of the config.toml to use instead of ~/.config/nevoflux/config.toml — e.g. a read-only mount at /etc/nevoflux/config.toml. Empty counts as unset.
NEVOFLUX_PROFILE_WORK
Where per-task profile clones and task workspaces go (default /tmp/nevoflux-profiles).
The entrypoint (under tini) runs: dbus → Xvfb → (optional VNC) →
GBrain init (skipped when NEVOFLUX_BRAIN_ENABLED is off) → daemon. In daemon mode it starts the daemon in the
background, installs any bundled packs once the daemon is listening, then hands
over (forwarding SIGTERM/SIGINT for a clean shutdown). Non-daemon invocations
(e.g. config, pack) run directly.