Distributed Transcription Setup¶
This guide walks through setting up distributed transcription with cast2md, allowing multiple machines to process transcription jobs in parallel.
Prerequisites¶
Server Requirements¶
- Running cast2md server
- Network accessible to transcriber nodes (local network or Tailscale)
Node Requirements¶
- Python 3.11+
- cast2md package installed
- Whisper dependencies (faster-whisper or mlx-whisper)
- Network access to the cast2md server
Recommended Hardware¶
| Platform | Backend | Notes |
|---|---|---|
| Apple Silicon Mac (M1-M4) | mlx-whisper | Best power efficiency |
| NVIDIA GPU (8GB+ VRAM) | faster-whisper + CUDA | Highest throughput |
| CPU-only | faster-whisper | Works but slower, use smaller models |
Part 1: Server Setup¶
Enable Distributed Transcription¶
- Open the cast2md web interface
- Navigate to Settings
- Enable "Distributed Transcription"
- Optionally adjust:
- Node Heartbeat Timeout: seconds before marking a node offline (default: 60)
- Remote Job Timeout: minutes before reclaiming a stuck job (default: 30)
- Click Save Settings
Verify Server is Ready¶
- Go to the Status page
- You should see a "Remote Transcriber Nodes" section (may show "No nodes registered")
- The Settings page has a "Transcriber Nodes" section for managing nodes
Note Your Server URL¶
You'll need the server's URL for node registration:
- Local network:
http://192.168.1.100:8000 - Tailscale:
http://your-server.tail12345.ts.net:8000 - Docker:
http://host.docker.internal:8000(from containers)
Part 2: Node Setup¶
Perform these steps on each machine you want to use as a transcriber node.
Quick Install (Recommended)¶
This script:
- Supports macOS (launchd) and Linux (systemd)
- Checks prerequisites (Python 3.11+, ffmpeg)
- Creates a virtual environment with minimal dependencies (~280 MB)
- Detects Apple Silicon and installs MLX backend automatically
- Prompts for server URL and node name
- Offers three service options: auto-start service, shell script, or manual
Tip
Updating: Run the same command again. The script detects existing installations and offers to update.
Uninstalling: Run the script and choose the uninstall option.
Manual Install¶
Step 1: Install cast2md¶
git clone https://github.com/meltforce/cast2md.git
cd cast2md
# Create virtual environment
python3 -m venv .venv
source .venv/bin/activate
# Install cast2md without dependencies
pip install --no-deps -e .
# Install node dependencies
pip install httpx pydantic-settings python-dotenv click fastapi \
'uvicorn[standard]' jinja2 python-multipart
# Install transcription backend (choose one)
pip install mlx-whisper # Apple Silicon
pip install faster-whisper # Intel/NVIDIA
Step 2: Configure Whisper Settings (Optional)¶
Create a .env file or set environment variables:
Step 3: Register the Node¶
Credentials are stored in ~/.cast2md/node.json:
{
"server_url": "http://192.168.1.100:8000",
"node_id": "a1b2c3d4-e5f6-...",
"api_key": "generated-secret-key",
"name": "M4 MacBook Pro"
}
Step 4: Start the Node Worker¶
The node will:
- Auto-open browser to the status UI at
http://localhost:8001 - Send heartbeats every 30 seconds
- Poll for jobs every 5 seconds
- Process any available transcription jobs
Step 5: Verify Connection¶
On the server, check the Status page -- the node should appear under "Remote Transcriber Nodes".
Node Web UI¶
The node runs a local web interface on port 8001:
| Page | Description |
|---|---|
Status (/) |
Node config, worker status, current job with progress bar |
Queue (/queue) |
Transcription queue stats, running and queued jobs |
Settings (/settings) |
System info, Whisper config, node config |
| Server Link | Opens the main cast2md server |
Part 3: Running Transcription Jobs¶
Queue episodes using any method:
- Web UI: Click "Get Transcript" or "Download Audio" on an episode
- CLI:
cast2md process <episode_id> - Batch: Click "Get All Transcripts" on a feed page
When jobs are queued:
- Local worker picks up the first unclaimed job
- Remote nodes poll and claim other jobs
- Both process in parallel
Part 4: Managing Nodes¶
View All Nodes¶
- Web UI: Settings page -> Transcriber Nodes section
- API:
GET /api/nodes
Test Node Connectivity¶
- Web UI: Click "Test" next to a node in Settings
- API:
POST /api/nodes/{node_id}/test
Remove a Node¶
- Web UI: Click "Delete" next to a node in Settings
- CLI (on the node):
cast2md node unregister - API:
DELETE /api/nodes/{node_id}
Rename a Node¶
- Edit
~/.cast2md/node.jsonand change the"name"field - Restart the node
- The server sees the new name within 30 seconds (synced via heartbeat)
Add Node Manually (Without CLI)¶
- Go to Settings -> Transcriber Nodes -> "Add Node Manually"
- Fill in name, URL, optional whisper model and priority
- Click "Add Node"
- Save the API key shown -- you'll need it for the node
Then create ~/.cast2md/node.json manually on the node machine.
Part 5: Running as a Service¶
If installed via script, use the helper commands:
~/.cast2md/stop # Stop the node
~/.cast2md/start # Start the node
~/.cast2md/restart # Restart the node
~/.cast2md/logs # Follow the log file
For manual setup, create ~/Library/LaunchAgents/com.cast2md.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>com.cast2md.node</string>
<key>ProgramArguments</key>
<array>
<string>/path/to/venv/bin/cast2md</string>
<string>node</string>
<string>start</string>
<string>--no-browser</string>
</array>
<key>WorkingDirectory</key>
<string>/path/to/cast2md</string>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>~/.cast2md/node.log</string>
<key>StandardErrorPath</key>
<string>~/.cast2md/node.log</string>
</dict>
</plist>
Load:
If installed via script:
systemctl --user stop cast2md-node
systemctl --user start cast2md-node
systemctl --user restart cast2md-node
systemctl --user status cast2md-node
Log file: ~/.cast2md/node.log
For manual setup, create ~/.config/systemd/user/cast2md-node.service:
[Unit]
Description=cast2md Transcriber Node
After=network.target
[Service]
Type=simple
WorkingDirectory=/path/to/cast2md
ExecStart=/path/to/venv/bin/cast2md node start --no-browser
Restart=always
RestartSec=10
Environment=WHISPER_MODEL=large-v3-turbo
Environment=WHISPER_BACKEND=faster-whisper
[Install]
WantedBy=default.target
Enable and start:
Auto-Termination¶
Node workers automatically terminate to save costs when idle. This is especially important for paid cloud instances like RunPod.
Termination Conditions¶
| Condition | Default | Description |
|---|---|---|
| Empty Queue | 2 checks x 60s | No claimable jobs available |
| Idle Timeout | 10 minutes | No jobs processed (safety net) |
| Server Unreachable | 5 minutes | Can't reach server |
| Circuit Breaker | 3 consecutive failures | Broken GPU protection |
Configuration¶
NODE_REQUIRED_EMPTY_CHECKS=2
NODE_EMPTY_QUEUE_WAIT=60
NODE_IDLE_TIMEOUT_MINUTES=10
NODE_SERVER_UNREACHABLE_MINUTES=5
NODE_MAX_CONSECUTIVE_FAILURES=3
# Disable ALL auto-termination
NODE_PERSISTENT=1
Troubleshooting¶
Node Shows "Offline" on Server¶
- Check network connectivity:
curl http://YOUR_SERVER:8000/api/system/health - Verify node is running:
cast2md node status - Check logs for connection errors
- Re-register if needed:
cast2md node unregister && cast2md node register --server ... --name ...
Jobs Not Being Claimed¶
- Ensure distributed transcription is enabled on the server
- Check node status -- must be "online" or "busy"
- Verify jobs exist:
curl http://localhost:8000/api/queue/status - Only transcription jobs go to nodes, not downloads
Transcription Fails on Node¶
- Test Whisper locally:
cast2md transcribe <episode_id> - Verify model is downloaded (auto-downloads on first use)
- Check available memory -- large models need significant resources
Job Stuck on Node¶
- Reset via API:
curl -X POST http://localhost:8000/api/queue/{job_id}/reset - Wait for timeout -- jobs auto-reclaim after the configured timeout
- Check node logs for errors
Performance Tips¶
Scaling Recommendations¶
| Episodes/Day | Recommended Setup |
|---|---|
| < 10 | Server only, no nodes needed |
| 10-50 | 1-2 fast nodes (M4 Mac or GPU) |
| 50-200 | 3-5 nodes |
| 200+ | Consider RunPod GPU workers |
Network Considerations¶
- Local Network: Best performance, lowest latency
- Tailscale/WireGuard: Good for remote nodes, adds ~10-50ms latency
- Public Internet: Not recommended (security, latency)
Audio files can be 50-200MB per episode, so bandwidth matters for the download step. Transcription happens locally on the node.