Deploy a release¶
A release comes in three forms, from https://github.com/ziyangliu-666/FastMM/releases and PyPI, all with the same version (Versions and compatibility).
| Form | What it holds | Use it for |
|---|---|---|
fastmm-<version>-x86_64.tar.gz |
fastmm-live, fastmm-gateway, fastmm-ctl, fastmm-top, fastmm-pnl, fastmm-replay, fastmm-sim-exchange, fastmm-sim-itch, the simulator configs, the systemd units, the Prometheus alert rules (0.2.0: fastmm-live, fastmm-top, fastmm-replay, fastmm-sim-itch and fastmm-live.service) |
a host that runs C++ strategies |
ghcr.io/ziyangliu-666/fastmm:<version> |
the same programs without the simulator, non-root | a container host |
pip install "fastmm-engine[live]" |
the engine as a Python package, for strategies written in Python | Python |
Both are built for x86-64-v2. The tarball needs glibc 2.28 or newer and nothing else (Ubuntu 20.04, Debian 10, RHEL 8, Amazon Linux 2023 and later): OpenSSL, libstdc++ and libgcc are linked in. Each release's tarball is run on Ubuntu 22.04 and 24.04, Debian 12, Rocky Linux 9 and Amazon Linux 2023 before it is published. Neither holds a strategy of your own: a C++ strategy is linked into your own fastmm-live build (Register a strategy).
Install the tarball¶
URL=https://github.com/ziyangliu-666/FastMM/releases/download/v0.2.0
curl -LO $URL/fastmm-0.2.0-x86_64.tar.gz
curl -LO $URL/fastmm-0.2.0-x86_64.tar.gz.sha256
sha256sum -c fastmm-0.2.0-x86_64.tar.gz.sha256
sudo tar xzf fastmm-0.2.0-x86_64.tar.gz -C /opt
sudo ln -sfn /opt/fastmm-0.2.0-x86_64 /opt/fastmm
/opt/fastmm/bin/fastmm-live --version
An upgrade is a second directory and a new symlink target. Stop the session before you move the link: fastmm-top refuses a status file written by another build. To keep the pause to one restart, run the new build with --takeover from its own directory (Hand over a running session); the old unit exits 0 on the handoff and systemd does not restart it.
scripts/package-release.sh builds the tarball from a checkout. Its default preset, release-dpdk, links in the DPDK receive backend (Receive a multicast feed); --preset picks another. A tarball built on the host needs that host's glibc or newer; --container builds in manylinux_2_28 with docker, as the release does, and scripts/check-release.sh <tarball> runs it on the five distributions.
Run under systemd¶
deploy/fastmm-live.service is in the tarball and the repository. One unit per engine: one config, one journal directory, one status file.
sudo useradd --system --home-dir /var/lib/fastmm --shell /usr/sbin/nologin fastmm
sudo install -d -o fastmm -g fastmm /var/lib/fastmm /var/lib/fastmm/runs
sudo install -d -m 0750 -o root -g fastmm /etc/fastmm
sudo install -m 0640 -o root -g fastmm my-live.toml /etc/fastmm/live.toml
sudo install -m 0644 /opt/fastmm/deploy/fastmm-live.service /etc/systemd/system/
sudo systemctl daemon-reload && sudo systemctl start fastmm-live
/etc/fastmm/fastmm-live.env holds FASTMM_CONFIG and the API keys the config reads as ${VAR}, root-owned, mode 0600. An unset variable exits 2 before anything connects (Keys).
What the unit sets:
| Directive | Why |
|---|---|
Restart=on-failure, RestartPreventExitStatus=2 3 5 6 7 8 |
a crash (any signal, kill -9 included) or exit 4 restarts after 2 s: the new session restores the position from the store and the venue's executions and cancels the orders the dead one left (Run in production). 2 and 3 need a config fix, 5, 6 and 7 a human (Errors and exit codes), and 8 means another process holds the instance lock; a latched kill switch exits 6 at every start. StartLimitBurst=5 in ten minutes ends a crash loop |
TimeoutStopSec=90 |
SIGTERM pulls the quotes and cancels every order over REST before the process exits (Kill switch and shutdown) |
LimitMEMLOCK=infinity |
[engine] lock_memory = true calls mlockall(), which fails with a warning without it. Drop the line when lock_memory is off |
#LimitRTPRIO=50, #AmbientCapabilities=CAP_SYS_NICE |
commented out. [engine] rt_priority and net_rt_priority set SCHED_FIFO, which needs either line (LimitRTPRIO at least the highest priority configured); without them the session warns and runs on the default scheduler. Uncomment one only with the threads on isolated cores (Real-time scheduling) |
CPUAffinity=2 3 |
the cores the process may use, a superset of [engine] cpu and net_cpus and disjoint from everything else on the host. Pair it with isolcpus, nohz_full and rcu_nocbs (scripts/host-setup.sh tune), and delete the line on a shared host, where cpu = -1 (Go-live checklist) |
MemorySwapMax=0, OOMScoreAdjust=-500 |
a swapped-out or OOM-killed engine leaves orders resting at the venue with nothing to cancel them |
ReadWritePaths=/var/lib/fastmm |
ProtectSystem=strict makes the rest of the filesystem read-only; journals, the epoch file and the kill file live here |
[engine] cpu_dma_latency_us needs write access to /dev/cpu_dma_latency, which the unit does not grant: add the udev rule in Host tuning.
Logs: stderr carries the lines at [logging] mirror_level and above, so journalctl -u fastmm-live -f shows the warnings and errors, and [logging] file takes the full log for a rotation you own. Journal files grow without a cap (The journal).
systemctl status fastmm-live # the exit code of the last run
journalctl -u fastmm-live -f
/opt/fastmm/bin/fastmm-top --name <engine name>
sudo rm /var/lib/fastmm/runs/<engine name>.kill # clears a latched max_loss trip
Run the container¶
The image runs as uid 10001 with no login shell, trusts the distro CA bundle, and runs tini as PID 1 to forward signals and reap children. It holds no simulator, no test certificates and no configuration: the first argument is a config path.
mkdir -p runs && sudo chown 10001:10001 runs
docker run --rm --name fastmm \
-v "$PWD/live.toml:/etc/fastmm/live.toml:ro" \
-v "$PWD/runs:/var/lib/fastmm/runs" \
-e FASTMM_API_KEY -e FASTMM_API_SECRET \
--stop-timeout 90 \
ghcr.io/ziyangliu-666/fastmm:0.2.0 /etc/fastmm/live.toml --duration 60s
| Path in the container | Mount |
|---|---|
/etc/fastmm/live.toml |
the config, read-only. CMD uses this path when you pass no argument |
/var/lib/fastmm/runs |
journals, the epoch file and the latched kill file; the host directory has to be writable by uid 10001 |
/dev/shm/fastmm-<name>.status |
the status file. docker exec fastmm fastmm-top reads it from inside the container |
--stop-timeout 90 gives the cancel-all the same room as the systemd unit. Do not restart a container that exited with 2, 3, 5, 6 or 7. Docker's restart policies cannot exclude exit codes, so use --restart no, or an orchestrator that restarts only on a signal or exit 4, as the unit does.
Other programs run through the same entrypoint by name: docker run --rm -v "$PWD/runs:/var/lib/fastmm/runs" ghcr.io/ziyangliu-666/fastmm:0.2.0 fastmm-replay --journal /var/lib/fastmm/runs/<file>.fmj --verify.
Build it from a checkout with docker build -f deploy/docker/Dockerfile.production -t fastmm:local .. deploy/docker/Dockerfile and deploy/docker/compose.yml are the demo instead: they run as root with test certificates and a simulated exchange (Install).
Next: Run in production, then the Go-live checklist.