Skip to content

Deployment

This guide covers deploying DataMgmt Node in production environments.

Deployment Options

The repo ships a production-ready, multi-stage Dockerfile at the project root. It:

  1. Builds the Vue 3 / Vite website from web/ (stage website-builder)
  2. Installs the Python runtime + nginx + supervisor on python:3.11-slim
  3. Copies the node source, contracts, and plugins under /app
  4. Runs nginx (port 80, fronting both the static site and the node) and python -m datamgmtnode.main together under supervisord (see deploy/supervisord.conf)
docker build -t datamgmtnode .
docker run -d -p 80:80 \
  -e KEY_MASTER_PASSWORD="strong-secret" \
  -e BLOCKCHAIN_URL="https://mainnet.infura.io/v3/YOUR-PROJECT-ID" \
  -e PRIVATE_KEY="0x..." \
  -e NODE_ID="prod-node-1" \
  datamgmtnode

The container's healthcheck pings /health, which nginx proxies to the Dashboard API on port 8082 (see deploy/nginx.conf).

CapRover

A captain-definition is included at the repo root, pointing at the Dockerfile. Deploying via CapRover requires no extra Docker config.

Custom Dockerfile

If you want a slimmer image without the website or nginx layer, the minimum required is:

FROM python:3.11-slim

WORKDIR /app

RUN apt-get update && apt-get install -y \
    libleveldb-dev build-essential \
    && rm -rf /var/lib/apt/lists/*

RUN pip install poetry

COPY pyproject.toml poetry.lock ./
RUN poetry config virtualenvs.create false \
    && poetry install --no-interaction --no-ansi --only main

COPY datamgmtnode/ ./datamgmtnode/
COPY contracts/ ./contracts/
COPY plugins/ ./plugins/

RUN mkdir -p /data

ENV DATA_DIR=/data
ENV DB_PATH=/data/nodedb
ENV SQLITE_DB_PATH=/data/sqlite.db

EXPOSE 8080 8081 8082 8000

CMD ["python", "datamgmtnode/main.py"]

Create a docker-compose.yml:

version: '3.8'

services:
  datamgmt-node:
    build: .
    ports:
      - "8080:8080"  # Internal API (localhost-bound by default; expose carefully)
      - "8081:8081"  # External API
      - "8082:8082"  # Dashboard API (WebSocket + web/TUI)
      - "8000:8000"  # P2P
    volumes:
      - node-data:/data
    environment:
      - KEY_MASTER_PASSWORD=${KEY_MASTER_PASSWORD}
      - BLOCKCHAIN_URL=${BLOCKCHAIN_URL}
      - PRIVATE_KEY=${PRIVATE_KEY}
      - NODE_ID=${NODE_ID}
      - INITIAL_PEERS=${INITIAL_PEERS}
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 10s

volumes:
  node-data:

Deploy:

# Set environment variables
export KEY_MASTER_PASSWORD="your-secure-password"
export BLOCKCHAIN_URL="https://mainnet.infura.io/v3/YOUR-PROJECT-ID"
export PRIVATE_KEY="0x..."
export NODE_ID="prod-node-1"
export INITIAL_PEERS="http://bootstrap1.example.com:8000"

# Deploy
docker-compose up -d

Kubernetes

Create Kubernetes manifests:

# namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: datamgmt

---
# secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: datamgmt-secrets
  namespace: datamgmt
type: Opaque
stringData:
  KEY_MASTER_PASSWORD: "your-secure-password"
  PRIVATE_KEY: "0x..."

---
# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: datamgmt-config
  namespace: datamgmt
data:
  BLOCKCHAIN_URL: "https://mainnet.infura.io/v3/YOUR-PROJECT-ID"
  NODE_ID: "k8s-node-1"
  P2P_PORT: "8000"
  INITIAL_PEERS: "http://bootstrap1.example.com:8000"

---
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: datamgmt-node
  namespace: datamgmt
spec:
  replicas: 1
  selector:
    matchLabels:
      app: datamgmt-node
  template:
    metadata:
      labels:
        app: datamgmt-node
    spec:
      containers:
      - name: datamgmt-node
        image: datamgmt-node:latest
        ports:
        - containerPort: 8080
          name: internal-api
        - containerPort: 8081
          name: external-api
        - containerPort: 8000
          name: p2p
        envFrom:
        - configMapRef:
            name: datamgmt-config
        - secretRef:
            name: datamgmt-secrets
        volumeMounts:
        - name: data
          mountPath: /data
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 10
          periodSeconds: 30
        readinessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 10
        resources:
          requests:
            memory: "512Mi"
            cpu: "250m"
          limits:
            memory: "2Gi"
            cpu: "1000m"
      volumes:
      - name: data
        persistentVolumeClaim:
          claimName: datamgmt-pvc

---
# service.yaml
apiVersion: v1
kind: Service
metadata:
  name: datamgmt-node
  namespace: datamgmt
spec:
  selector:
    app: datamgmt-node
  ports:
  - name: internal-api
    port: 8080
    targetPort: 8080
  - name: external-api
    port: 8081
    targetPort: 8081
  - name: p2p
    port: 8000
    targetPort: 8000

Deploy:

kubectl apply -f namespace.yaml
kubectl apply -f secret.yaml
kubectl apply -f configmap.yaml
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml

Systemd (Linux)

Create a systemd service:

# /etc/systemd/system/datamgmt-node.service
[Unit]
Description=DataMgmt Node
After=network.target

[Service]
Type=simple
User=datamgmt
Group=datamgmt
WorkingDirectory=/opt/datamgmt
Environment="PATH=/opt/datamgmt/.venv/bin"
EnvironmentFile=/opt/datamgmt/.env
ExecStart=/opt/datamgmt/.venv/bin/python datamgmtnode/main.py
Restart=always
RestartSec=10

# Security hardening
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
ReadWritePaths=/opt/datamgmt/data
PrivateTmp=yes

[Install]
WantedBy=multi-user.target

Enable and start:

sudo systemctl daemon-reload
sudo systemctl enable datamgmt-node
sudo systemctl start datamgmt-node

Production Checklist

Security

  • [ ] Strong KEY_MASTER_PASSWORD set
  • [ ] Private key secured (not in version control)
  • [ ] Firewall configured (only expose necessary ports)
  • [ ] HTTPS reverse proxy configured
  • [ ] API keys configured for external access
  • [ ] Rate limiting enabled

Networking

  • [ ] P2P port accessible from internet (if public node)
  • [ ] Internal API bound to localhost only
  • [ ] External API behind reverse proxy
  • [ ] SSL/TLS certificates configured

Storage

  • [ ] Persistent storage configured
  • [ ] Backup strategy in place
  • [ ] Disk space monitoring enabled

Monitoring

  • [ ] Health checks configured
  • [ ] Logging to centralized system
  • [ ] Metrics collection enabled
  • [ ] Alerts configured

High Availability

  • [ ] Multiple nodes deployed
  • [ ] Load balancer configured
  • [ ] Database replication (if applicable)

Reverse Proxy Configuration

Nginx

upstream datamgmt_external {
    server 127.0.0.1:8081;
}

server {
    listen 443 ssl http2;
    server_name api.datamgmt.example.com;

    ssl_certificate /etc/ssl/certs/datamgmt.crt;
    ssl_certificate_key /etc/ssl/private/datamgmt.key;

    # Rate limiting
    limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;

    location / {
        limit_req zone=api burst=20 nodelay;

        proxy_pass http://datamgmt_external;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    location /health {
        # No rate limiting for health checks
        proxy_pass http://datamgmt_external;
    }
}

Traefik

# traefik.yml
http:
  routers:
    datamgmt-api:
      rule: "Host(`api.datamgmt.example.com`)"
      service: datamgmt-api
      tls:
        certResolver: letsencrypt
      middlewares:
        - rate-limit

  services:
    datamgmt-api:
      loadBalancer:
        servers:
          - url: "http://127.0.0.1:8081"

  middlewares:
    rate-limit:
      rateLimit:
        average: 10
        burst: 20

Scaling

Horizontal Scaling

Deploy multiple nodes with shared bootstrap configuration:

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

services:
  node1:
    build: .
    environment:
      - NODE_ID=node-1
      - P2P_PORT=8000
      - INITIAL_PEERS=http://node2:8000,http://node3:8000

  node2:
    build: .
    environment:
      - NODE_ID=node-2
      - P2P_PORT=8000
      - INITIAL_PEERS=http://node1:8000,http://node3:8000

  node3:
    build: .
    environment:
      - NODE_ID=node-3
      - P2P_PORT=8000
      - INITIAL_PEERS=http://node1:8000,http://node2:8000

Load Balancing

Use a load balancer for API traffic:

upstream datamgmt_cluster {
    least_conn;
    server node1:8081;
    server node2:8081;
    server node3:8081;
}

Next Steps