Run the project (Ubuntu)
Ubuntu 22.04 LTS or newer is the supported and tested target. Track 1 (Docker Compose) is the recommended path; Track 2 (bare metal) is at the bottom for advanced setups.
Track 1: Docker Compose (recommended)
Prerequisites
- Docker Engine 20.10+ and the
docker compose v2 plugin
git
If you do not yet have Docker installed, the steps below match the official Docker docs and INSTALL.md:
# Remove old Docker versions
sudo apt remove docker docker-engine docker.io containerd runc
# Install prerequisites
sudo apt update
sudo apt install -y ca-certificates curl gnupg lsb-release
# Add Docker's official GPG key
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
# Set up the repository
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" \
| sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# Install Docker Engine and the Compose v2 plugin
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
# Add yourself to the docker group so you don't need sudo
sudo usermod -aG docker $USER
newgrp docker
Verify:
docker --version
docker compose version
Use docker compose (with a space). The legacy hyphenated docker-compose is the deprecated Python script — install the v2 plugin (docker-compose-plugin package) instead.
Three-step quickstart
git clone https://github.com/kynesyslabs/node.git && cd node
cp .env.example .env
docker compose up
The first run pulls images and builds the node container, which can take a few minutes. Subsequent starts are near-instant.
When the stack is healthy:
- Node RPC:
http://localhost:53550 — try curl http://localhost:53550/info
- Grafana:
http://localhost:3000 (default admin / demos)
- Prometheus:
http://localhost:9091
Tour of .env
.env.example is the canonical template. The defaults work for local development. The variables you are most likely to touch:
| Variable | Default | Notes |
|---|
CONSENSUS_TIME | 10 | Block production interval (seconds). |
RPC_FEE | 1 | Per-tx fee component. Total flat fee = RPC_FEE + NETWORK_FEE + BURN_FEE (default 1+1+1=3). |
NETWORK_FEE | 1 | Per-tx fee component. |
BURN_FEE | 1 | Per-tx fee component. |
RPC_PORT | 53550 | Host-mapped RPC port. Change if 53550 is taken. |
EXPOSED_URL | http://localhost:53550 | Public URL advertised to peers. Loopback default is fine for local dev — set it to your reachable address before joining a real network. |
OMNI_ENABLED / OMNI_PORT | true / 53551 | OmniProtocol binary RPC. Modes via OMNI_MODE: HTTP_ONLY, OMNI_PREFERRED, OMNI_ONLY. |
TLSNOTARY_ENABLED | true | Set to false to skip TLSNotary entirely. |
TLSNOTARY_SIGNING_KEY | (empty) | Leave empty in default docker mode — the sidecar manages its own key. Only set when TLSNOTARY_MODE=ffi. |
GRAFANA_ADMIN_PASSWORD | demos | Change this. |
COMPOSE_PROFILES | monitoring,tlsnotary | Which compose profiles to bring up (see below). |
Leave PG_HOST=postgres and TLSNOTARY_HOST=tlsnotary exactly as shipped — those are the in-network service names used by docker compose.
Old docs referenced RPC_FEE=5 and SERVER_PORT. Both are wrong. The current names are RPC_FEE (default 1, plus NETWORK_FEE=1 and BURN_FEE=1) and RPC_PORT.
Compose profiles
COMPOSE_PROFILES in .env controls the optional services:
| Profile | Adds |
|---|
| (none) | postgres + node — bare minimum |
tlsnotary | TLSNotary sidecar (HTTPS attestation) |
monitoring | Prometheus + Grafana |
full | node-exporter (host CPU/RAM/disk metrics) — pair with monitoring |
neo4j | Neo4j (only for CGC/KYC features) |
# Default — postgres + node + tlsnotary + monitoring (uses .env)
docker compose up
# Minimal — only postgres + node (also set TLSNOTARY_ENABLED=false in .env)
COMPOSE_PROFILES= docker compose up
# Add host-level metrics
COMPOSE_PROFILES=monitoring,tlsnotary,full docker compose up
Common operations
# Follow logs
docker compose logs -f node
# Update to the latest source
git pull
docker compose up -d --build
# Stop containers, KEEP volumes (your identity, chain data, etc.)
docker compose down
# Stop AND DELETE all volumes — nuclear, destroys identity + state
docker compose down -v
State persists in named Docker volumes prefixed demos_ (e.g. demos_node_state holds .demos_identity, demos_pgdata holds chain data). They survive docker compose down but not docker compose down -v.
Going public (VPS / public Ubuntu host)
Before joining a real Demos network:
-
Set
EXPOSED_URL in .env to your reachable address — public IP or DNS — not localhost.
-
Open the firewall before advertising the public URL:
sudo ufw allow 53550/tcp comment "Demos RPC"
sudo ufw allow 53551/tcp comment "Demos OmniProtocol"
sudo ufw allow 7047/tcp comment "Demos TLSNotary"
sudo ufw enable
-
Do not expose
5432 (Postgres), 9090 (node metrics), 9091 (Prometheus), or 3000 (Grafana) to the public internet.
-
Seed
demos_peerlist.json with bootstrap peers from the team. Do not invent hostnames; use the values the team publishes.
See INSTALL.md for the full peerlist seeding procedure.
Troubleshooting
| Symptom | Fix |
|---|
docker compose: command not found | You have the legacy Python docker-compose. Install the v2 plugin: sudo apt install docker-compose-plugin. |
Port 53550 already in use | Set RPC_PORT (and EXPOSED_URL) to a free port in .env, then docker compose up -d. |
| Permission denied talking to Docker | sudo usermod -aG docker $USER && newgrp docker. |
EXPOSED_URL warning at startup | Expected for local dev. Set EXPOSED_URL to a routable address before joining a network. |
| Postgres connection refused | Postgres takes a few seconds to become healthy on first boot. The node waits via depends_on. If it persists, check docker compose logs postgres. |
Track 2 runs the node binary natively via Bun and uses Docker only for a Postgres sidecar managed by the ./run script. Pick this path for core development, kernel-level debugging, or TUI-driven operation.
The full walkthrough — prerequisites (build-essential, Bun via Mise or the direct installer, Rust for wstcp), ./scripts/install-deps.sh, identity generation, ./run flags — lives in the source of truth:
For the bare-metal path, override these in your .env:
PG_HOST=localhost
PG_PORT=5332
TLSNOTARY_HOST=localhost