Deployment
one server, one disk.
OUTRIDER is a long-running process that writes to its data folder. Host it on one small server with a persistent disk, keep the dashboard on localhost, and put a reverse proxy with HTTPS in front if you want to reach it from a browser.
01What the desk needs from a host
- One always-on process. A VPS or a home server. Serverless platforms do not fit: the desk loops forever, keeps WebSockets open and stores its console in SQLite on disk.
- A persistent disk for
data/: state, journal, recordings, wallet lists and the console database. - Node.js 22+ and outbound internet. Run
node src/cli.mjs doctoron the server: some APIs answer differently from data-centre addresses. - One instance per data folder. Two desks on the same
data/would write over each other.
02Install on the server
- Copy the folderUpload the unzipped package, for example to
/opt/outrider, and createconfig.json(Configuration). - Install exact versions
cd /opt/outrider npm ci npm test node src/cli.mjs doctor - Set the console secret keyRequired on a server. Generate it once and store it in your process manager's environment, never in git:
If you skip it, the desk writes one toopenssl rand -hex 32data/.admin-secret-key. Either way, lose the key and the secrets stored in the console cannot be read.
03Keys in .env
Copy .env.example to .env next to package.json and fill in what you use. The desk loads it on start; a variable already set in the environment wins. .env is in .gitignore and .dockerignore: never commit it.
cp .env.example .env
openssl rand -hex 32 # paste as ADMIN_SECRET_KEY
04Docker
The package includes a Dockerfile and docker-compose.yml. The port is published on the host's loopback only. The Docker files were written to the standard Node pattern but not built on every platform; tell us if your host needs a change.
- Keys
cp .env.example .env # set ADMIN_SECRET_KEY at least - Your settings (optional) Create
config.jsonfromconfig.example.jsonand uncomment the./config.jsonline undervolumesindocker-compose.yml. Without it the container uses the defaults:.dockerignorekeeps your localconfig.jsonout of the image. - Start
docker compose up -d --build docker compose logs -f outrider # the console setup code (printed on every start until the owner exists) # open http://127.0.0.1:8790/admin (through an SSH tunnel on a server) and create the owner docker compose down # stops cleanly: the desk saves its state first
Data lives in the named volume outrider-data, created with the right owner, so it works on any host. Back it up with docker compose cp outrider:/app/data ./backup. Inside the container the dashboard listens on 0.0.0.0 (DASH_HOST); that is allowed only because the operator console guards it. Memory: plan for 2 GB; the research refresh briefly needs more than the desk itself.
05PM2
npm i -g pm2
pm2 start ecosystem.config.cjs
pm2 logs outrider # setup code on the first start
pm2 save && pm2 startup # start again after a reboot
The included ecosystem.config.cjs gives the desk 30 seconds to save on stop and restarts it if it exits.
06Run it as a service (systemd)
An example systemd unit (adjust the user, paths and variables; not part of the package):
[Unit]
Description=OUTRIDER desk
After=network-online.target
[Service]
User=outrider
WorkingDirectory=/opt/outrider
Environment=ADMIN_SECRET_KEY=<64 hex characters>
Environment=TELEGRAM_BOT_TOKEN=<token>
Environment=TELEGRAM_CHAT_ID=<chat id>
ExecStart=/usr/bin/npm start
Restart=on-failure
KillSignal=SIGINT
TimeoutStopSec=30
[Install]
WantedBy=multi-user.target
On stop the desk finishes its cycle, saves state and closes sockets. Read the first log after starting for the one-time setup code (journalctl -u outrider).
07Create the owner before you open it
Run directly or under PM2, the dashboard and console listen on 127.0.0.1 only. In Docker they listen on 0.0.0.0 inside the container, and docker-compose.yml publishes the port on the host's 127.0.0.1 only. Either way, reach them through an SSH tunnel and create the owner account first:
ssh -L 8790:127.0.0.1:8790 <user>@<server>
# then open http://127.0.0.1:8790/admin on your own computer
Until the owner exists, the desk answers only requests whose Host is a loopback address (in Docker: every dashboard request is refused until the owner exists), so a browser coming through a public domain gets 403 create the owner at /admin first on the dashboard. /admin itself stays reachable, and creating the owner needs the one-time setup code printed in the server log. Creating the owner through the tunnel is the simplest path. Consider requiring two-factor for owners and managers right after.
08Reverse proxy with HTTPS
If you want the dashboard on a domain, put a reverse proxy on the same server. Three things matter:
- Allowed hosts. The desk rejects requests whose Host is not a loopback address with
403 host not allowed(protection against DNS rebinding). List your public domain(s) inDASH_ALLOWED_HOSTS, comma-separated, for exampleDASH_ALLOWED_HOSTS=desk.example.com. The proxy passes the original Host header. - HTTPS. Send
X-Forwarded-Proto: httpsso the session cookie is markedSecure. - Streaming. The page uses Server-Sent Events on
/api/stream; turn off response buffering.
Add the variable to the service (above) and restart:
Environment=DASH_ALLOWED_HOSTS=desk.example.com
nginx example:
server {
listen 443 ssl;
server_name desk.example.com;
# ssl_certificate … (for example from certbot)
location / {
proxy_pass http://127.0.0.1:8790;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_buffering off;
proxy_read_timeout 1h;
}
}
Caddy example:
desk.example.com {
reverse_proxy 127.0.0.1:8790 {
header_up X-Forwarded-Proto https
flush_interval -1
}
}
These proxy files are examples and were not tested for this release (unverified). Caddy passes the original Host header by default. The host rule itself was tested with DASH_ALLOWED_HOSTS=desk.example.com before the owner existed: Host 127.0.0.1 got the dashboard, Host desk.example.com got 403 on the dashboard and 200 on /admin/, and any other Host got 403 on both.
09Backups
| What | Why |
|---|---|
data/journal.jsonl, data/state.json | Your paper track record and the desk's state. The journal is hash-chained; a restored copy still verifies. |
data/admin.sqlite* | Console users, settings, history and audit log (SQLite with -wal and -shm files). |
ADMIN_SECRET_KEY or data/.admin-secret-key | Without it, secrets stored in the console cannot be decrypted. Keep it separate from the data backup. |
config.json | Your settings; may contain your RPC key. |
data/pump-events/, gecko-trades/, wallet files | Recordings and wallet books. Large; back up if you want to replay later. |
The simplest safe copy: stop the service, copy data/ and config.json, start it again.
10A public read-only demo
node scripts/export-demo.mjs demo-site # sample paper book only
node scripts/export-demo.mjs demo-site --real data # plus your real liquidation map, wallets, leaderboard and calls
node scripts/export-demo.mjs demo-site --frame-origin https://your-landing.example # allow your landing page to embed it
It writes a static site (index.html, demo-shim.js, api-static/*.json, SNAPSHOT.txt and a vercel.json with security headers) that shows the real dashboard page fed from snapshots. The paper book is sample data with the banner; the kill switch answers that it only works on your own desk. Host the folder on any static host.
By default the generated vercel.json lets the demo be framed only by its own site (frame-ancestors 'self'). To show it in an iframe on your landing page, pass that page's origin with --frame-origin (an https:// origin).
With --real, the copied files contain real wallet addresses and positions from public chains; publish only what you are comfortable showing.
11Updating
- Read the changelogCheck Changelog for migration notes.
- Stop and back upStop the service and copy
data/andconfig.json. - Replace the codeCopy the new version over the old folder, keeping
data/andconfig.json. - Install and check
npm ci npm test node src/cli.mjs doctor node src/cli.mjs verify - StartStart the service and open the dashboard.