Development Mode
Development Mode
Cbox Init includes a development mode with configuration file watching and automatic reload capabilities. When config changes are detected, Cbox gracefully reloads, making it easier to iterate on configurations during development.
Overview
Features:
- 📁 File Watching - Monitors configuration file using fsnotify
- 🔄 Auto-Reload - Automatically reloads when changes detected
- ⏱️ Debouncing - Prevents multiple rapid reloads (2-second window)
- ✅ Validation - Validates new config before triggering reload
- 🛡️ Graceful Restart - Cleanly shuts down existing processes
Quick Start
# Enable dev mode with --dev flag
./cbox-init serve --dev
# Or with explicit config path
./cbox-init serve --config cbox-init.yaml --dev
# Dev mode watches the config file and auto-reloads on changes
How It Works
1. Startup
Cbox Init starts normally and initializes all processes with --dev flag:
$ ./cbox-init serve --config myconfig.yaml --dev
🚀 Cbox Init v1.0.0
time=2025-11-23T15:23:38.935+01:00 level=INFO msg="Development mode enabled" watch_config=/path/to/myconfig.yaml
time=2025-11-23T15:23:38.935+01:00 level=INFO msg="Config watcher started" path=/path/to/myconfig.yaml debounce=2s
# Cbox Init starts all processes normally...
2. File Watching
Watcher monitors the configuration file for changes:
# Terminal 1: Cbox Init running with --dev
# Terminal 2: Edit configuration
$ vim myconfig.yaml
# Make changes (e.g., change scale: 3 → scale: 5)
# Save file (:wq)
3. Change Detection
When config file is modified and saved, watcher detects the change:
# Terminal 1: Auto-reload triggered
time=2025-11-23T15:23:58.750+01:00 level=INFO msg="Config file changed, triggering reload" path=/path/to/myconfig.yaml event=WRITE
time=2025-11-23T15:23:58.751+01:00 level=INFO msg="Config reload triggered"
4. Validation
New configuration is loaded and validated before triggering reload:
time=2025-11-23T15:23:58.752+01:00 level=INFO msg="Performing config reload"
time=2025-11-23T15:23:58.752+01:00 level=INFO msg="Initiating graceful shutdown" reason="config reload" timeout=30s
If validation fails:
time=2025-11-23T15:24:10.123+01:00 level=INFO msg="Config file changed, triggering reload"
time=2025-11-23T15:24:10.124+01:00 level=ERROR msg="Config reload failed" error="invalid config: depends_on contains unknown process: 'nonexistent-process'"
# Cbox Init continues running with old configuration
5. Graceful Reload
If validation passes, graceful shutdown is initiated:
time=2025-11-23T15:23:58.752+01:00 level=INFO msg="Initiating graceful shutdown" reason="config reload" timeout=30s
time=2025-11-23T15:23:59.100+01:00 level=INFO msg="Stopping process" name=horizon
time=2025-11-23T15:23:59.200+01:00 level=INFO msg="Stopping process" name=nginx
time=2025-11-23T15:23:59.300+01:00 level=INFO msg="Stopping process" name=php-fpm
time=2025-11-23T15:24:00.500+01:00 level=INFO msg="All processes stopped successfully"
6. Exit Message
Cbox Init exits cleanly with message:
time=2025-11-23T15:24:00.752+01:00 level=INFO msg="Config reload complete - restart Cbox Init to apply changes"
7. Manual Restart
User restarts Cbox Init to apply new configuration:
$ ./cbox-init serve --config myconfig.yaml --dev
# Cbox Init starts with new configuration
Configuration
No special configuration required - dev mode is enabled via CLI flag only.
# Enable dev mode
./cbox-init serve --dev
Not a config file setting:
# ❌ This does NOT enable dev mode
global:
dev_mode: true # Not a valid config option
# ✅ Use --dev flag instead
./cbox-init serve --dev
File Watcher Behavior
Monitored Events
Watcher responds to these filesystem events:
- WRITE - File content modified
- CREATE - File created (e.g., after delete + save)
Ignored Events
These events do NOT trigger reload:
- CHMOD - Permission changes
- RENAME - File renamed (unless part of save strategy)
- REMOVE - File deleted
Debouncing
2-second debounce period prevents multiple rapid reloads:
# Multiple saves within 2 seconds
time=2025-11-23T15:24:20.100+01:00 level=INFO msg="Config file changed, triggering reload"
time=2025-11-23T15:24:20.500+01:00 level=DEBUG msg="Config change debounced" since_last_reload=0.4s
time=2025-11-23T15:24:21.000+01:00 level=DEBUG msg="Config change debounced" since_last_reload=0.9s
# Only one reload is triggered after 2 seconds of stability
Why debouncing?
- Some editors save files multiple times
- Prevents unnecessary reload storms
- Ensures file is fully written before reload
Auto-detected Paths
Watcher monitors the configuration file specified via:
--configflag (explicit path)CBOX_INIT_CONFIGenvironment variable- Auto-detected paths (priority order):
cbox-init.yaml(current directory)/etc/cbox-init/cbox-init.yaml(system-wide)~/.cbox/init/config.yaml(user-specific)
Example:
# Watches explicit config
./cbox-init serve --config custom.yaml --dev
# Watches: custom.yaml
# Watches auto-detected config
./cbox-init serve --dev
# Watches: cbox-init.yaml (if exists)
Validation Before Reload
Config validation runs before triggering reload:
Valid Config Example
# Original config
processes:
nginx:
scale: 3
# Edit and save: scale: 3 → scale: 5
processes:
nginx:
scale: 5 # Valid change
# Result: Reload triggered ✅
Invalid Config Example
# Edit with error
processes:
nginx:
scale: abc # Invalid! Must be integer
# Result: Error logged, reload aborted ❌
time=2025-11-23T15:24:10.124+01:00 level=ERROR msg="Config reload failed" error="invalid config: scale must be integer"
# Cbox continues with old config
Circular Dependency Example
# Edit with circular dependency
processes:
nginx:
depends_on: [php-fpm]
php-fpm:
depends_on: [nginx] # Circular!
# Result: Validation fails, reload aborted ❌
time=2025-11-23T15:24:10.124+01:00 level=ERROR msg="Config reload failed" error="circular dependency detected: nginx → php-fpm → nginx"
Development Workflow
Typical Iteration Cycle
1. Start Cbox with dev mode:
./cbox-init serve --config dev.yaml --dev
2. Edit configuration:
# Another terminal
vim dev.yaml
# Change: scale: 3 → scale: 5
# Save (:wq)
3. Watch auto-reload:
time=2025-11-23T15:24:00.750+01:00 level=INFO msg="Config file changed, triggering reload"
time=2025-11-23T15:24:00.752+01:00 level=INFO msg="Config reload triggered"
time=2025-11-23T15:24:00.753+01:00 level=INFO msg="Initiating graceful shutdown"
...
time=2025-11-23T15:24:01.500+01:00 level=INFO msg="Config reload complete - restart Cbox Init to apply changes"
4. Restart to apply:
./cbox-init serve --config dev.yaml --dev
# New configuration active
5. Verify changes:
# Check process list
curl http://localhost:9180/api/v1/processes
# OR
./cbox-init tui
6. Iterate:
- Repeat steps 2-5 as needed
- Fast feedback loop
- No manual process management
Use Cases
1. Adjusting Worker Counts
# Initial config: 3 workers
processes:
queue-worker:
scale: 3
# Test with 5 workers
# Edit: scale: 3 → scale: 5
# Save → Auto-reload
# Test with 10 workers
# Edit: scale: 5 → scale: 10
# Save → Auto-reload
# Find optimal count through iteration
2. Tuning Health Checks
# Initial config
health_check:
interval: 30
timeout: 5
# Too slow, adjust
# Edit: interval: 30 → interval: 10
# Save → Auto-reload
# Observe health check behavior
# Fine-tune based on observations
# Edit: timeout: 5 → timeout: 3
# Save → Auto-reload
3. Testing Log Levels
# Start with info
global:
log_level: info
# Hit issue, need debug logs
# Edit: log_level: info → log_level: debug
# Save → Auto-reload (now with debug logging)
# Investigate with detailed logs
# Restore normal level
# Edit: log_level: debug → log_level: info
# Save → Auto-reload
4. Experimenting with Dependencies
# Initial: No dependencies
processes:
nginx:
php-fpm:
# Add dependency for correct order
# Edit: Add depends_on: [php-fpm] to nginx
# Save → Auto-reload
# Verify startup order
# Check logs for dependency resolution
Limitations
1. Manual Restart Required
Cbox exits after reload (does not auto-restart):
time=2025-11-23T15:24:00.752+01:00 level=INFO msg="Config reload complete - restart Cbox Init to apply changes"
# Process exits with code 0
# User must restart manually:
./cbox-init serve --config config.yaml --dev
Rationale:
- Clean exit ensures proper cleanup
- User control over restart timing
- Prevents unexpected behavior
Workaround for auto-restart:
# Use systemd with Restart=always
[Unit]
Description=Cbox Init
[Service]
ExecStart=/usr/local/bin/cbox-init serve --dev
Restart=always
# OR use simple loop (not recommended for production)
while true; do
./cbox-init serve --dev
sleep 1
done
2. Full Restart (No Hot Reload)
All processes are stopped and started (not individual process reload):
- Cannot reload single process
- All instances restart together
- No zero-downtime reload
For production: Use rolling updates with orchestration (Kubernetes, etc.)
3. Debounce Delay
Minimum 2 seconds between reloads:
- Rapid changes are debounced
- Last change triggers reload after 2s stability
- Cannot force immediate reload
4. File System Events
Relies on OS file system notifications:
- May not work on some network filesystems (NFS, SMB)
- Rare edge cases with certain editors
- macOS/Linux only (fsnotify limitation)
5. Validation-Only Checking
Validates config syntax, not runtime behavior:
# Syntax valid, but command doesn't exist
processes:
worker:
command: ["nonexistent-binary"] # Won't catch until runtime
# Validation passes ✅
# Reload triggered
# Process fails to start at runtime ❌
Troubleshooting
Watcher Not Detecting Changes
Issue: Config changes not triggering reload
Solutions:
-
Verify absolute path logged:
time=2025-11-23T15:23:38.935+01:00 level=INFO msg="Config watcher started" path=/absolute/path/to/config.yaml -
Use explicit config path:
./cbox-init serve --config /absolute/path/to/config.yaml --dev -
Check filesystem support:
# Test fsnotify on your filesystem # Dev mode relies on filesystem event notifications -
Try different editor:
# Some editors use atomic saves that may not trigger events # Try vim, nano, or direct file write
Multiple Reloads on Single Save
Issue: Reload triggers multiple times for one save
Cause: Editor saves file multiple times
Solution:
- Debouncing is automatic (2s window)
- Watch logs for "Config change debounced" messages
- Only one reload happens after stability
Example:
time=2025-11-23T15:24:20.100+01:00 level=INFO msg="Config file changed, triggering reload"
time=2025-11-23T15:24:20.500+01:00 level=DEBUG msg="Config change debounced" since_last_reload=0.4s
# Only one reload after 2 seconds
Reload Fails with Validation Error
Issue: Valid-looking config fails validation
Solutions:
-
Check error message:
time=2025-11-23T15:24:10.124+01:00 level=ERROR msg="Config reload failed" error="..." -
Validate manually:
./cbox-init check-config --config config.yaml --strict -
Common issues:
- Circular dependencies
- Invalid data types
- Out-of-range values
- Missing required fields
Dev Mode in Production
Issue: Accidentally using dev mode in production
❌ Don't do this:
# Production container
docker run myapp /usr/local/bin/cbox-init serve --dev
✅ Use dev mode only for:
- Local development
- Testing configurations
- Debugging issues
Production best practices:
- Never use
--devflag - Use immutable container images
- Test config changes in staging first
Best Practices
1. Use Dev Mode for Development Only
# ✅ Development
./cbox-init serve --dev
# ❌ Production
./cbox-init serve # No --dev flag
2. Validate Before Saving
# Validate in another terminal before saving
./cbox-init check-config --config config.yaml --strict
# If valid, save file
# If invalid, fix errors first
3. Small, Incremental Changes
# ✅ Good: One change at a time
# Edit: scale: 3 → scale: 5
# Save → Test → Iterate
# ❌ Bad: Many changes at once
# Edit: Change scale, log level, dependencies, health checks
# Save → Hard to debug if issues arise
4. Watch the Logs
# Keep Cbox Init logs visible
./cbox-init serve --dev | tee cbox.log
# Watch for:
# - Config change detection
# - Validation errors
# - Graceful shutdown
# - Process start/stop
5. Test Validation First
# Before making risky changes
./cbox-init check-config --config config.yaml
# Intentionally make invalid change
# Observe validation prevents reload
# Fix error, save again
6. Use Version Control
# Commit working configs
git commit -m "Working config with 3 workers"
# Experiment with changes
# If broken, easy to revert
git checkout config.yaml
Examples
Example 1: Iterating on Worker Counts
# Terminal 1: Start Cbox with dev mode
./cbox-init serve --config dev.yaml --dev
# Terminal 2: Edit config
vim dev.yaml
# Initial: scale: 3
# Change to: scale: 5
# Save
# Cbox auto-reloads
# Restart Cbox to apply
# Test with 5 workers
# If needed, change to: scale: 10
# Save, restart, test
# Find optimal count
Example 2: Debugging with Log Levels
# Terminal 1: Running with info level
./cbox-init serve --config dev.yaml --dev
# Terminal 2: Hit issue, need debug logs
vim dev.yaml
# Change: log_level: info → log_level: debug
# Save
# Terminal 1: Auto-reload triggers
# Restart with debug logging
# Investigate issue with detailed logs
# Fix issue, restore info level
vim dev.yaml
# Change: log_level: debug → log_level: info
# Save, restart
Example 3: Health Check Tuning
# Start with conservative settings
health_check:
interval: 30
timeout: 10
retries: 5
# Edit: Reduce interval for faster detection
# Change interval: 30 → interval: 10
# Save, restart, observe
# Edit: Reduce timeout
# Change timeout: 10 → timeout: 5
# Save, restart, observe
# Edit: Reduce retries
# Change retries: 5 → retries: 3
# Save, restart, test
# Final tuned config based on observations
Next Steps
- Configuration Validation - Validate changes before reload
- Configuration Overview - Complete configuration reference
- Quick Start - Get started with Cbox Init