Install with Docker
One backend image and one viewer image, a project folder mounted from your disk, and a first paper mount that can reach nothing real.
The backend image carries the whole headless product — the daemon, the CLI, the runner, the data
service, the recorder and the reporting tools — as one container. Your settings, credentials,
state and data live in a project folder on the host, mounted in; the image contains no credential,
and a fresh folder mounts every venue on paper. Expect about ten minutes: a pull, three commands
in a folder, one docker run.
Requirements
Docker or Podman on any host; see Requirements for the
per-OS notes. On Windows use PowerShell or a WSL shell for every command below, never Git Bash —
MSYS path conversion rewrites the container side of -v, so the daemon finds no project at all
and every venue silently stays on paper.
Steps
1. Pull the image
docker pull ghcr.io/vike-io/vike-tradehub:<version> # GHCR — named first, on purpose
docker pull vikeio/vike-tradehub:<version> # Docker Hub, the same bytesThe release job that publishes these images tags each one with the release version alone —
vike-tradehub:<version>, and no moving latest — so a pull always names a version you chose.
GHCR is named first because publishing there stores no secret anywhere: that job pushes with its
own token, scoped to writing packages, while the Docker Hub name is the shopfront, the default
registry, so that pull needs no host prefix. CHANGELOG.md's 0.1.16 entry records both
destinations and why each is reached the way it is.
Inside is one multicall binary, vike, answering to vike-tradehub (what
the container starts by default), vike-cli, backtest, vike-datahub, vike-recorder,
vike-study and tearsheet (crates/vike/src/main.rs's TOOLS). The binaries are the
release's own bytes, copied in and never compiled inside docker build, which is why --version
can still name the commit.
2. Make the project folder
R=/srv/vike-trader # your folder — substitute it everywhere below
mkdir -p "$R/settings/state" "$R/data"
docker run --rm ghcr.io/vike-io/vike-tradehub:<version> --template > "$R/settings/tradehub.toml"settings/stateis the daemon's one writable directory. TheHALTkill switch lives there and nothing creates it lazily; without it the switch is dead until someone reaches for it.settings/tradehub.tomlis the daemon profile — venue, symbol, strategy.--templateis a verb of the entrypoint, not the daemon: it prints the repository's own template to stdout and nothing else, so the redirect is safe. Without the file the daemon exits (bad profile <path>: No such file or directory).data/is the store, which must outlive a container.
The entrypoint refuses to start without the mount or settings/, then reports everything else
wrong inside the folder in one numbered list (deploy/docker/entrypoint.sh's check_project),
and probes that settings/state keeps flock-serialised appends — the one filesystem shape no code
change rescues.
3. Run it
docker run -d --name vike-tradehub \
--user "$(id -u):$(id -g)" \
-v /srv/vike-trader:/project \
--restart on-failure \
--stop-timeout 10 \
--memory 2g \
--cap-drop ALL \
--security-opt no-new-privileges \
ghcr.io/vike-io/vike-tradehub:<version>The node server is opt-in and the docker run above starts no listener: turn it on with
-e VIKE_TRADEHUB_ADDR=0.0.0.0:7879, publish the port to loopback — -p 127.0.0.1:7879:7879,
never -p 7879:7879 — and reach it over an SSH tunnel. The image sets
VIKE_TRADEHUB_ALLOW_PUBLIC_BIND=1 because a container must bind the wildcard inside its
namespace; on that wildcard address you will see one ProceedExposed line at start, and it is
true.
On Docker Desktop (Windows/macOS) keep settings/ a bind mount and put the two writable paths
on named volumes: the mmap-backed journal suite measured 389 s over the host passthrough against
1.23 s on a volume.
docker volume create vike-state && docker volume create vike-data
docker run -d --name vike-tradehub \
-v /srv/vike-trader:/project:ro \
-v vike-state:/project/settings/state \
-v vike-data:/project/data \
--read-only --tmpfs /tmp \
--restart on-failure --stop-timeout 10 --memory 2g \
--cap-drop ALL --security-opt no-new-privileges \
ghcr.io/vike-io/vike-tradehub:<version>Drop --user with named volumes (a volume is owned by whoever first writes it); keep it with a
Linux bind mount, which carries host ownership.
The template gives you a paper mount, and that is the point
Nothing in a daemon profile arms a venue. Going live needs the venue's keys in
settings/secrets.env — absent credentials are the live gate — and the per-venue ceiling in
policy.toml raised from its paper default, which can only ever refuse. Start on paper, read
the snapshot summaries, then read Arm one venue for demo.
Verify
docker run --rm -v /srv/vike-trader:/project --user "$(id -u):$(id -g)" \
--entrypoint /opt/vike/bin/vike-cli ghcr.io/vike-io/vike-tradehub:<version> config check
docker run --rm --entrypoint /opt/vike/bin/vike-tradehub ghcr.io/vike-io/vike-tradehub:<version> --versionThe first judges the settings tree and the credential store and says what is wrong; it does not
judge tradehub.toml, which the entrypoint checks itself before this call. The second prints the
commit the binary was built from. The kill switch is a file, so it works from the host or through
docker exec whether or not the runtime answers: touch /srv/vike-trader/settings/state/HALT
stops new orders and rm resumes.
Next steps
A fresh store is empty, and every tool over it answers with an empty result that looks exactly like a broken install. Two commands close it, both through the image's other tools:
docker run --rm -v "$R:/project" --entrypoint /opt/vike/bin/vike-cli ghcr.io/vike-io/vike-tradehub:<version> init
docker run --rm -v "$R:/project" --entrypoint /opt/vike/bin/backtest ghcr.io/vike-io/vike-tradehub:<version> --seed-demoThen Quickstart: first backtest. To watch the daemon
from a browser — with its node server turned on, as above — the viewer image runs
vike-desktop --observe on a virtual display:
docker run --rm -p 127.0.0.1:6080:6080 \
-e VIKE_TRADEHUB_ADDR=host.docker.internal:7879 \
-e VIKE_TRADEHUB_OBSERVE_KEY=<the daemon's observe key> \
-e VIKE_THIN_VNC_PASSWORD=<a password> \
ghcr.io/vike-io/vike-thin:<version>Open http://localhost:6080; it connects on its own. Publish that port to loopback only — VNC
carries mouse and keyboard, so whoever reaches it drives a GUI authenticated to a trading node —
and prefer the native vike-desktop binary whenever you can run one.
Troubleshooting
- Every venue on paper, nothing in the log. The mount or
settings/was missing, ordocker runcame from Git Bash. The entrypoint names the-vflag; see Troubleshooting. - Polymarket dials fail at the SOCKS connect. The egress proxy defaults to the container's own
loopback (
crates/bridges/polymarket/src/egress.rs'sproxy_url). Set-e POLY_PROXY_HOST=host.docker.internal, a full-e POLY_SOCKS_PROXY=..., or-e POLY_PROXY_ENABLED=falseif you are not geo-blocked; usesocks5h, neversocks5. - The log lands in your folder and grows. Keep the baked
VIKE_LOG_FILE_LEVEL=warn; the file layer defaults totrace. docker stopcuts the resting-order cancel sweep short. If you raiseshutdown_deadline_msintradehub.toml, raise--stop-timeoutin the same breath.'<project>/bin' holds files this image did not install. The entrypoint refuses to replace abin/it did not stamp; move the files aside or setVIKE_CONTAINER_ADOPT_BIN=1if you mean it.
Requirements
Windows and Linux, no macOS build; what each surface needs in GPU, memory, disk and network, with the numbers that were measured rather than guessed.
Install on Windows
The two Windows assets, the unknown-publisher prompt on an unsigned executable, one project folder, and the first launch.