Automated worker deployment via Docker images or git-clone. Git monitoring, zero-downtime restarts.
8.8K
Automated worker deployment and lifecycle management system that monitors Git repositories, builds packages, and manages worker processes.
docker run -d \
--name rec-worker-updater \
-p 3003:3003 \
-v /path/to/config:/app/config \
-v /path/to/repos:/app/repos \
-v /path/to/logs:/app/logs \
-v /path/to/secrets:/run/secrets:ro \
-v /path/to/ssh:/root/.ssh:ro \
-v /var/run/docker.sock:/var/run/docker.sock \
bionicburn/rec-worker-updater:latest
⚠️ IMPORTANT: Create the secrets directory and files before starting:
# Create secrets directory
mkdir -p /path/to/secrets
# Generate update secret (required for POST /update authentication)
openssl rand -base64 32 > /path/to/secrets/update_secret
chmod 600 /path/to/secrets/update_secret
# Optionally create GitHub webhook secret
openssl rand -base64 32 > /path/to/secrets/github_webhook_secret
chmod 600 /path/to/secrets/github_webhook_secret
The Worker Updater automates the deployment and management of worker packages with two deployment strategies:
Benefits: ~30 second deployments (vs 100+ minutes), no yarn install on slow NAS.
Use when: Pre-built Docker images are not available or you need custom builds.
| Path | Purpose | Required |
|---|---|---|
/app/config | Configuration files (config.json) | ✅ Yes |
/app/repos | Git repository clones | ✅ Yes |
/app/logs | Application and worker logs | ✅ Yes |
/run/secrets | Security secrets (update_secret, webhook secret, client keys) | ✅ Yes |
/root/.ssh | SSH keys for Git authentication | ✅ Yes |
/var/run/docker.sock | Docker socket (for container management) | ⚠️ Optional |
⚠️ CRITICAL: The /run/secrets mount is required for authentication to work. Without it:
POST /update endpoint will be unauthenticated (security risk)See the Security Setup section for how to create secrets.
The Worker Updater exposes REST API endpoints for monitoring and control:
GET /health
Returns lightweight health status. For detailed status with git and worker info, use GET /health/detail.
Example response:
Example Response:
{
"status": "healthy",
"updater": {
"version": "0.0.15-alpha.1",
"commit": "abc123d",
"buildTime": "2026-03-04T21:00:00Z",
"branch": "main",
"environment": "production"
},
"git": {
"status": "ready",
"branch": "main",
"commit": "abc123def",
"lastCheck": "2026-03-04T22:00:00Z",
"lastTag": "worker-v1.0.0"
},
"worker": {
"running": true,
"status": "running",
"startedAt": "2026-03-04T20:00:00Z",
"health": {
"status": "ok",
"version": "1.0.0",
"uptime": 3600
}
}
}
GET /version
Returns lightweight version information:
{
"version": "0.0.15-alpha.1",
"commit": "abc123d",
"buildTime": "2026-03-04T21:00:00Z",
"branch": "main",
"environment": "production"
}
POST /update
Manually triggers worker update check and deployment.
HMAC Signature Authentication (Recommended):
timestamp=$(date +%s%3N); signature=$(echo -n "${timestamp}" | openssl dgst -sha256 -hmac "your-secret" -hex | cut -d" " -f2); curl -X POST -H "X-Signature: ${signature}" -H "X-Timestamp: ${timestamp}" http://localhost:3003/update
RSA/Ed25519 Signature (Maximum Security):
curl -X POST -H "X-Client-Id: github-ci" -H "X-Signature: <base64-rsa-sig>" -H "X-Timestamp: 1710456789123" http://localhost:3003/update
Shared Secret (DEPRECATED):
curl -X POST -H "X-Update-Secret: your-secret" http://localhost:3003/update
POST /webhook/github
Receives GitHub push events for automatic deployments. Secured with HMAC-SHA256 signature validation.
Configure in GitHub repository settings:
https://your-server:3003/webhook/githubapplication/jsongithub.webhookSecretPath)push eventThe Worker Updater and managed workers may need multiple ports exposed:
| Port | Purpose |
|---|---|
| 3003 | Worker Updater API (health, version, webhooks) |
Configure in config.json:
{
"updater": {
"port": 3003
}
}
Workers managed by the updater may expose their own API endpoints. Configure port mapping in Docker run command or docker-compose:
Example: Worker with API on port 3002
docker run -d \
--name rec-worker-updater \
-p 3003:3003 \
-p 3002:3002 \
-v /path/to/config:/app/config \
...
bionicburn/rec-worker-updater:latest
Docker Compose Example:
services:
rec-worker-updater:
image: bionicburn/rec-worker-updater:latest
ports:
- "3003:3003" # Updater API
- "3002:3002" # Worker API (example)
volumes:
- ./config:/app/config
- ./repos:/app/repos
- ./logs:/app/logs
Port Mapping Strategy:
packages[].environment.PORT to configure worker listen portspackages[].environment.HOST to customise hostname (defaults to localhost)Example config.json with worker port:
{
"packages": [
{
"name": "@your-org/worker",
"environment": {
"PORT": "3002",
"API_URL": "https://api.example.com"
}
}
]
}
The worker application reads process.env.PORT and listens on that port. The worker-updater container must expose this port to make it accessible from the host.
Create a config.json file in /app/config/ with your deployment settings.
{
"$schema": "./config.schema.json",
"version": "1.0",
"updater": {
"port": 3003,
"logLevel": "info",
"updateCheckInterval": 300000,
"healthCheckInterval": 60000
},
"git": {
"repositoryUrl": "[email protected]:user/repo.git",
"branch": "main",
"trackingMode": "tags",
"tagPattern": "worker-v*",
"tagEnvironment": "production",
"sshKeyPath": "/root/.ssh/id_rsa",
"clonePath": "/app/repos/your-repo",
"author": {
"name": "Worker Updater",
"email": "[email protected]"
}
},
"github": {
"webhookEnabled": true,
"webhookSecretPath": "/run/secrets/github_webhook_secret",
"webhookPath": "/webhook/github",
"token": "ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
"security": {
"updateSecretPath": "/run/secrets/update_secret",
"signatureAuthEnabled": false,
"clientPublicKeysPath": "/run/secrets/client-keys"
},
"docker": {
"socketPath": "/var/run/docker.sock"
},
"packages": [
{
"name": "@your-org/worker",
"enabled": true,
"manifestPath": "packages/worker/package-config.json",
"containerName": "your-worker",
"environment": {
"PORT": "3002",
"DATABASE_URL": "postgresql://...",
"API_URL": "https://api.example.com",
"API_KEY": "your-api-key-here"
}
}
],
"storage": {
"basePath": "/storage",
"secrets": "/storage/secrets",
"data": "/storage/data",
"config": "/storage/config",
"logs": "/app/logs"
},
"logging": {
"level": "info",
"directory": "/app/logs"
}
}
The Worker Updater supports three tracking modes:
1. Branch Tracking - Monitors branch for new commits (dev/staging) 2. Tag Tracking - Deploys on new tags (production) 3. Commit Tracking - Pins to specific commit SHA (testing)
Example tag mode:
{
"git": {
"trackingMode": "tags",
"tagPattern": "worker-v*",
"tagEnvironment": "production"
}
}
Required for:
Token permissions needed:
repo (full control of private repositories)read:packages (download packages from GitHub Package Registry)write:packages (upload packages - optional, only for CD pipelines)Configuration:
{
"github": {
"token": "ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}
}
⚠️ Security: Store tokens in gitignored config files, never commit them to version control.
How it works:
docker login ghcr.io with token before pulling imagesAlternative: Use SSH for Git (no token needed for Git, but still required for GHCR).
Each worker package includes a manifest that defines its deployment strategy and lifecycle:
Docker Image (Recommended):
{
"$schema": "./package-config.schema.json",
"type": "nodejs-workspace",
"deployment": {
"strategy": "docker-image",
"dockerImage": {
"enabled": true,
"source": {
"type": "github-container-registry",
"registry": "ghcr.io",
"repository": "username/worker-name",
"tagPattern": "{tag}"
},
"fallbackToGitClone": false
},
"docker": {
"containerName": "my-worker",
"network": "my-network",
"ports": [{ "container": 3002, "protocol": "tcp" }],
"restart": "unless-stopped"
}
},
"lifecycle": {
"health": {
"type": "http",
"endpoint": "/health",
"expectedStatus": 200,
"timeout": 5000
}
}
}
Git-Clone (Legacy):
{
"$schema": "./package-config.schema.json",
"type": "nodejs-workspace",
"deployment": {
"strategy": "git-clone"
},
"lifecycle": {
"install": {
"command": "yarn workspaces focus @your-org/worker @your-org/shared",
"description": "Install only worker and shared workspace dependencies"
},
"build": {
"command": "yarn workspace @your-org/shared build && yarn workspace @your-org/worker build",
"description": "Build shared package and worker package"
},
"start": {
"command": "node dist/worker/src/main.js",
"workingDirectory": "packages/worker",
"description": "Start worker service"
},
"health": {
"type": "http",
"endpoint": "/health",
"expectedStatus": 200,
"timeout": 5000,
"description": "Lightweight health check (no expensive operations)"
},
"healthDetail": {
"type": "http",
"endpoint": "/health/detail",
"expectedStatus": 200,
"timeout": 10000,
"description": "Detailed health check with dependencies"
}
},
"update": {
"watchPaths": [
"packages/worker/**",
"packages/shared/**",
"yarn.lock",
"package.json"
],
"excludePaths": [
"packages/worker/dist/**",
"packages/worker/node_modules/**",
"**/*.test.ts",
"**/*.spec.ts"
]
}
}
Lifecycle Commands:
install: Install dependencies before buildingbuild: Compile/build the worker packagestart: Command to run the workerhealth: Lightweight health check endpoint (path-only format)healthDetail: (Optional) Detailed health check with dependency validationHealth Endpoint Format:
Health endpoints use path-only format (e.g., /health) instead of full URLs. The Worker-Updater automatically constructs the full URL using the worker's PORT and HOST environment variables:
/health → Constructed as http://localhost:${PORT}/healthhttp://custom-host:8080/health → Used as-is (backward compatible)This allows the same package-config.json to work across different environments without hardcoded ports or hostnames.
Update Configuration:
watchPaths: File patterns to monitor for changesexcludePaths: Files to ignore when detecting changesPass environment variables to workers via the packages[].environment section:
{
"packages": [
{
"name": "@your-org/worker",
"environment": {
"PORT": "3002",
"DATABASE_URL": "postgresql://...",
"API_KEY": "secret-key-here"
}
}
]
}
Environment variables are:
.env file mountingAutomatic NODE_ENV Injection:
NODE_ENV is automatically set by the Worker-Updater based on git.tagEnvironment:
tagEnvironment: "preview" → NODE_ENV=developmenttagEnvironment: "production" → NODE_ENV=productionYou don't need to manually set NODE_ENV in the environment configuration.
Automatic Version Injection:
When using git tag tracking mode, the Worker-Updater automatically injects the APP_VERSION environment variable to worker processes. This provides accurate version reporting without manual version management:
{
"git": {
"trackingMode": "tags",
"tagPattern": "worker-v*"
}
}
When a tag like worker-v1.0.0 is deployed, the Worker-Updater:
1.0.0 from the tagAPP_VERSION=1.0.0 environment variable to the worker/health endpointThis ensures version consistency between deployment tags and runtime reporting, eliminating manual version management in package.json.
Configure persistent storage for workers:
{
"packages": [
{
"name": "@your-org/worker",
"volumes": {
"/host/database": "/app/data/db",
"/host/uploads": "/app/data/uploads",
"/host/cache": "/app/cache"
}
}
]
}
Volume Features:
The Worker Updater provides structured logging for both the updater service and managed workers:
Updater Logs (/app/logs/updater/):
YYYY-MM-DD.log - All updater activity (JSON format)YYYY-MM-DD-error.log - Error-only logsWorker Logs (/app/logs/worker/):
YYYY-MM-DD.log - Worker application logs (JSON format)YYYY-MM-DD-error.log - Worker error logsLog Levels: error, warn, info, debug
Configuration:
{
"logging": {
"level": "info",
"directory": "/app/logs"
}
}
View logs:
# Updater logs
tail -f /app/logs/updater/2026-03-04.log
# Worker logs
tail -f /app/logs/worker/2026-03-04.log
# Follow container logs
docker logs -f rec-worker-updater
Mount your SSH private key for Git operations:
# Create SSH key directory
mkdir -p ./ssh
cp ~/.ssh/id_rsa ./ssh/
# Set correct permissions
chmod 600 ./ssh/id_rsa
# Mount in Docker
-v $(pwd)/ssh:/root/.ssh:ro
The Worker Updater monitors worker health and automatically restarts failed processes:
/health and /health/detail endpointsEnable self-healing health monitoring by adding healthCheckInterval to your config.json:
{
"updater": {
"healthCheckInterval": 60000
}
}
How it works:
healthCheckInterval millisecondsRecommended intervals:
60000 (60 seconds) - Quick recovery from crashes120000 (120 seconds) - Less aggressive checkinghealthCheckInterval to disable self-healingWhen to use:
Uses semantic versioning: <major>.<minor>.<patch>[-alpha.<increment>]
Available Tags:
latest - Latest stable production release (recommended for production)preview - Preview/staging builds from main branch (for testing)<version> - Specific version pin (e.g., 0.0.15-alpha.1) for reproducible deploymentsCheck version:
curl http://localhost:3003/version
services:
rec-worker-updater:
image: bionicburn/rec-worker-updater:latest
container_name: rec-worker-updater
restart: unless-stopped
ports:
- "3003:3003" # Updater API
- "3002:3002" # Worker API (if worker exposes port)
volumes:
- ./config:/app/config
- ./repos:/app/repos
- ./logs:/app/logs
- ./secrets:/run/secrets:ro # REQUIRED for authentication
- ./ssh:/root/.ssh:ro
- /var/run/docker.sock:/var/run/docker.sock
environment:
- NODE_ENV=production
Before starting, create required secrets:
# Create secrets directory
mkdir -p ./secrets
# Generate update secret (required)
openssl rand -base64 32 > ./secrets/update_secret
chmod 600 ./secrets/update_secret
# Generate webhook secret (if using GitHub webhooks)
openssl rand -base64 32 > ./secrets/github_webhook_secret
chmod 600 ./secrets/github_webhook_secret
# Start the container
docker-compose up -d
⚠️ CRITICAL: Mount /run/secrets for authentication.
mkdir -p /path/to/secrets && chmod 700 /path/to/secrets
openssl rand -base64 32 > /path/to/secrets/update_secret
chmod 600 /path/to/secrets/update_secret
{
"security": {
"updateSecretPath": "/run/secrets/update_secret"
}
}
docker run -d -v /path/to/secrets:/run/secrets:ro ... bionicburn/rec-worker-updater:latest
openssl rand -base64 32 > /path/to/secrets/github_webhook_secret
chmod 600 /path/to/secrets/github_webhook_secret
🔒 See Security Setup above for required secrets configuration.
The Worker Updater supports two authentication methods for the POST /update endpoint:
1. Signature Authentication (Advanced - Recommended for Production)
Setup:
# 1. Create client-keys directory inside secrets
mkdir -p /path/to/secrets/client-keys
chmod 700 /path/to/secrets/client-keys
# 2. Generate client key pair
openssl genrsa -out client-private.pem 4096
openssl rsa -in client-private.pem -pubout -out /path/to/secrets/client-keys/github-ci.pem
chmod 600 /path/to/secrets/client-keys/github-ci.pem
# 3. Keep private key secure (for CI/CD use)
chmod 600 client-private.pem
# Store client-private.pem in GitHub Secrets or secure vault
# 4. Secrets directory structure:
# /path/to/secrets/
# ├── update_secret # Shared secret (still needed as fallback)
# ├── github_webhook_secret # Optional webhook secret
# └── client-keys/ # Client public keys directory
# └── github-ci.pem # Client public key
Configuration (config.json):
{
"security": {
"updateSecretPath": "/run/secrets/update_secret",
"signatureAuthEnabled": true,
"clientPublicKeysPath": "/run/secrets/client-keys"
}
}
Volume Mount (already done if you followed Security Setup):
volumes:
- ./secrets:/run/secrets:ro # Mounts update_secret AND client-keys/
Note: The client-keys directory is inside the secrets directory, so the same volume mount provides access to both shared secrets and client public keys.
2. Shared Secret Authentication (Basic)
X-Update-Secret headerBest Practices:
:ro)MIT License - Built and maintained by Red Eyed Crows.
Content type
Image
Digest
sha256:44fe884df…
Size
224.5 MB
Last updated
3 months ago
docker pull bionicburn/rec-worker-updater