Skip to content

Deploying CNCjs

CNCjs is a full-featured web-based interface for CNC controllers running Grbl, Marlin, Smoothieware, or TinyG firmware. Built with Node.js and JavaScript, CNCjs provides a responsive web interface for controlling CNC milling machines, allowing you to operate your CNC equipment from any device with a web browser. Whether you’re running a small hobby CNC, a production milling machine, or a Raspberry Pi-based system, CNCjs delivers comprehensive control capabilities with intuitive user experience.

The platform supports multiple CNC controller types including Grbl, Grbl-Mega, Marlin, Smoothieware, TinyG, and g2core, making it compatible with a wide range of CNC machines and hobby projects. CNCjs runs on Linux, macOS, Windows, and Raspberry Pi devices, connecting to your CNC controller via USB serial port, Bluetooth serial module, or wireless connections. With features like 3D visualization, tool path preview, real-time position monitoring, custom widgets, and pendant support, CNCjs provides professional-grade CNC control software.

Key Features

  • Multi-Controller Support: Compatible with Grbl, Grbl-Mega, Marlin, Smoothieware, TinyG, and g2core firmware
  • 3D Tool Path Visualization: Preview G-code tool paths before execution with 3D visualization
  • Responsive Web Interface: Works on desktop browsers, tablets, and mobile devices
  • 6-Axis Digital Readout (DRO): Real-time position display and monitoring
  • Multiple Simultaneous Connections: Allow multiple clients to connect to the same serial port
  • Customizable Workspace: Personalize the interface with custom layouts and widgets
  • Custom Widgets: Extend functionality with custom widget development
  • G-Code Editor: Built-in editor for G-code files with syntax highlighting
  • File Upload: Upload and manage G-code files directly from the interface
  • Tool Change Support: Automated or manual tool change workflows
  • Z-Probe: Automatic workpiece height detection and zeroing
  • Keyboard Shortcuts: Comprehensive keyboard shortcuts for rapid operation
  • Contour ShuttleXpress: Hardware pendant support for Contour design devices
  • Commands: Programmable commands for custom macros and operations
  • Events: Trigger custom actions based on CNC events
  • Multi-Language Support: Interface available in multiple languages (Chinese, Czech, Dutch, English, French, German, Hungarian, Italian, Japanese, Portuguese, Russian, and more)
  • Watch Directory: Automatically load G-code files from monitored directories
  • Pendant Support: Hardware pendant control via USB, Bluetooth, or GPIO
  • Desktop Application: Native desktop apps available for Linux, macOS, and Windows
  • Access Control: User authentication and permission management
  • Configuration File: Persistent configuration with .cncrc JSON file
  • Remote Access: Enable remote access for off-site CNC operation
  • Console Output: Real-time console for monitoring CNC controller communication

Prerequisites

To deploy CNCjs on Klutch.sh, ensure you have the following:

  • An active Klutch.sh account with access to the dashboard at klutch.sh/app
  • A GitHub repository for version control
  • Node.js 14 or higher runtime environment (provided by Klutch.sh Docker image)
  • Understanding of CNC terminology and G-code concepts
  • Knowledge of serial port communication and USB device configuration
  • Familiarity with Docker and containerization
  • CNC machine with compatible controller (Grbl, Marlin, Smoothieware, TinyG, or g2core)

Important Considerations

Deployment Steps

  1. Create the Dockerfile

    Create a Dockerfile in the root directory of your repository:

    FROM node:18-alpine
    # Install build dependencies for node-serialport
    RUN apk add --no-cache \
    python3 \
    make \
    g++ \
    udev
    # Set working directory
    WORKDIR /app
    # Install CNCjs globally
    RUN npm install -g cncjs@latest
    # Create non-root user for running CNCjs
    RUN addgroup -g 1000 cncjs && \
    adduser -D -u 1000 -G cncjs cncjs
    # Create necessary directories
    RUN mkdir -p /home/cncjs/.cncrc.d && \
    mkdir -p /app/gcode && \
    chown -R cncjs:cncjs /home/cncjs && \
    chown -R cncjs:cncjs /app
    # Switch to non-root user
    USER cncjs
    # Expose CNCjs default port
    EXPOSE 8000
    # Health check
    HEALTHCHECK --interval=30s --timeout=10s --start-period=15s --retries=3 \
    CMD wget --quiet --tries=1 --spider http://localhost:8000 || exit 1
    # Start CNCjs server
    CMD ["cncjs", "--host", "0.0.0.0", "--port", "8000", "--allow-remote-access"]

    This Dockerfile creates a Node.js-based CNCjs environment with proper user permissions and port configuration.

  2. Create CNCjs Configuration File

    Create a .cncrc configuration file for CNCjs settings:

    {
    "watchDirectory": "/app/gcode",
    "accessTokenLifetime": "30d",
    "allowRemoteAccess": true,
    "baudrates": [115200, 250000, 230400, 57600, 38400, 19200, 9600],
    "state": {
    "checkForUpdates": true,
    "controller": {
    "exception": {
    "ignoreErrors": false
    }
    }
    },
    "commands": [
    {
    "title": "Check CNCjs Version",
    "commands": "cncjs -V"
    },
    {
    "title": "Restart Server",
    "commands": "pkill -f cncjs; sleep 2; cncjs --allow-remote-access"
    }
    ],
    "events": [],
    "macros": [
    {
    "id": "home-all",
    "title": "Home All Axes",
    "commands": "$H"
    },
    {
    "id": "unlock",
    "title": "Unlock Machine",
    "commands": "$X"
    },
    {
    "id": "probe-z",
    "title": "Probe Z Axis",
    "commands": "G38.2 Z-50 F10"
    }
    ],
    "users": []
    }
  3. Create Environment Configuration File

    Create an .env.example file with environment variables:

    # CNCjs Server Configuration
    CNCJS_PORT=8000
    CNCJS_HOST=0.0.0.0
    # Network and Access
    CNCJS_ALLOW_REMOTE_ACCESS=true
    CNCJS_ACCESS_TOKEN_LIFETIME=30d
    # Serial Port Configuration
    CNCJS_BAUDRATE=115200
    CNCJS_DATABITS=8
    CNCJS_STOPBITS=1
    CNCJS_PARITY=none
    # Controller Configuration
    CNCJS_CONTROLLER_TYPE=Grbl
    # G-Code Watch Directory
    CNCJS_WATCH_DIRECTORY=/app/gcode
    # Logging
    CNCJS_LOG_LEVEL=info
    # Mount Points (for custom widgets and pendants)
    CNCJS_MOUNT_WIDGET=/widget:https://cncjs.github.io/cncjs-widget-boilerplate/v1/
    CNCJS_MOUNT_PENDANT=/pendant:/app/pendant
    # Security
    CNCJS_ENABLE_AUTH=false
    # Database Configuration (if using persistent user database)
    CNCJS_DB_PATH=/home/cncjs/.cncrc
  4. Create Entrypoint Script

    Create an entrypoint.sh file for initialization:

    #!/bin/sh
    set -e
    echo "Starting CNCjs initialization..."
    # Create necessary directories
    mkdir -p /home/cncjs/.cncrc.d
    mkdir -p /app/gcode
    # Copy default configuration if not exists
    if [ ! -f /home/cncjs/.cncrc ]; then
    if [ -f /app/.cncrc ]; then
    cp /app/.cncrc /home/cncjs/.cncrc
    echo "Default configuration file created"
    fi
    fi
    # Ensure correct permissions
    chmod 755 /app/gcode || true
    echo "Starting CNCjs server..."
    # Start CNCjs with parameters
    exec cncjs \
    --host "0.0.0.0" \
    --port "${CNCJS_PORT:-8000}" \
    --watch-directory "/app/gcode" \
    --allow-remote-access \
    --access-token-lifetime "${CNCJS_ACCESS_TOKEN_LIFETIME:-30d}" \
    "$@"

    Update the Dockerfile CMD to use this script if custom initialization is needed.

  5. Create a .gitignore File

    Create a .gitignore file to exclude sensitive files:

    # Environment variables
    .env
    .env.local
    .env.*.local
    # CNCjs configuration
    .cncrc
    config.json
    # G-Code files
    *.gcode
    *.nc
    *.ngc
    gcode/
    # Node modules
    node_modules/
    npm-debug.log
    yarn-error.log
    # IDE and editor files
    .vscode/
    .idea/
    *.swp
    *.swo
    *~
    # OS files
    .DS_Store
    Thumbs.db
    # Logs
    *.log
    logs/
    # Build artifacts
    dist/
    build/
  6. Push to GitHub

    Initialize and push your repository:

    Terminal window
    git init
    git add Dockerfile entrypoint.sh .cncrc .env.example .gitignore
    git commit -m "Initial CNCjs deployment setup"
    git branch -M main
    git remote add origin https://github.com/YOUR_USERNAME/YOUR_REPOSITORY.git
    git push -u origin main

    Replace YOUR_USERNAME and YOUR_REPOSITORY with your actual GitHub credentials.

  7. Deploy on Klutch.sh

    1. Log in to your Klutch.sh dashboard at klutch.sh/app
    2. Click Create App and select GitHub
    3. Connect your GitHub account and select your CNCjs repository
    4. Configure deployment settings:
      • App Name: cncjs-controller (or your preferred name)
      • Branch: Select main
    5. Add environment variables:
      • CNCJS_PORT: 8000
      • CNCJS_ALLOW_REMOTE_ACCESS: true
      • CNCJS_WATCH_DIRECTORY: /app/gcode
      • CNCJS_ACCESS_TOKEN_LIFETIME: 30d
      • Other variables from .env.example as needed
    6. Click Deploy and wait for deployment to complete

    Klutch.sh automatically detects the Dockerfile and uses it for building and deploying your CNCjs instance.

  8. Attach Persistent Storage (Optional)

    If you want to store G-code files persistently:

    1. Navigate to your deployed CNCjs app
    2. Go to Storage or Volumes section
    3. Click Add Persistent Volume
    4. Configure the volume:
      • Mount Path: /app/gcode
      • Size: 10GB (adjust based on your G-code library)
    5. Click Add Volume and restart the container

    This allows you to store and manage your G-code file library persistently.

  9. Configure Network Traffic

    Set up HTTP traffic for your CNCjs web interface:

    1. In your app settings, navigate to Network section
    2. Set Traffic Type to HTTP
    3. Internal port should be set to 8000 (CNCjs default)
    4. Your CNCjs interface will be accessible at https://example-app.klutch.sh

Initial Setup and Configuration

After your CNCjs deployment is running, follow these steps to configure it:

Access the Web Interface

  1. Open your browser and navigate to https://example-app.klutch.sh
  2. You should see the CNCjs dashboard with an empty workspace
  3. The interface is ready for configuration

Configure Serial Port Connection

  1. Look for the Ports section in the left sidebar
  2. If your CNC machine is connected via serial port, it should appear in the ports list
  3. Select your CNC controller port from the dropdown
  4. Verify the correct baud rate is selected (typically 115200 for Grbl)
  5. Click Connect to establish the connection

Set Controller Type

  1. Go to Settings in the workspace
  2. Select your CNC controller type:
    • Grbl: For Grbl and Grbl-Mega controllers
    • Marlin: For Marlin-based 3D printers and CNC machines
    • Smoothieware: For Smoothieware controllers
    • TinyG: For TinyG and g2core controllers
  3. Save the controller type selection

Create a User Account

  1. Go to My Account section
  2. Set up your user profile with username and password
  3. Configure any user-specific preferences
  4. Save account settings

Upload G-Code Files

  1. Click the GCode widget in the sidebar
  2. Use the file browser to select and upload G-code files
  3. Uploaded files appear in your file library
  4. Files are stored in the watch directory for persistence

Verify CNC Machine Connection

  1. After connecting to your CNC controller, you should see:
    • Machine status (Idle, Running, Hold, Alarm)
    • Current position (X, Y, Z coordinates)
    • Real-time feed rate and spindle speed
  2. Test the connection by sending a simple command like $H (Home)
  3. Verify the machine responds correctly

Environment Variables

Basic Configuration

CNCJS_PORT=8000
CNCJS_HOST=0.0.0.0
CNCJS_ALLOW_REMOTE_ACCESS=true
CNCJS_WATCH_DIRECTORY=/app/gcode
CNCJS_BAUDRATE=115200

These variables control the basic CNCjs server configuration and connection parameters.

Production Configuration

For production deployments, use these environment variables with Nixpacks customization:

# Advanced Production Configuration
# Server Configuration
CNCJS_PORT=8000
CNCJS_HOST=0.0.0.0
CNCJS_BACKLOG=511
# Security and Access Control
CNCJS_ALLOW_REMOTE_ACCESS=true
CNCJS_ACCESS_TOKEN_LIFETIME=30d
CNCJS_ENABLE_AUTH=true
# Serial Port Configuration
CNCJS_BAUDRATE=115200
CNCJS_DATABITS=8
CNCJS_STOPBITS=1
CNCJS_PARITY=none
# CNC Controller Configuration
CNCJS_CONTROLLER_TYPE=Grbl
CNCJS_CONTROLLER_BAUDRATE=115200
# Watch Directory Configuration
CNCJS_WATCH_DIRECTORY=/app/gcode
# Mount Points for Widgets and Pendants
CNCJS_MOUNT_WIDGET=/widget:https://cncjs.github.io/cncjs-widget-boilerplate/v1/
CNCJS_MOUNT_PENDANT=/pendant:/app/pendant
CNCJS_MOUNT_TINYWEB=/tinyweb:/app/tinyweb
# Logging and Debugging
CNCJS_LOG_LEVEL=info
CNCJS_VERBOSE_MODE=false
# Configuration File Path
CNCJS_CONFIG_FILE=/home/cncjs/.cncrc
# Security Settings
CNCJS_CHECK_FOR_UPDATES=true
CNCJS_EXCEPTION_IGNORE_ERRORS=false
# Database Configuration
CNCJS_DB_PATH=/home/cncjs/.cncrc.d
# Performance Settings
CNCJS_SOCKET_TIMEOUT=10000
CNCJS_SESSION_SECRET=your-secret-key-change-this
# Language Settings
CNCJS_LANGUAGE=en
# Optional: Hardware Pendant Support
CNCJS_PENDANT_KEYBOARD=true
CNCJS_PENDANT_PS3=false
CNCJS_PENDANT_NUMPAD=false

To apply these variables:

  1. Go to your app settings in the Klutch.sh dashboard
  2. Navigate to Environment Variables
  3. Add or update each variable
  4. Click Save and redeploy if necessary

Code Examples

JavaScript: CNCjs API Client

This example shows how to interact with CNCjs via its API:

// CNCjs API Client Example
class CNCjsClient {
constructor(baseUrl, accessToken = null) {
this.baseUrl = baseUrl || 'https://example-app.klutch.sh';
this.accessToken = accessToken;
}
async request(method, endpoint, data = null) {
const url = `${this.baseUrl}/api${endpoint}`;
const headers = {
'Content-Type': 'application/json',
};
if (this.accessToken) {
headers['Authorization'] = `Bearer ${this.accessToken}`;
}
const options = {
method,
headers,
};
if (data) {
options.body = JSON.stringify(data);
}
const response = await fetch(url, options);
return response.json();
}
// Get machine status
async getStatus() {
return this.request('GET', '/status');
}
// Get available ports
async getPorts() {
return this.request('GET', '/ports');
}
// Connect to a specific port
async connectPort(port, baudrate = 115200) {
return this.request('POST', `/ports/${port}`, { baudrate });
}
// Send G-code command
async sendGCode(command) {
return this.request('POST', '/gcode', { command });
}
// Get current position
async getPosition() {
return this.request('GET', '/position');
}
// List available G-code files
async listGcodeFiles() {
return this.request('GET', '/gcode/files');
}
// Load G-code file
async loadGcodeFile(filename) {
return this.request('POST', `/gcode/load`, { filename });
}
// Start job execution
async startJob() {
return this.request('POST', '/job/start', {});
}
// Pause job
async pauseJob() {
return this.request('POST', '/job/pause', {});
}
// Stop job
async stopJob() {
return this.request('POST', '/job/stop', {});
}
}
// Usage example
async function main() {
const client = new CNCjsClient('https://example-app.klutch.sh');
// Get machine status
const status = await client.getStatus();
console.log('Machine Status:', status);
// Get available ports
const ports = await client.getPorts();
console.log('Available Ports:', ports);
// Send home command
const response = await client.sendGCode('$H');
console.log('Home Response:', response);
// Get current position
const position = await client.getPosition();
console.log('Current Position:', position);
}
main().catch(console.error);

Bash: G-Code Batch Processing

This script processes and uploads multiple G-code files:

#!/bin/bash
# CNCjs G-Code Batch Processing Script
set -e
# Configuration
CNCJS_URL="https://example-app.klutch.sh"
GCODE_DIRECTORY="/app/gcode"
LOG_FILE="/var/log/cncjs-batch.log"
echo "$(date): Starting G-code batch processing..." >> "$LOG_FILE"
# Function to validate G-code file
validate_gcode() {
local file="$1"
# Check for common G-code errors
if grep -q "^G[0-9]\+ [^0-9]" "$file"; then
echo "Warning: Potentially invalid G-code syntax in $file" >> "$LOG_FILE"
return 1
fi
echo "G-code file validated: $file" >> "$LOG_FILE"
return 0
}
# Function to process G-code files
process_gcode_files() {
for gcode_file in "$GCODE_DIRECTORY"/*.gcode; do
if [ -f "$gcode_file" ]; then
echo "Processing: $gcode_file" >> "$LOG_FILE"
# Validate the file
if validate_gcode "$gcode_file"; then
echo "Ready for execution: $gcode_file" >> "$LOG_FILE"
else
echo "Validation failed: $gcode_file" >> "$LOG_FILE"
fi
fi
done
}
# Function to get machine status
check_machine_status() {
local status=$(curl -s "$CNCJS_URL/api/status" | grep -o '"state":"[^"]*"' | cut -d'"' -f4)
echo "Machine Status: $status" >> "$LOG_FILE"
echo "$status"
}
# Function to list loaded G-code files
list_gcode_files() {
echo "Available G-code files:" >> "$LOG_FILE"
ls -lh "$GCODE_DIRECTORY"/*.gcode >> "$LOG_FILE" 2>&1 || true
}
# Main execution
echo "Checking machine status..." >> "$LOG_FILE"
STATUS=$(check_machine_status)
if [ "$STATUS" = "Idle" ] || [ "$STATUS" = "idle" ]; then
echo "Machine is ready for operation" >> "$LOG_FILE"
process_gcode_files
list_gcode_files
else
echo "Machine is not in Idle state. Current state: $STATUS" >> "$LOG_FILE"
fi
echo "$(date): Batch processing complete" >> "$LOG_FILE"

Shell: CNCjs Control Commands

Common commands for controlling CNCjs from the terminal:

Terminal window
# Connect to CNCjs instance
CNCJS_HOST="https://example-app.klutch.sh"
# Get server status
curl -X GET "$CNCJS_HOST/api/status" \
-H "Content-Type: application/json"
# List available serial ports
curl -X GET "$CNCJS_HOST/api/ports" \
-H "Content-Type: application/json"
# Connect to a serial port
curl -X POST "$CNCJS_HOST/api/ports/COM3/open" \
-H "Content-Type: application/json" \
-d '{"baudrate": 115200}'
# Send G-code command (home all axes)
curl -X POST "$CNCJS_HOST/api/gcode" \
-H "Content-Type: application/json" \
-d '{"command": "$H"}'
# Send unlock command
curl -X POST "$CNCJS_HOST/api/gcode" \
-H "Content-Type: application/json" \
-d '{"command": "$X"}'
# Send absolute positioning command
curl -X POST "$CNCJS_HOST/api/gcode" \
-H "Content-Type: application/json" \
-d '{"command": "G90"}'
# Send movement command (move to position)
curl -X POST "$CNCJS_HOST/api/gcode" \
-H "Content-Type: application/json" \
-d '{"command": "G00 X10 Y20 Z5 F1000"}'
# Get current machine state
curl -X GET "$CNCJS_HOST/api/state" \
-H "Content-Type: application/json"
# Disconnect from port
curl -X POST "$CNCJS_HOST/api/ports/COM3/close" \
-H "Content-Type: application/json"

Best Practices

CNC Machine Operation

  • Pre-Flight Checks: Always verify machine position, tool installation, and workpiece setup before execution
  • G-Code Verification: Carefully review G-code files and preview tool paths before running
  • Test Runs: Perform dry runs without the spindle running to verify movements
  • Monitor Execution: Actively supervise CNC operation and be ready to stop if issues occur
  • Emergency Stop: Know the location and operation of emergency stops
  • Safety First: Always prioritize safety over efficiency

Configuration Management

  • Backup Configuration: Regularly back up your .cncrc configuration file
  • Version Control: Keep your G-code files organized in version control
  • Documentation: Document your CNC setup, controller type, and calibration settings
  • Update Management: Keep CNCjs updated for bug fixes and new features
  • Test Updates: Test updates in a non-production environment first

Performance and Reliability

  • Connection Stability: Use stable serial connections (USB recommended over Bluetooth for critical operations)
  • Network Optimization: For remote access, ensure low-latency network connections
  • Backup Storage: Maintain backups of important G-code files
  • Monitoring: Monitor CNCjs logs for errors and warnings
  • Resource Limits: Ensure adequate system resources for smooth operation

Security

  • Access Control: Use authentication when enabling remote access
  • Network Security: Use HTTPS and strong passwords for remote deployments
  • Token Management: Rotate access tokens regularly
  • User Permissions: Limit access to trusted users only
  • Audit Logging: Monitor who connects and what commands are executed

Troubleshooting

Issue: Cannot Connect to Serial Port

Solution: Verify the serial port is correctly identified and not already in use. Check USB cable connection if using USB serial adapter. Ensure user running CNCjs has serial port permissions. Test connection with serial port testing tools separately.

Issue: G-Code Not Loading or Executing

Solution: Verify G-code file format is compatible. Check file path and permissions. Validate G-code syntax using an online G-code validator. Ensure machine is in Idle state before sending commands. Check error messages in console output.

Issue: Machine Not Responding to Commands

Solution: Verify serial connection is established and showing connected status. Check if machine needs homing first ($H command). Ensure controller firmware matches CNCjs configuration. Test with simple commands (like $$ for settings dump) to verify communication.

Issue: Intermittent Connection Drops

Solution: Check USB cable quality and connections. Reduce baud rate to improve stability. Monitor system resources for CPU/memory issues. Consider upgrading to a higher-quality USB serial adapter. Check for USB hub issues if using hubs.

Issue: Slow Performance or Latency

Solution: Check network latency if using remote access. Reduce update frequency in settings. Monitor system resource usage (CPU, memory). Close unnecessary browser tabs and applications. Consider upgrading instance size if server-side performance is limited.

Updating CNCjs

To update to a newer version:

  1. Go to your Klutch.sh app dashboard
  2. Navigate to Deployments
  3. Update the Dockerfile to use a newer Node.js version or CNCjs version
  4. Commit and push changes to GitHub:
    Terminal window
    git add Dockerfile
    git commit -m "Update CNCjs and Node.js versions"
    git push origin main
  5. Klutch.sh automatically redeploys with the updated version
  6. Verify CNCjs is running correctly after update
  7. Test machine connectivity and basic operations

Always test updates in a safe environment before production deployment.

Use Cases

Home Hobby CNC

Deploy CNCjs to control a hobby CNC mill or router from your workshop, enabling precise control and monitoring.

Production Manufacturing

Use CNCjs as the control interface for production CNC equipment with multi-user access and job management.

Makerspcae Equipment

Set up CNCjs as a shared CNC control station in a makerspace for community members to operate equipment safely.

Remote CNC Operation

Enable remote CNC machine operation by deploying CNCjs on a cloud server and connecting a CNC machine via stable network connection.

Educational Training

Use CNCjs in educational settings to teach students CNC operation and G-code programming with hands-on experience.

Prototyping and Product Development

Leverage CNCjs for rapid prototyping with quick design iteration and CNC manufacturing capabilities.

Additional Resources

Conclusion

CNCjs provides a comprehensive, open-source solution for controlling CNC machinery through a web-based interface accessible from any device. By deploying CNCjs on Klutch.sh, you gain a scalable, maintainable platform for CNC operation with professional-grade features including 3D visualization, multi-client support, customizable widgets, and extensive hardware compatibility.

The combination of CNCjs’s powerful CNC control capabilities with Klutch.sh’s simplified deployment model makes it straightforward to establish web-based CNC control. Whether you’re running a hobby CNC mill, production equipment, or makerspace shared machinery, CNCjs provides the tools needed for effective CNC machine operation and control.

Start with the deployment steps outlined in this guide, configure your serial port or network connection, and gradually expand your CNCjs setup with custom widgets, pendants, and macros. Always prioritize safety and proper testing before operational use, and leverage the active community for support and best practices.