Why Redeploy Exists
The Problem: Normal process restart/respawn preserves the deployment directory for performance. When you push new code to GitHub:- New tools are added
- Bugs are fixed
- Features are updated
Architecture Context
Per-User Instance Model
DeployStack follows a per-user instance architecture:- Installation: Team-level MCP server record (
mcpServerInstallationstable) - Instance: Per-user running process with merged config (
mcpServerInstancestable) - ProcessId: Unique identifier including user:
{slug}-{team}-{user}-{installation-id}
Shared Deployment Directory
CRITICAL: All user instances for an installation share ONE deployment directory:| Environment | Path | Type |
|---|---|---|
| Production (Linux) | /opt/mcp-deployments/{team-id}/{installation-id} | tmpfs (memory-backed) |
| Development (Mac/Win) | /tmp/mcp-{uuid} | Regular filesystem |
- One installation = one GitHub repository
- Download and build artifacts ONCE
- All user instances execute from the same code
- Reduces disk usage and build time
services/satellite/src/process/github-deployment.ts line 890:
Redeploy vs Restart vs Update
| Action | Deployment Directory | GitHub Download | All Instances | Use Case |
|---|---|---|---|---|
| Restart | ✓ Preserved (fast) | ✗ No | Per user | Process crashed, needs restart with same code |
| Redeploy | ✗ Deleted (fresh) | ✓ Yes | All users | New code pushed to GitHub, need latest version |
| Update Config | ✓ Preserved | ✗ No | All users | Changed args/env vars, same code |
Complete Redeploy Flow
Step 1: User Action (Frontend)
- User clicks “Redeploy” button in DeployStack dashboard
- Frontend sends POST request to backend:
/api/github-deployments/:installationId/redeploy
Step 2: Backend Operations
File:services/backend/src/routes/mcp-servers/github-deployments.ts
-
Fetch Latest SHA from GitHub:
- Uses GitHub App API to get latest commit SHA
- Updates
mcpServers.git_commit_shawith new SHA
mcpServers.git_commit_shawith the new SHA. Thetemplate_argsfield is NOT updated - it continues to store the base GitHub reference without SHA (e.g.,github:owner/repo). This separation allows the satellite to dynamically reconstruct args with the latest SHA during redeploy. -
Update All Instance Statuses:
- Sets ALL user instances to
status: 'restarting' - Query:
UPDATE mcpServerInstances SET status='restarting' WHERE installation_id=...
- Sets ALL user instances to
-
Notify Satellites:
- Calls
satelliteCommandService.notifyMcpRedeploy(installation_id, team_id, user_id) - Creates
configurecommand with priorityimmediate - Command sent to ALL satellites (global deployment)
- Calls
Step 3: Satellite Command Processing
File:services/satellite/src/services/command-processor.ts
When satellite receives the mcp_redeploy command:
-
Route to Handler:
- Checks
payload.event === 'mcp_redeploy' - Routes to
handleMcpRedeploy()method
- Checks
-
Find ALL Instances:
-
Stop ALL Instances:
-
Clear Tool Cache:
-
Trigger Config Refresh:
- Stopping ALL instances ensures no process uses old code
removeServerCompletely()setsisUninstallShutdownflag- Termination handler detects flag and deletes deployment directory
- Config refresh downloads fresh code from GitHub
- ALL instances respawn from new code
Step 4: Process Termination & Cleanup
File:services/satellite/src/lib/termination-handler.ts
For each instance being stopped:
-
Process Termination:
- Send SIGTERM for graceful shutdown
- Wait for timeout (10 seconds)
- Send SIGKILL if needed
-
Deployment Directory Deletion (only if
isUninstallShutdown = true):
- ALL processes stopped
- Shared deployment directory deleted
- Old code completely removed from filesystem
Step 5: Fresh Download & Build
File:services/satellite/src/process/github-deployment.ts
Config refresh detects missing deployment and triggers fresh preparation:
-
Reconstruct Args with New SHA:
- Satellite receives config with
argsWITHOUT SHA:["github:owner/repo"] - Receives
git_commit_shaas separate field:"def456..." - Combines them:
["github:owner/repo#def456..."] - See Dynamic Args Reconstruction
- Satellite receives config with
-
Download Repository:
- Fetches tarball from GitHub using reconstructed SHA reference
- Uses GitHub App installation token for authentication
-
Create New Deployment Directory:
- Production:
mount -t tmpfs -o size=300M tmpfs /opt/mcp-deployments/{team}/{install} - Development:
mkdir /tmp/mcp-{new-uuid}
- Production:
-
Extract Tarball:
- Extracts repository contents to deployment directory
-
Install Dependencies:
- Node.js:
npm install --omit=dev - Python:
uv syncoruv pip install
- Node.js:
-
Run Build (if configured):
- Node.js:
npm run build(ifscripts.buildexists) - Python: No build step typically
- Node.js:
-
Resolve Entry Point:
- Node.js:
binormainfrom package.json - Python: Installed script or standalone file
- Node.js:
Step 6: Respawn ALL Instances
File:services/satellite/src/process/manager.ts
For each user instance:
-
Spawn Process:
- Launches process with transformed config
- Uses NEW entry point from fresh code
- Applies user-specific environment variables
-
Status Updates:
provisioning→ Process startingconnecting→ Establishing MCP connectiondiscovering_tools→ Calling tools/listsyncing_tools→ Syncing to backendonline→ Ready for use
-
Tool Discovery:
- Discovers NEW tools from fresh code
- Updates tool cache
- Syncs to backend database
- All team members have NEW code
- New/updated tools are available
- Old tools (if removed) are gone
What Gets Deleted on Redeploy
Always Deleted
| Item | Location | Why |
|---|---|---|
| Deployment directory | /opt/mcp-deployments/{team}/{install} or /tmp/mcp-{uuid} | Forces fresh code download |
| Installed dependencies | node_modules/ or .venv/ | Ensures dependency updates |
| Build artifacts | dist/, compiled files | Forces rebuild with new code |
| Tool cache | In-memory StdioToolDiscoveryManager | Ensures new tools discovered |
Never Deleted
| Item | Location | Why |
|---|---|---|
| Runtime cache | /var/cache/deploystack/npm or /var/cache/deploystack/uv | Shared across all installations |
| Instance records | Database mcpServerInstances | Preserves instance metadata |
| User configurations | Database (env vars, args) | User settings preserved |
Performance Impact
| Operation | Duration | Reason |
|---|---|---|
| Initial Deployment | 20-60 seconds | Download + install + build |
| Normal Restart | 1-2 seconds | Reuses cached deployment |
| Redeploy | 20-60 seconds | Same as initial deployment |
Logs During Redeploy
Backend Logs
Satellite Logs
Command received:Verification Steps
How to Verify Redeploy Worked
-
Before Redeploy:
- Check deployment directory:
ls -la /opt/mcp-deployments/{team}/{install}/ - Note the directory modification time
- Check tool list:
GET /api/mcp-tools?installation_id=...
- Check deployment directory:
-
Push New Code:
- Add a new tool to your MCP server
- Commit and push to GitHub:
git push origin main
-
Trigger Redeploy:
- Click “Redeploy” button in DeployStack dashboard
- Wait for status to progress:
restarting→online
-
Verify:
- Check satellite logs for “GitHub deployment directory deleted”
- Check satellite logs for “Downloading repository from GitHub”
- Verify new tool appears:
GET /api/mcp-tools?installation_id=... - Check directory modification time (should be recent)
- Test new tool:
POST /api/mcp-proxy/execute
Debugging Failed Redeployments
If redeploy fails:-
Check Backend Logs:
- Look for GitHub API errors (rate limits, auth failures)
- Verify commit SHA was updated in database
-
Check Satellite Logs:
- Search for
mcp_redeploy_failedoperation - Check for tmpfs unmount errors
- Look for download/install/build failures
- Search for
-
Check Instance Status:
- Query:
SELECT status, status_message FROM mcpServerInstances WHERE installation_id=... - Look for error status:
failed,error,requires_reauth
- Query:
-
Common Issues:
- GitHub token expired: Re-authorize GitHub App
- Build script failed: Check build logs, validate dependencies
- Quota exceeded: Deployment too large (>300MB)
- Process still running: Old process didn’t terminate properly
Edge Cases
Multiple Users Redeploying Simultaneously
Scenario: Two team members click “Redeploy” at the same time. Behavior:- Backend processes requests sequentially (database lock)
- Only one
mcp_redeploycommand created - Satellite processes command once
- Both users see the same result
Redeploy During Active Requests
Scenario: Users have active MCP requests when redeploy is triggered. Behavior:- Active processes receive SIGTERM
- Processes finish current requests (graceful shutdown)
- After 10 seconds, SIGKILL if not terminated
- New processes spawn with fresh code
- Clients reconnect automatically (if using persistent connections)
Redeploy with Dormant Instances
Scenario: Some users’ instances are dormant (idle timeout), others are active. Behavior:removeServerCompletely()handles both:- Active: Terminates process, deletes directory
- Dormant: Clears dormant config (no directory exists yet)
- Deployment directory deleted once (shared)
- ALL instances respawn from fresh code
Failed Download After Deletion
Scenario: Directory deleted successfully, but GitHub download fails. Behavior:- Directory deletion succeeds
- GitHub download fails (rate limit, network error)
- Installation status set to
failed - Next config refresh retries download
- Eventually succeeds or requires user intervention
HTTP/SSE Servers (Non-GitHub)
For HTTP/SSE servers, redeploy triggers tool re-discovery instead of code download:- HTTP/SSE servers are externally hosted
- No deployment directory to manage
- Redeploy = “refresh tool list from remote server”
Related Documentation
- GitHub Deployment - Complete deployment flow
- Instance Lifecycle - Per-user instance management
- Satellite Commands - Command types and payloads
- Process Management - Process lifecycle and termination
- Idle Process Management - Dormant state handling
- Backend Events - Status tracking and event emission

