Skip to main content
This guide covers deploying a production-ready DeployStack Satellite with nsjail process isolation for secure multi-team environments. For development or single-team deployments, see the Quick Start guide.
When to use this guide:
  • Production deployments serving multiple teams
  • Enterprise environments with strict security requirements
  • Shared infrastructure where teams need complete isolation
  • Multi-tenant satellite deployments
For development or single-team usage, the Docker Compose setup is simpler and sufficient.

Overview

Production satellites provide enterprise-grade security through:
  • nsjail Process Isolation: Complete process separation per team with Linux namespaces and cgroup enforcement
  • Resource Limits: CPU, memory, and process limits per MCP server (virtual RAM unlimited via rlimit, 512MB physical RAM via cgroup when enabled, 60s CPU, 1000 processes)
  • Multi-Runtime Support: Node.js (npx) and Python (uvx) with runtime-aware isolation
  • Filesystem Jailing: Read-only system directories, isolated writable spaces per runtime
  • Non-Root Execution: Satellite runs as dedicated deploystack user
  • Audit Logging: Complete activity tracking with automatic rotation

Prerequisites

System Requirements

  • Operating System: Debian 13 (Trixie) - required for nsjail compatibility
  • RAM: Minimum 4GB (8GB+ recommended for multiple teams)
  • Storage: 20GB+ available disk space
  • Network: Outbound HTTPS access to DeployStack Backend
  • Access: Root/sudo access for initial setup

Required Knowledge

  • Linux system administration
  • systemd service management
  • Basic networking and firewall configuration

Installation Process

The installation follows a two-phase approach:
  1. System Setup: Install Node.js, nsjail, configure kernel (run once per server)
  2. Satellite Installation: Build satellite, configure, create systemd service
1

System Setup

Install system dependencies and configure the server for satellite operation.
2

Satellite Installation

Build the satellite service and configure it for your environment.
3

Service Configuration

Create systemd service and start the satellite.
4

Verification

Confirm the satellite is running correctly and registered with the backend.

Phase 1: System Setup

Install Node.js 24

DeployStack Satellite requires Node.js 24 for compatibility with the latest MCP protocol features.

Install Python and UV

DeployStack Satellite supports Python MCP servers via uvx (UV package runner).
Python Runtime Support: The satellite automatically detects Python MCP servers and spawns them using uvx with runtime-aware isolation. Python and Node.js servers run in separate cache directories for complete isolation.

Install nsjail

nsjail provides the process isolation that enables secure multi-team satellite operation.
Why nsjail? nsjail uses Linux namespaces and cgroups to create completely isolated environments for each team’s MCP servers. This prevents teams from accessing each other’s data or interfering with other processes.

Configure Kernel for User Namespaces

nsjail requires unprivileged user namespaces to be enabled at the kernel level.
Important: This kernel setting is required for nsjail to function. Without it, all MCP server spawns will fail. The setting persists across reboots via the sysctl configuration file.

Create Service User

Create a dedicated non-root user for running the satellite service.

Set Up Logging Infrastructure

Configure log directories and rotation for the satellite service.
Log Rotation: Logs rotate daily and retain 7 days of history by default. Adjust the rotate value in the logrotate configuration if you need longer retention.

Phase 2: Satellite Installation

Clone or Copy Satellite Code

Install Dependencies and Build

Create MCP Cache Directory

Runtime-Aware Caching: The satellite automatically creates runtime-specific cache directories:
  • /opt/deploystack/mcp-cache/node/{team_id} - Node.js packages (npm)
  • /opt/deploystack/mcp-cache/python/{team_id} - Python packages (UV)
This ensures complete isolation between different runtimes and teams.

Create GitHub Deployment Base Directory

Critical Requirement: This directory is required for GitHub-based MCP server installations. Without it, the satellite will fail to start in production mode with a clear error message:
Why this directory is needed: When users install MCP servers directly from GitHub repositories (e.g., github:owner/repo#ref), the satellite:
  1. Downloads the GitHub tarball
  2. Creates a tmpfs mount at /opt/mcp-deployments/{team_id}/{installation_id}
  3. Extracts the code into the tmpfs mount (300MB size limit)
  4. Builds and runs the MCP server in isolated memory
This approach provides secure, isolated execution for GitHub-sourced MCP servers without polluting the filesystem.

Configure Environment

Create the .env file with your production configuration.
Registration Token: You must generate this token from your DeployStack admin interface before proceeding. Navigate to Admin → Satellites → Pairing to generate a global satellite token.
Satellite Name Requirements:
  • Length: 10-32 characters
  • Characters: lowercase letters (a-z), numbers (0-9), hyphens (-), underscores (_)
  • No spaces or uppercase letters
  • Must be unique across your DeployStack deployment

Create Systemd Service

Exit back to root/sudo user to create the systemd service.
Security Features: The systemd service runs with several security hardening options:
  • NoNewPrivileges: Prevents privilege escalation
  • PrivateTmp: Isolated /tmp directory
  • ProtectSystem: Read-only system directories
  • ProtectHome: Restricted home directory access
  • ReadWritePaths: Only specific directories are writable

Start the Service

Verification

Check Service Status

Verify Port Listening

Check Registration

Look for successful registration in the logs:
You should see:

Verify in Admin Interface

  1. Log in to your DeployStack admin interface
  2. Navigate to Admin → Satellites
  3. Confirm your satellite appears with status “Active”
  4. Check last heartbeat timestamp is recent

Service Management

Common Commands

Updating the Satellite

Security Considerations

nsjail Isolation

Production satellites use nsjail to provide:
  • PID Namespace Isolation: Each team’s MCP servers run in separate process trees
  • Mount Namespace Isolation: Isolated filesystem view per team
  • IPC Namespace Isolation: Separate inter-process communication
  • UTS Namespace Isolation: Each team gets unique hostname (mcp-)

Resource Limits

Each MCP server process is limited to:
  • Virtual Memory: unlimited (rlimit_as = inf — required because Node.js v24 uses WASM internally which reserves ~10GB of virtual address space; this is virtual, not physical RAM)
  • Physical Memory: 512MB via cgroup (only active when Delegate=yes is set in the systemd unit — see below)
  • CPU Time: 60 seconds (enforced via rlimit_cpu)
  • Processes: 1000 (enforced via rlimit_nproc and cgroup pids.max, required for package managers like npm and uvx)
  • File Descriptors: 1024 (enforced via rlimit_nofile)
  • Maximum File Size: 50MB (enforced via rlimit_fsize)
  • tmpfs /tmp: 100MB (enforced via tmpfs mount)
Cgroup limits are auto-detected: The satellite automatically detects whether cgroup v2 is available and delegated. When running as a systemd service with Delegate=yes, physical memory (512MB) and PID limits are enforced via cgroup in addition to rlimits. Without Delegate=yes, the satellite falls back to rlimit-only mode — nsjail still runs safely with full namespace isolation. See the Enable Cgroup Limits section below to activate precise physical memory enforcement.
Primary Security = Namespace Isolation: The satellite’s security model relies on Linux namespaces (PID, Mount, User, IPC, UTS) to isolate MCP servers from each other and the host system. Resource limits (rlimits) provide secondary DoS protection. With user namespace active, all privilege escalation attacks (including setuid-based rlimit bypasses) are prevented.

Network Security

Configure firewall rules for production:

Troubleshooting

Service Won’t Start

Check logs for errors:
Common issues:
  • Missing registration token in .env
  • Invalid satellite name format
  • Backend URL unreachable
  • Port 3001 already in use

nsjail Spawning Failures

Symptoms:
  • MCP servers fail to spawn
  • Errors mentioning “clone” or “namespace”
Check kernel setting:
Verify nsjail installation:

Registration Fails

Check registration token:
Common registration issues:
  • Token expired (global tokens expire after 1 hour)
  • Token already used (tokens are single-use)
  • Backend URL incorrect or unreachable
  • Network connectivity issues
Test backend connectivity:

High Memory Usage

Check process memory:
Adjust idle timeout to terminate unused processes faster:

Port Already in Use

Find what’s using port 3001:
Change satellite port:

Monitoring and Maintenance

Log Management

View current logs:
Check log disk usage:
Manual log cleanup:

Health Monitoring

Set up automated health checks:

Performance Monitoring

Monitor satellite performance metrics:

Production Best Practices

Backup Configuration

Regularly backup your satellite configuration:

Update Strategy

  1. Test updates in staging environment first
  2. Schedule maintenance windows for updates
  3. Keep backup of previous working version
  4. Monitor logs closely after updates

Security Auditing

Regularly review:
  • Systemd service permissions
  • Log file permissions
  • Environment file security (.env should be 600)
  • User and group ownership

Capacity Planning

Monitor and plan for:
  • Number of active MCP server processes
  • Memory usage per team
  • Log disk usage growth
  • Network bandwidth for backend communication

Enable Cgroup Limits

By default the satellite runs in rlimit-only mode. Adding Delegate=yes to the systemd unit gives the satellite ownership of its cgroup subtree, which activates precise physical memory (512MB) and PID enforcement per MCP process. No code changes are needed — the satellite auto-detects cgroup availability at startup.

1. Modify Systemd Service File

Edit /etc/systemd/system/deploystack-satellite.service and add Delegate=yes:

2. Reload and Restart Service

3. Verify Cgroup Limits Are Active

Check the startup log for confirmation:
You should see a line like:
If you see Cgroup v2 unavailable instead, verify that Delegate=yes is in the service file and that you reloaded systemd. You can also check active limits on a running MCP process:
Cgroup limits are optional. The rlimit-only default provides strong security through namespace isolation and adequate DoS protection. Cgroup limits add precise physical memory enforcement per MCP process, which is useful in high-density multi-team environments where a single runaway process consuming all RAM would otherwise affect other teams.

Next Steps

MCP Server Configuration

Configure MCP servers for your teams

Team Management

Set up teams and access control

Satellite Monitoring

Monitor satellite health and usage

API Documentation

Explore the satellite API

Need help? Join our Discord community or check GitHub Issues for support.