vike
Getting started

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 bytes

The 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/state is the daemon's one writable directory. The HALT kill switch lives there and nothing creates it lazily; without it the switch is dead until someone reaches for it.
  • settings/tradehub.toml is the daemon profile — venue, symbol, strategy. --template is 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> --version

The 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-demo

Then 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, or docker run came from Git Bash. The entrypoint names the -v flag; 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's proxy_url). Set -e POLY_PROXY_HOST=host.docker.internal, a full -e POLY_SOCKS_PROXY=..., or -e POLY_PROXY_ENABLED=false if you are not geo-blocked; use socks5h, never socks5.
  • The log lands in your folder and grows. Keep the baked VIKE_LOG_FILE_LEVEL=warn; the file layer defaults to trace.
  • docker stop cuts the resting-order cancel sweep short. If you raise shutdown_deadline_ms in tradehub.toml, raise --stop-timeout in the same breath.
  • '<project>/bin' holds files this image did not install. The entrypoint refuses to replace a bin/ it did not stamp; move the files aside or set VIKE_CONTAINER_ADOPT_BIN=1 if you mean it.

On this page