Prefer Docker? See DOCKER.md for the container-based deployment guide — the recommended path for production nodes.
This guide walks you through setting up and running a JMDN node from source on Linux or Raspberry Pi. Estimated time: 10–30 minutes on a clean machine.
Before you begin, ensure your machine meets the following requirements.
| Requirement | Minimum |
|---|---|
| OS | Ubuntu 20.04+, Debian 11+, Raspberry Pi OS (64-bit) |
| Architecture | x86_64 or ARM64 |
| RAM | 2 GB |
| Disk | 10 GB free |
| Network | Internet access |
| Tool | Version | Notes |
|---|---|---|
| Git | Any | Required to clone the repo |
| Go | 1.25+ | Installed automatically by setup_dependencies.sh |
| GCC | Any | Required for CGO build (gcc package) |
| ImmuDB | Latest | Installed automatically |
| Yggdrasil | Latest | Installed automatically |
| Redis | 5+ | Installed automatically; optional — powers the account sync queue, node falls back to direct ImmuDB writes if unavailable |
# Ubuntu / Debian
sudo apt update && sudo apt install -y git curl
# CentOS / RHEL
sudo yum install -y git curl
# macOS (development only)
brew install gitgit clone https://github.com/JupiterMetaLabs/jmdn.git
cd jmdnTo run a specific release:
git checkout v2.0.0 # replace with target versionRun the unified setup script. This installs Go, ImmuDB, Yggdrasil, and Redis.
sudo ./Scripts/setup_dependencies.shNote: After Go is installed, restart your shell or run
source ~/.bashrc(or~/.zshrc) to update yourPATH.
To install dependencies individually:
sudo ./Scripts/setup_dependencies.sh --go # Go runtime only
sudo ./Scripts/setup_dependencies.sh --immudb # ImmuDB only
sudo ./Scripts/setup_dependencies.sh --yggdrasil # Yggdrasil only
sudo ./Scripts/setup_dependencies.sh --redis # Redis onlyRedis password: the script generates a random password on first run and saves it to
/etc/jmdn/redis.env(root-only). If you start the node viastart_jmdn_wrapper.sh(the standard systemd/launchd/rc.d path, Step 6), this is automatic — the wrapper sourcesredis.envand exportsJMDN_DATABASE_REDIS_PASSWORDfor you, no manual step needed. Only setdatabase.redis.passwordin/etc/jmdn/jmdn.yamlby hand if you're running the binary directly instead of via the wrapper — the config generator in Step 5 does not do this for you yet. Re-running the script reuses the same password rather than rotating it.
./Scripts/build.shThis compiles the jmdn binary into your current directory with version metadata embedded (commit, branch, tag, build time).
To verify the build:
./jmdn --versionCopy the default template and manually inject your Node Alias and secrets:
sudo cp jmdn_default.yaml /etc/jmdn/jmdn.yaml
sudo nano /etc/jmdn/jmdn.yaml(Note: The legacy setup_config.sh tool is deprecated as it lacks automated secrets injection).
For all available options, see config/config.go or run:
./jmdn --helpInstall the binary to /usr/local/bin/ and register systemd services (jmdn and immudb):
sudo ./Scripts/install_services.shBefore opening firewall rules, review PORTS.md for the full security posture of each port and recommended cloud firewall rules.
Skip this and a fresh node will start from genesis and slowly scan every block
on its own — that's what fastsync.catch_up_from_block: 0 in jmdn.yaml
means. For anything other than a throwaway dev node, load the pre-built
snapshot instead, same as the Docker path does with jmdn-bootstrap:
sudo ./Scripts/bootstrap_sync.shRequires curl, wget, awk, md5sum, tar, python3 on PATH — install
any that are missing (sudo apt install -y wget python3, most are already
present on a stock Ubuntu/Debian image). Downloads and verifies the chain
snapshot into /opt/jmdn/data, same location install_services.sh just
created. Takes 10–30 minutes depending on bandwidth; safe to re-run — it
skips immediately if /opt/jmdn/data/.bootstrapped already exists.
Ownership note: the script chowns
/opt/jmdn/datatoIMMUDB_UID(default3322, matching the Docker image'sjmdnuser) so it can be re-run unmodified against Docker-style snapshots. On bare metal with the defaultSERVICE_USER=root(seeinstall_services.sh), immudb runs as root and ignores file ownership, so this is a no-op in practice. If you setSERVICE_USERto a non-root user, passIMMUDB_UID=<that user's uid>so immudb can actually read its own data:sudo IMMUDB_UID=$(id -u jmdn) ./Scripts/bootstrap_sync.sh
To force a fresh snapshot later (e.g. after a long time offline):
sudo rm /opt/jmdn/data/.bootstrapped
sudo ./Scripts/bootstrap_sync.shStart the services:
sudo systemctl start immudb
sudo systemctl start jmdnEnable them to start automatically on reboot:
sudo systemctl enable immudb
sudo systemctl enable jmdn# Check service status
sudo systemctl status jmdn
# Follow live logs
sudo journalctl -u jmdn -fA healthy node will log peer connections and block synchronisation activity within a few seconds of starting.
To run the node directly without systemd — useful for local development or debugging:
./jmdn -config /etc/jmdn/config.envImportant: ImmuDB must be running before starting
jmdn. Either start it via systemd (sudo systemctl start immudb) or manually:immudb --dir /opt/jmdn/data
To update a running node to the latest code, use the deploy script (Ansible calls this automatically in production):
sudo ./Scripts/deploy.shThis script builds a new binary, performs an atomic swap, restarts the service, and automatically rolls back to the previous version if the health check fails.
sudo journalctl -u jmdn -n 100 --no-pagerCheck for: missing config file, ImmuDB not running, or port conflicts.
Ensure ImmuDB is running and accessible:
sudo systemctl status immudbIf you see server state is older than the client one, ImmuDB's state is ahead of the local client cache. This typically resolves after a clean restart:
sudo systemctl restart immudb && sudo systemctl restart jmdnThe default configuration disables console logging (LOG_CONSOLE=false) on low-resource devices. Logs are available via journald:
sudo journalctl -u jmdn -fTo enable console logs, set LOG_CONSOLE=true in /etc/jmdn/config.env and restart the service.
export PATH="/usr/local/go/bin:${PATH}"Add this line to your ~/.bashrc or ~/.zshrc for persistence.
| Command | Description |
|---|---|
sudo systemctl restart jmdn |
Restart the node |
sudo systemctl stop jmdn |
Stop the node |
sudo journalctl -u jmdn -f |
Follow live logs |
sudo journalctl -u jmdn -n 50 --no-pager |
View last 50 log lines |
./jmdn --version |
Check running binary version |
For architecture and protocol documentation, see README.md.