Server requirements

What a server must have before you run the Bicrypto installer — Ubuntu 24.04 LTS or newer, the exact Node.js range, MySQL, Redis, optional ScyllaDB, open ports and root access.

11 min readUpdated 3 August 2026install, ubuntu, glibc, nodejs, redis, mysql, scylladb

Bicrypto is not a PHP application you unzip into a web root. A running install is three long-lived Node.js processes supervised by PM2 (backend, frontend, cron), talking to MySQL and Redis, behind a reverse proxy that terminates TLS. Every requirement below follows from that shape.

The installer (bash installer.sh) checks only three things — RAM, free disk and internet — and warns rather than stops on all three. Everything else on this page is an unstated prerequisite that fails later, during the build or at first boot.

The short version

    • A Linux VPS or dedicated server you control, with SSH and root
    • Ubuntu 24.04 LTS or newer — or another distro shipping glibc 2.38+ (Debian 13+, RHEL/Rocky/Alma 10+, Fedora 39+). Ubuntu 22.04 will not run.
    • x86-64 or ARM64, glibc — not Alpine/musl
    • Node.js 22, 24 or 26 — no other major version will boot
    • pnpm (the repo pins pnpm@10.28.0); npm and yarn will not resolve the workspace
    • 4 GB RAM minimum, 8 GB if you want the frontend build to finish reliably
    • 10 GB free disk on the partition holding the app directory
    • 2+ CPU cores
    • A reachable MySQL or MariaDB server, plus the mysql client on the app box
    • Redis, reachable and answering PING
    • ScyllaDB — only if you intend to run the Ecosystem or Futures addons
    • nginx or Apache in front, with a real TLS certificate
    • initial.sql present in the directory you run the installer from
    • Outbound HTTPS to updates.mashdiv.com for licensing

Node.js — the version is not negotiable

Supported majors are 22, 24 and 26. Nothing else works, and nothing at install time tells you. The build succeeds, PM2 reports the apps as started, and the frontend then answers every request with a connection error to the API.

Both package.json files declare the range:

"engines": {
  "node": "22 || 24 || 26"
}

The constraint comes from one dependency. uWebSockets.js — the HTTP and WebSocket server the backend runs on — has no build step. It loads a prebuilt binary named for your Node ABI:

require('./uws_' + platform + '_' + arch + '_' + process.versions.modules + '.node')

The pinned version (v20.69.0) ships ABI 127, 137 and 147 only, which is exactly Node 22, 24 and 26. On Node 20 (ABI 115) there is no file to load and the process dies with Cannot find module './uws_linux_x64_115.node' four frames deep inside the request handler — a message that reads like a corrupt install, not a wrong runtime.

Those same binaries carry a second, independent constraint that Node cannot satisfy: they are linked against glibc 2.38. That is what sets the operating system floor at Ubuntu 24.04 — see Operating system below. Installing the right Node major on a 22.04 box does not help.

To make that survivable, backend/preflight.ts runs before anything else, prints the running and required versions, and exits with code 78 (EX_CONFIG). The backend and cron apps set stop_exit_codes: [78], so PM2 stops the app with the message still on screen instead of crash-looping sixteen times and scrolling it away.

The installer targets Node 26 specifically — it adds the NodeSource setup_26.x repository, falls back to nvm, and verifies the resulting major version rather than merely that node exists.

Two traps when you change Node by hand:

  • PM2 keeps the Node it was started with. node -v can read 26 while the daemon still runs 20. Re-point it with pm2 kill && npm install -g pm2.
  • Native modules are compiled against an ABI. After a major change, run pnpm rebuild -r before starting.

Preflight also resolves dotenv, module-alias, ioredis, sequelize, mysql2, bullmq and uWebSockets.js up front and exits 78 listing all the missing ones at once, rather than surfacing them one restart at a time.

Operating system

Linux only, and Ubuntu 24.04 LTS is the oldest release that runs the platform. Build on 24.04 unless you have a reason not to — it is what we install on, what the managed-hosting images use, and what the installer is tested against.

The pinned uWebSockets.js prebuilt binaries are linked against glibc 2.38. Ubuntu 22.04 ships glibc 2.35, so the binary cannot be loaded at all and the backend dies at require time:

Error: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.38' not found
       (required by .../node_modules/uWebSockets.js/uws_linux_x64_147.node)

There is no way around it on that box. uWebSockets.js has no build step to fall back to, glibc is not something you upgrade under a running distro, and the requirement is in every Linux binary in the package — x86-64 and ARM64, all three Node ABIs. The fix is a newer OS.

Nothing warns you first. backend/preflight.ts checks the Node major and that uWebSockets.js resolves — it never loads the binary — so an Ubuntu 22.04 box passes every check the platform makes, installs cleanly, builds the frontend, and then crash-loops on the first start.

Check the box before you install anything:

ldd --version | head -1     # glibc must be 2.38 or higher
cat /etc/os-release         # Ubuntu 24.04 or newer
Platform glibc Works
Ubuntu 24.04 LTS and newer 2.39+ Yes — the reference platform
Debian 13 (trixie) and newer 2.41 Yes
RHEL / Rocky / Alma 10 2.39 Yes
Fedora 39 and newer 2.38+ Yes
Ubuntu 22.04 LTS and older 2.35 and older No — too old for the uWebSockets.js binary
Debian 12 (bookworm) and older 2.36 and older No
RHEL / Rocky / Alma 9, Amazon Linux 2023 2.34 No
RHEL 8 / CentOS 7 2.28 / 2.17 No
Linux x86-64 and ARM64 (glibc 2.38+) Yes — prebuilt binaries ship for both
Alpine or any musl distro No — no uWebSockets.js binary exists for musl
Windows / macOS Development only. There is no installer, and pnpm start expects PM2 plus a Linux service layer.

The installer does not enforce any of this. It reads /etc/os-release, exits if the file is absent, then picks a package manager in this order — apt, then dnf, then yum — and never looks at the version. On Ubuntu 22.04 it will run to completion and report success. Checking the release is your job.

If you are already on 22.04, a do-release-upgrade to 24.04 works, but treat it as a migration: snapshot the box, and expect to pnpm rebuild -r afterwards so every other native module is rebuilt against the new libc.

CPU, RAM and disk

RAM. The installer warns below 4096 MB and offers to continue anyway. Treat 4 GB as the floor for running the platform, not for building it: the frontend build runs Node with a 7,780 MB heap ceiling (NODE_OPTIONS=--max-old-space-size=7780). On a 4 GB box with no swap, pnpm build:frontend can be killed by the OOM reaper part-way through, leaving frontend/.next incomplete and the frontend serving 500s. Give the box 8 GB, or add swap before you build.

Disk. The installer requires 10 GB free, measured with df -BG . — that is the partition holding the app directory, not /var and not the MySQL data directory. Below 10 GB it offers to continue. What consumes it: three node_modules trees (root, backend, frontend), the local pnpm store the installer creates at ./.pnpm-store, the Next.js build output, the MySQL data directory, and frontend/public/uploads which grows with every KYC document and dispute attachment. SSD, not spinning disk — order matching is latency-sensitive.

CPU. Nothing checks core count, so nothing stops you installing on a single core. Two is the practical floor: you are running three Node processes plus MySQL plus Redis on the same box. If you later switch to the threaded backend entry point, it spawns min(NEXT_PUBLIC_BACKEND_THREADS, os.cpus().length) workers, so cores directly cap concurrency.

CPU age. On x86-64 without the AVX2 flag, the prebuilt sharp image binaries do not apply. The installer detects this from /proc/cpuinfo and works down a chain — build sharp from source, then sharp@0.32.6, then canvas, then disable Next.js image optimisation entirely. It usually recovers, but the build takes far longer and the last rung costs you optimised images. Any CPU from the last decade has AVX2; very old or heavily restricted VPS profiles may not.

Package manager

pnpm. The repo is a pnpm workspace (frontend and backend are packages) and pins the version:

"packageManager": "pnpm@10.28.0"

npm and yarn will not resolve the workspace links. Note the installer runs npm install -g pnpm@latest, so the version it lands may be newer than the pin — if a dependency resolves oddly, install the pinned version explicitly.

The installer runs pnpm install --store-dir ./.pnpm-store --frozen-lockfile, falling back to a plain install and then --shamefully-hoist. It builds the frontend and runs the seeders; it does not build the backend, because backend/dist ships pre-built and is never deleted.

MySQL or MariaDB

No mysql-server or mariadb-server package appears anywhere in the installer. It only prompts for credentials and then uses them. Worse, the connection check is written as local x=$(mysql …) followed by if [[ $? -eq 0 ]], and $? there is the exit status of local, which is always 0. The check reports success no matter what, and the entire error-handling menu behind it is unreachable. Prove the credentials yourself first, from the app server:

mysql -h DB_HOST -P 3306 -u DB_USER -p -e "SELECT 1"

What must be true before you start:

  • A MySQL or MariaDB server, reachable from the app box on DB_PORT (default 3306). Local or remote both work.
  • The mysql client binary on the app server, even for a remote database — the installer shells out to it to create the schema and import initial.sql.
  • A user that can CREATE DATABASE, and create, drop and alter tables in it. The installer creates the database as utf8mb4 / utf8mb4_unicode_ci if it does not exist.
  • Elevated privileges are helpful but optional. Before importing, the installer raises max_allowed_packet, innodb_log_file_size and clears sql_mode via SET GLOBAL, which needs SUPER / SYSTEM_VARIABLES_ADMIN. Those calls are swallowed on failure, so a restricted user simply skips them — and then a large statement in initial.sql can fail against a low max_allowed_packet.
  • At least 1 GB free in the MySQL data directory; the installer checks and stops below that.

initial.sql must be in the directory you run the installer from. If it is missing the installer exits immediately. If the database already contains tables, you are asked whether to drop them all and reimport — answering yes destroys the existing data.

Credentials go into .env, which the installer creates from .env.example if it is absent:

DB_NAME="v4"
DB_USER="root"
DB_PASSWORD=""
DB_HOST="localhost"
DB_PORT="3306"

Redis

Required. Not optional, not a cache you can skip.

If Redis is unreachable the backend prints the endpoint it tried, which environment variables chose it, and how to install Redis — then exits 78. PM2 stops the app rather than restarting into the same permanent fault. There is no in-memory fallback; an earlier release had one and it was removed, because a per-process store cannot coordinate anything: two processes each believed they held the same lock, and a settings change never reached the others.

Redis holds sessions, CSRF tokens, rate-limit counters, distributed locks, the BullMQ cron queues, and the settings invalidation channel. Losing it mid-flight takes the platform down.

REDIS_HOST="127.0.0.1"
REDIS_PORT="6379"
REDIS_PASSWORD=""

Verify before installing:

redis-cli -h 127.0.0.1 -p 6379 ping   # expects: PONG

The installer does install and start Redis on a supported distro, verifies it with redis-cli ping, and — after three failed attempts — offers to continue without it. Do not take that option. The install will finish and the backend will refuse to boot.

ScyllaDB — Ecosystem and Futures only

Skip this entirely unless you are running the Ecosystem (native trading) or Futures addons. Everything else works without it.

Where it is used, it is the order book and market data store: orders, candles, order book levels, trades, open orders by market and stop orders live in the SCYLLA_KEYSPACE keyspace, with a second keyspace for futures.

Two things to plan for:

  • The installer never touches ScyllaDB. There is not a single mention of it in installer.sh. Install it, create nothing (the client creates its own keyspaces and tables on first connection), and make port 9042 reachable.
  • None of its settings are in .env.example. You add them by hand.
Variable Default
SCYLLA_CONNECT_POINTS 127.0.0.1:9042
SCYLLA_DATACENTER datacenter1
SCYLLA_KEYSPACE trading
SCYLLA_FUTURES_KEYSPACE futures
SCYLLA_USERNAME / SCYLLA_PASSWORD unset
SCYLLA_ENABLED true

If Scylla is disabled or unreachable, ecosystem trading endpoints return 503 rather than failing quietly — the rest of the platform stays up. Setting SCYLLA_ENABLED="false" makes that explicit and stops the connection retries.

Note also that the built-in database backup covers MySQL only. ScyllaDB has no backup path in the product; if you run it, you own its backups.

Ports

Only three ports should be reachable from the internet. Everything else is loopback or private-network traffic.

Port Process Exposure
22 SSH Public (restrict by source if you can)
80 Reverse proxy — ACME challenges and the HTTPS redirect Public
443 Reverse proxy — all real traffic, HTTP and WebSocket Public
3000 Next.js frontend Loopback only
4000 Backend API and WebSockets Loopback only
4001 Cron worker Loopback only — nothing may connect to it
3306 MySQL Loopback or private network
6379 Redis Loopback or private network
9042 ScyllaDB (if used) Loopback or private network

Two details that catch people out.

The installer opens port 3000 in the firewall. Its ufw and firewalld rules allow ssh, http, https and 3000. That is there so you can see the site before a proxy exists. Close it once nginx or Apache is in front, or the frontend is served on plain HTTP alongside your HTTPS site — and nobody will be able to log in on that URL anyway (see below).

Port 4000 is never opened, and must stay that way. In production the backend calls listen() with no host argument, so it binds every interface. Nothing in the installer firewalls it. If your provider has no default-deny firewall, the API is publicly reachable on 4000 the moment it starts. The cron worker on 4001 is worse — it exists purely to avoid a port clash and is not meant to serve anyone.

Outbound, the box needs HTTPS to updates.mashdiv.com for license validation. The heartbeat runs hourly and there is a 72-hour grace period when the host is unreachable, so a blocked egress rule takes three days to become visible.

TLS is mandatory, not a hardening step

When NODE_ENV=production, the backend sets accessToken and sessionId with Secure and SameSite=None. Browsers discard Secure cookies over HTTP, and SameSite=None requires Secure. Login will appear to succeed and the next request will be anonymous.

There is no ACME or certbot automation anywhere in the repo — the installer only prints a reminder. Have a certificate ready, or plan to issue one immediately after install.

The backend itself speaks plain HTTP; TLS terminates at the proxy. Note that in production Next.js does not proxy /api to the backend — those rewrites are development-only — so without a location /api block in your proxy config the entire API is unreachable.

Root and SSH access

The installer refuses to run as anything but root, and exits immediately:

sudo bash installer.sh

That is not defensiveness. It installs system packages, adds the NodeSource repository, enables and starts services with systemctl, writes to /var/log/bicrypto-installer.log, configures ufw or firewalld, creates a dedicated bicrypto system user when the app directory is owned by root, installs /usr/local/bin/bicrypto-start, and registers PM2 with pm2 startup.

You also need ongoing shell access. Starting, stopping and updating the platform are command-line operations (pnpm start, pnpm stop, pnpm updator) — there is no control panel button for them.

Why shared hosting will not work

Not "will be slow" — will not run at all. Each of these on its own is fatal:

  • No root. The installer exits before it does anything.
  • No long-running processes. cPanel-style hosting kills background processes. Bicrypto needs three of them alive continuously, plus PM2 supervising them.
  • No PM2 and no systemd. pnpm start, pnpm stop and the whole update chain are PM2 operations.
  • No native modules. uWebSockets.js, sharp, argon2, bcrypt and the crypto libraries all load compiled binaries.
  • No Redis. Shared hosts do not offer it, and the backend will not boot without it.
  • You cannot choose the Node major. Shared "Node.js app" panels give you a fixed list, and if 22, 24 or 26 is not on it there is nothing to do.
  • No control over ports or the reverse proxy. You need to bind 3000 and 4000 locally and put your own location /api block in front of them.

A small VPS with root beats any shared plan here. If you want the platform installed for you, managed hosting is the alternative to running the installer yourself.

Before you run the installer

Run these on the target box. Every one of them is something the installer will not reliably tell you about.

# OS must be Ubuntu 24.04+ (or another distro with glibc 2.38+)
cat /etc/os-release | head -2
ldd --version | head -1

# Node major must be 22, 24 or 26
node -v

# pnpm present
pnpm -v

# Database reachable with the credentials you are about to type in
mysql -h 127.0.0.1 -P 3306 -u YOUR_DB_USER -p -e "SELECT VERSION()"

# Redis answering
redis-cli -h 127.0.0.1 -p 6379 ping

# Free space on the partition holding the app directory (need 10 GB)
df -h .

# RAM (need 4 GB; 8 GB to build comfortably)
free -m

# The schema file the installer requires in this directory
ls -l initial.sql