Skip to content

IFM Deployment Guide

Overview

This guide covers deploying IFM nodes in various environments: from development laptops to production bootstrap nodes, mobile devices, and browser applications.


Deployment Scenarios

ScenarioUse CaseComplexity
DevelopmentLocal testing, single machineLow
Personal NodeDaily use on laptop/phoneLow
Community RelayHelp network connectivityMedium
Bootstrap NodeNetwork entry pointsHigh
Server/HeadlessBots, bridges, servicesMedium
Browser/WASMWeb applicationsMedium
MobileiOS/Android appsMedium
IoT/EmbeddedSensors, devicesHigh

Development Deployment

Quick Start (Single Machine)

bash
# Terminal 1: Bootstrap node (optional, for isolated testing)
ifm bootstrap --port 4001 --data-dir ./bootstrap-data

# Terminal 2: Node A
ifm init --name "Alice" --data-dir ./alice-data
ifm --data-dir ./alice-data config set network.bootstrap_peers '["/ip4/127.0.0.1/udp/4001/quic-v1/p2p/BOOTSTRAP_PEER_ID"]'
ifm --data-dir ./alice-data connect
ifm --data-dir ./alice-data tune 91.700

# Terminal 3: Node B
ifm init --name "Bob" --data-dir ./bob-data
ifm --data-dir ./bob-data config set network.bootstrap_peers '["/ip4/127.0.0.1/udp/4001/quic-v1/p2p/BOOTSTRAP_PEER_ID"]'
ifm --data-dir ./bob-data connect
ifm --data-dir ./bob-data tune 91.700

# Now they can communicate!
ifm --data-dir ./alice-data broadcast "Hello Bob"

Docker Compose (Multi-node Testing)

yaml
# docker-compose.yml
version: '3.8'

services:
  bootstrap:
    image: ifmprotocol/ifm:latest
    command: bootstrap --port 4001 --data-dir /data
    volumes:
      - ./bootstrap-data:/data
    ports:
      - "4001:4001/udp"
      - "4001:4001/tcp"
    networks:
      - ifm-net

  alice:
    image: ifmprotocol/ifm:latest
    command: sh -c "ifm init --name Alice --data-dir /data --force && ifm connect --bootstrap /ip4/bootstrap/udp/4001/quic-v1/p2p/BOOTSTRAP_ID && ifm tune 91.700 && tail -f /dev/null"
    volumes:
      - ./alice-data:/data
    depends_on:
      - bootstrap
    networks:
      - ifm-net

  bob:
    image: ifmprotocol/ifm:latest
    command: sh -c "ifm init --name Bob --data-dir /data --force && ifm connect --bootstrap /ip4/bootstrap/udp/4001/quic-v1/p2p/BOOTSTRAP_ID && ifm tune 91.700 && tail -f /dev/null"
    volumes:
      - ./bob-data:/data
    depends_on:
      - bootstrap
    networks:
      - ifm-net

networks:
  ifm-net:
    driver: bridge
bash
docker-compose up -d
docker-compose logs -f alice

Personal Node Deployment

Linux/macOS (Systemd/User Service)

ini
# ~/.config/systemd/user/ifm.service
[Unit]
Description=IFM Node
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
ExecStart=%h/.cargo/bin/ifm connect --config %h/.config/ifm/config.toml
Restart=on-failure
RestartSec=5
Environment=IFM_DATA_DIR=%h/.local/share/ifm
Environment=IFM_CONFIG=%h/.config/ifm/config.toml

# Resource limits
MemoryMax=256M
CPUQuota=50%

[Install]
WantedBy=default.target
bash
# Enable and start
systemctl --user daemon-reload
systemctl --user enable --now ifm.service

# Check status
systemctl --user status ifm.service
journalctl --user -u ifm.service -f

macOS (LaunchAgent)

xml
<!-- ~/Library/LaunchAgents/network.ifm.node.plist -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>network.ifm.node</string>
    <key>ProgramArguments</key>
    <array>
        <string>/opt/homebrew/bin/ifm</string>
        <string>connect</string>
        <string>--config</string>
        <string>/Users/USERNAME/.config/ifm/config.toml</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <true/>
    <key>StandardOutPath</key>
    <string>/Users/USERNAME/.local/share/ifm/logs/ifm.log</string>
    <key>StandardErrorPath</key>
    <string>/Users/USERNAME/.local/share/ifm/logs/ifm.error.log</string>
    <key>EnvironmentVariables</key>
    <dict>
        <key>IFM_DATA_DIR</key>
        <string>/Users/USERNAME/.local/share/ifm</string>
    </dict>
</dict>
</plist>
bash
launchctl load ~/Library/LaunchAgents/network.ifm.node.plist
launchctl start network.ifm.node

Windows (Task Scheduler / NSSM)

powershell
# Using NSSM (Non-Sucking Service Manager)
nssm install IFMNode
nssm set IFMNode Application "C:\Program Files\ifm\ifm.exe"
nssm set IFMNode AppParameters "connect --config C:\Users\USERNAME\AppData\Roaming\ifm\config.toml"
nssm set IFMNode AppDirectory "C:\Users\USERNAME\AppData\Roaming\ifm"
nssm set IFMNode Environment "IFM_DATA_DIR=C:\Users\USERNAME\AppData\Local\ifm"
nssm start IFMNode

Production Bootstrap Node

Bootstrap nodes are critical infrastructure. They only introduce peers — they never see user traffic.

Requirements

ResourceMinimumRecommended
CPU1 vCPU2+ vCPU
RAM512 MB2 GB
Disk1 GB10 GB SSD
Network100 Mbps1 Gbps
IPStatic IPv4Static IPv4 + IPv6
LocationSingle regionMulti-region (anycast)

Hardened Configuration

toml
# /etc/ifm/bootstrap.toml
[node]
name = "bootstrap.ifm.network"
storage = "/var/lib/ifm-bootstrap"
relay = true
connections = 2000       # High connection limit

[network]
bootstrap = false        # Don't bootstrap from others
lan = false              # No local discovery
quic = true
webrtc = false           # Not needed for bootstrap
tcp = true               # Fallback

[network.quic]
port = 4001
congestion_controller = "bbr"
max_concurrent_streams = 1024

[network.tcp]
port = 4001
nodelay = true

[network.dht]
mode = "server"          # Full DHT participation
replication_factor = 20

[gossip]
fanout = 20              # High fanout for bootstrap
mesh_n = 20
mesh_n_high = 40

[logging]
level = "warn"
file = "/var/log/ifm/bootstrap.log"
format = "json"
max_file_size = "100MB"
max_files = 10

[security]
dedup_cache_size = 1000000  # Large dedup cache

Systemd Service (Production)

ini
# /etc/systemd/system/ifm-bootstrap.service
[Unit]
Description=IFM Bootstrap Node
Documentation=https://docs.ifm.network/deployment/bootstrap
After=network-online.target
Wants=network-online.target

[Service]
Type=notify
User=ifm-bootstrap
Group=ifm-bootstrap
ExecStart=/usr/local/bin/ifm bootstrap --config /etc/ifm/bootstrap.toml
Restart=always
RestartSec=10
TimeoutStartSec=60
TimeoutStopSec=30

# Security hardening
NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=strict
ProtectHome=yes
ReadWritePaths=/var/lib/ifm-bootstrap /var/log/ifm
CapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_NET_RAW
AmbientCapabilities=CAP_NET_BIND_SERVICE CAP_NET_RAW

# Resource limits
LimitNOFILE=65536
LimitNPROC=4096
MemoryMax=2G
CPUQuota=200%

# Monitoring
WatchdogSec=30

[Install]
WantedBy=multi-user.target
bash
# Create user
sudo useradd -r -s /bin/false -d /var/lib/ifm-bootstrap ifm-bootstrap

# Create directories
sudo mkdir -p /var/lib/ifm-bootstrap /var/log/ifm
sudo chown ifm-bootstrap:ifm-bootstrap /var/lib/ifm-bootstrap /var/log/ifm

# Install and start
sudo systemctl daemon-reload
sudo systemctl enable --now ifm-bootstrap.service

Firewall Rules

bash
# UFW (Ubuntu/Debian)
sudo ufw allow 4001/udp comment "IFM QUIC"
sudo ufw allow 4001/tcp comment "IFM TCP"
sudo ufw enable

# firewalld (RHEL/Fedora)
sudo firewall-cmd --permanent --add-port=4001/udp
sudo firewall-cmd --permanent --add-port=4001/tcp
sudo firewall-cmd --reload

# iptables (raw)
sudo iptables -A INPUT -p udp --dport 4001 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 4001 -j ACCEPT

Monitoring (Prometheus + Grafana)

yaml
# prometheus.yml scrape config
scrape_configs:
  - job_name: 'ifm-bootstrap'
    static_configs:
      - targets: ['localhost:9090']  # IFM metrics endpoint

Key Metrics to Alert On:

  • ifm_peers_connected < 10 (for bootstrap)
  • ifm_dht_routing_table_size < 100
  • ifm_memory_bytes > 1.5GB
  • ifm_cpu_percent > 80%
  • ifm_network_errors_total increasing

DNS & TLS

bash
# A record for IPv4
bootstrap.ifm.network.  IN  A   1.2.3.4

# AAAA record for IPv6
bootstrap.ifm.network.  IN  AAAA  2001:db8::1

# SRV record for QUIC (optional)
_quic._udp.bootstrap.ifm.network.  IN  SRV  0 0 4001 bootstrap.ifm.network.

Multiaddr for users:

/ip4/1.2.3.4/udp/4001/quic-v1/p2p/12D3KooWBootstrap1
/ip6/2001:db8::1/udp/4001/quic-v1/p2p/12D3KooWBootstrap1
/dns/bootstrap.ifm.network/udp/4001/quic-v1/p2p/12D3KooWBootstrap1

Community Relay Node

Relays help peers behind NATs connect. Lower requirements than bootstrap.

Configuration

toml
# /etc/ifm/relay.toml
[node]
name = "relay.community.ifm"
storage = "/var/lib/ifm-relay"
relay = true
connections = 500

[network]
bootstrap = true
lan = false
quic = true
webrtc = false

[network.quic]
port = 4001

[gossip]
fanout = 8

Deployment

Same as bootstrap but with lower resource limits and relay = true in config.


Headless Server Deployment

For bots, bridges, automated services.

Docker

dockerfile
# Dockerfile
FROM rust:1.75-slim as builder
WORKDIR /app
COPY . .
RUN cargo build --release --bin ifm-cli

FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
    ca-certificates libssl3 && \
    rm -rf /var/lib/apt/lists/*
COPY --from=builder /app/target/release/ifm-cli /usr/local/bin/ifm
USER 1000:1000
ENTRYPOINT ["ifm"]
yaml
# docker-compose.yml for headless bot
version: '3.8'
services:
  ifm-bot:
    image: myorg/ifm-bot:latest
    environment:
      - IFM_CONFIG=/config/bot.toml
      - IFM_DATA_DIR=/data
    volumes:
      - ./bot-config.toml:/config/bot.toml:ro
      - bot-data:/data
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 256M
          cpus: '0.5'

volumes:
  bot-data:

Kubernetes

yaml
# ifm-bot-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ifm-bot
spec:
  replicas: 1
  selector:
    matchLabels:
      app: ifm-bot
  template:
    metadata:
      labels:
        app: ifm-bot
    spec:
      containers:
      - name: ifm-bot
        image: myorg/ifm-bot:latest
        env:
        - name: IFM_CONFIG
          value: /config/bot.toml
        - name: IFM_DATA_DIR
          value: /data
        volumeMounts:
        - name: config
          mountPath: /config
          readOnly: true
        - name: data
          mountPath: /data
        resources:
          requests:
            memory: "128Mi"
            cpu: "100m"
          limits:
            memory: "256Mi"
            cpu: "500m"
        livenessProbe:
          exec:
            command: ["ifm", "stats", "--json"]
          initialDelaySeconds: 30
          periodSeconds: 60
      volumes:
      - name: config
        configMap:
          name: ifm-bot-config
      - name: data
        emptyDir: {}  # Or persistent volume

Mobile Deployment

iOS (Swift)

swift
// IFMBridge.swift
import Foundation
import ifm_ffi  // Generated from uniffi

class IFMManager {
    private var node: OpaquePointer?
    
    func start(config: IFMConfig) async throws {
        node = try await IFMFFI.createNode(config: config)
        try await IFMFFI.connect(node: node!)
    }
    
    func tune(_ frequency: String) async throws {
        try await IFMFFI.tune(node: node!, frequency: frequency)
    }
    
    func broadcast(_ text: String) async throws {
        try await IFMFFI.broadcast(node: node!, text: text)
    }
    
    func onMessage(_ handler: @escaping (IFMMessage) -> Void) {
        IFMFFI.setMessageHandler(node: node!) { msg in
            handler(IFMMessage(from: msg))
        }
    }
}

Android (Kotlin)

kotlin
// IFMManager.kt
class IFMManager(private val context: Context) {
    private var node: Long = 0
    
    suspend fun start(config: IFMConfig) {
        node = IFMNative.createNode(config)
        IFMNative.connect(node)
    }
    
    suspend fun tune(frequency: String) {
        IFMNative.tune(node, frequency)
    }
    
    suspend fun broadcast(text: String) {
        IFMNative.broadcast(node, text)
    }
    
    fun setMessageHandler(handler: (IFMMessage) -> Unit) {
        IFMNative.setMessageHandler(node) { msg ->
            handler(IFMMessage.fromNative(msg))
        }
    }
}

Permissions (Android)

xml
<!-- AndroidManifest.xml -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" />

<service
    android:name=".IFMService"
    android:foregroundServiceType="microphone" />

Browser/WASM Deployment

Build WASM Module

bash
# Install wasm-pack
cargo install wasm-pack

# Build
cd crates/ffi
wasm-pack build --target web --out-dir ../../pkg/web

Web Application

html
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>IFM Web Radio</title>
    <script type="module">
        import init, { IFM } from './pkg/web/ifm_sdk.js';
        
        async function main() {
            await init();
            
            const radio = await IFM.create({
                storage: 'indexeddb',
                name: 'Web User'
            });
            
            await radio.connect();
            await radio.tune('91.700');
            
            radio.on('message', (msg) => {
                console.log('Message:', msg.text);
                addMessage(msg);
            });
            
            // UI handlers
            document.getElementById('send').onclick = async () => {
                const input = document.getElementById('input');
                await radio.broadcast(input.value);
                input.value = '';
            };
        }
        
        main().catch(console.error);
    </script>
</head>
<body>
    <div id="messages"></div>
    <input id="input" placeholder="Type message...">
    <button id="send">Send</button>
</body>
</html>

HTTPS Required

bash
# Development with mkcert
mkcert -install
mkcert localhost 127.0.0.1 ::1

# Serve with HTTPS
npx serve -s . -l 3000 --ssl-cert localhost+2.pem --ssl-key localhost+2-key.pem

Service Worker (Offline Support)

javascript
// sw.js
self.addEventListener('install', (event) => {
    event.waitUntil(
        caches.open('ifm-v1').then((cache) => {
            return cache.addAll([
                '/',
                '/pkg/web/ifm_sdk.js',
                '/pkg/web/ifm_sdk_bg.wasm'
            ]);
        })
    );
});

self.addEventListener('fetch', (event) => {
    event.respondWith(
        caches.match(event.request).then((response) => {
            return response || fetch(event.request);
        })
    );
});

IoT/Embedded Deployment

Raspberry Pi

bash
# Install Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
source ~/.cargo/env

# Build for ARM
rustup target add aarch64-unknown-linux-gnu
cargo build --release --target aarch64-unknown-linux-gnu

# Run
./target/aarch64-unknown-linux-gnu/release/ifm-cli connect

systemd for Pi

ini
# /etc/systemd/system/ifm-sensor.service
[Unit]
Description=IFM Sensor Node
After=network-online.target

[Service]
Type=simple
User=pi
ExecStart=/home/pi/ifm-cli connect --config /home/pi/.config/ifm/sensor.toml
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target

Sensor Config

toml
# sensor.toml
[node]
name = "greenhouse-sensor-01"
storage = "./data"
relay = false
connections = 16

[network]
bootstrap = true
lan = true
quic = true
webrtc = false

[audio]
enabled = false

[plugins]
auto_load = false

Upgrading Nodes

Zero-Downtime Upgrade (Relay/Bootstrap)

bash
# 1. Prepare new binary
cp ifm-new /usr/local/bin/ifm-new

# 2. Test with new binary
/usr/local/bin/ifm-new stats --json

# 3. Swap atomically
mv /usr/local/bin/ifm /usr/local/bin/ifm-old
mv /usr/local/bin/ifm-new /usr/local/bin/ifm

# 4. Restart service
systemctl reload ifm-bootstrap  # Or restart for non-reloadable changes

Configuration Migration

bash
# Validate new config format
ifm config validate --config new-config.toml

# Migrate
ifm config migrate --from old-config.toml --to new-config.toml

Backup & Recovery

Identity Backup

bash
# Backup identity (CRITICAL)
cp ~/.local/share/ifm/identity.key ~/backups/ifm-identity-$(date +%Y%m%d).key
chmod 600 ~/backups/ifm-identity-*.key

# Encrypt backup
gpg --symmetric --cipher-algo AES256 ~/backups/ifm-identity-$(date +%Y%m%d).key

Full Data Backup

bash
#!/bin/bash
# backup-ifm.sh

DATA_DIR="${IFM_DATA_DIR:-~/.local/share/ifm}"
BACKUP_DIR="$HOME/backups/ifm-$(date +%Y%m%d-%H%M%S)"

mkdir -p "$BACKUP_DIR"
rsync -av --exclude='cache/' --exclude='logs/' "$DATA_DIR/" "$BACKUP_DIR/"
tar czf "$BACKUP_DIR.tar.gz" -C "$(dirname "$BACKUP_DIR")" "$(basename "$BACKUP_DIR")"
rm -rf "$BACKUP_DIR"

echo "Backup saved to $BACKUP_DIR.tar.gz"

Restore

bash
# Stop node
systemctl --user stop ifm.service

# Restore identity
cp backup/identity.key ~/.local/share/ifm/identity.key
chmod 600 ~/.local/share/ifm/identity.key

# Restore config
cp backup/config.toml ~/.config/ifm/config.toml

# Start node
systemctl --user start ifm.service

Troubleshooting Deployment

Common Issues

IssueCauseSolution
"Address already in use"Port conflictChange port in config, check ss -tulpn
"Permission denied"File permsFix ownership: chown -R user:user ~/.local/share/ifm
"No peers found"Firewall/NATOpen UDP 4001, enable UPnP, check bootstrap
"Out of memory"Cache too largeReduce node.cache in config
"Too many open files"FD limitIncrease ulimit -n 65536
"Identity corrupted"Disk errorRestore from backup, or regenerate

Debug Commands

bash
# Verbose connection
ifm connect -v

# Check network
ifm stats --json | jq '.network'

# Test specific bootstrap
ifm connect --bootstrap /ip4/1.2.3.4/udp/4001/quic-v1/p2p/12D3KooW...

# Check ports
ss -tulpn | grep 4001

# Check logs
journalctl -u ifm-bootstrap -f
tail -f ~/.local/share/ifm/logs/ifm.log

Security Checklist

  • [ ] Identity key backed up securely (offline, encrypted)
  • [ ] Config file permissions: 640 (root:ifm group)
  • [ ] Data directory permissions: 700 (ifm user)
  • [ ] Firewall: Only required ports open (4001 UDP/TCP)
  • [ ] Systemd: Hardening options enabled
  • [ ] Monitoring: Alerts on peer count, memory, CPU
  • [ ] Updates: Automatic security updates enabled
  • [ ] Logging: No sensitive data in logs
  • [ ] Bootstrap nodes: Multiple, geographically distributed
  • [ ] Relay nodes: Bandwidth limits configured

Scaling Guidelines

NodesBootstrapRelaysConfig Adjustments
< 10010Default
100-1K2-32-5connections=500, cache=512MB
1K-10K5+10+connections=1000, cache=1GB, DHT server mode
10K+10+ (anycast)50+connections=5000, cache=5GB, dedicated hardware

Support

Released under the MIT License.