Process Configuration
Process Configuration
Complete reference for configuring individual processes managed by Cbox Init.
Basic Process Structure
processes:
process-name:
enabled: true
command: ["executable", "arg1", "arg2"]
restart: always
scale: 1
user: www-data # Optional: run as user
group: www-data # Optional: run as group
working_dir: /var/www/html
env:
KEY: value
Core Settings
enabled
Type: boolean
Default: true
Description: Whether to start this process.
processes:
nginx:
enabled: true # Start this process
Use cases:
- Temporarily disable processes without removing configuration
- Environment-specific processes (dev vs production)
- Feature flags for optional services
command
Type: array of strings
Required: Yes
Description: Command and arguments to execute.
processes:
php-fpm:
command: ["php-fpm", "-F", "-R"] # Foreground mode, pool config, restart on-demand
Important:
- ✅ Use array format for proper argument parsing
- ✅ Run processes in foreground mode (no daemonizing)
- ❌ Don't use shell syntax like
["sh", "-c", "nginx -g 'daemon off;'"]
Common Commands:
# PHP-FPM
command: ["php-fpm", "-F", "-R"]
# Nginx
command: ["nginx", "-g", "daemon off;"]
# Laravel Queue Worker
command: ["php", "artisan", "queue:work", "--tries=3"]
# Laravel Horizon
command: ["php", "artisan", "horizon"]
# Symfony Messenger
command: ["php", "bin/console", "messenger:consume", "async"]
# WordPress Cron
command: ["php", "/var/www/html/wp-cron.php"]
# Custom PHP Worker
command: ["php", "/app/worker.php"]
restart
Type: string
Options: always, on-failure, never
Default: always
Description: Restart policy when process exits.
processes:
php-fpm:
restart: always # Always restart on exit
migration:
restart: never # One-time execution
queue:
restart: on-failure # Only restart on non-zero exit
Policy Guide:
always- Long-running services (PHP-FPM, Nginx, Horizon)on-failure- Services that should recover from errors but respect clean exitsnever- One-time tasks (migrations, seed data)
scale
Type: integer
Default: 1
Description: Number of identical instances to run.
processes:
queue-default:
command: ["php", "artisan", "queue:work"]
scale: 3 # Run 3 queue workers
When to scale:
- Queue workers (3-10 instances)
- Background processors
- Parallel job execution
Automatic naming:
queue-default-1queue-default-2queue-default-3
Advanced Settings
user
Type: string
Default: None (inherit from parent process)
Description: Run process as specified user (name or numeric UID).
processes:
php-fpm:
command: ["php-fpm", "-F", "-R"]
user: www-data # Run as www-data user
Formats:
- Username:
www-data,nginx,nobody - Numeric UID:
82,33,65534
Notes:
- Requires root privileges to switch users
- If only
useris specified, the user's primary group is used - Implements s6-overlay compatible USER directive functionality
group
Type: string
Default: User's primary group (if user specified)
Description: Run process as specified group (name or numeric GID).
processes:
php-fpm:
command: ["php-fpm", "-F", "-R"]
user: www-data
group: www-data # Explicit group
Formats:
- Group name:
www-data,nginx,nogroup - Numeric GID:
82,33,65534
Docker Example:
# Common pattern for Alpine-based images
processes:
php-fpm:
command: ["php-fpm", "-F", "-R"]
user: "82" # www-data UID on Alpine
group: "82" # www-data GID on Alpine
nginx:
command: ["nginx", "-g", "daemon off;"]
user: nginx
group: nginx
Security Best Practice:
- Run services with minimal required privileges
- Use dedicated service users (www-data, nginx) instead of root
- Cbox Init will warn if running as root without user/group specified
depends_on
Type: array of strings
Default: []
Description: Process dependencies for startup ordering.
processes:
php-fpm:
command: ["php-fpm", "-F", "-R"]
nginx:
command: ["nginx", "-g", "daemon off;"]
depends_on: [php-fpm] # Wait for PHP-FPM to be healthy
horizon:
command: ["php", "artisan", "horizon"]
depends_on: [php-fpm] # Wait for PHP-FPM
Behavior:
- Processes wait for dependencies to be healthy (if health check configured)
- Creates a dependency graph (DAG) for proper ordering
- Prevents startup failures from missing dependencies
- Processes without dependencies start in alphabetical order
working_dir
Type: string
Default: /var/www/html or current directory
Description: Working directory for process execution.
processes:
app:
command: ["./my-app"]
working_dir: /opt/application
stdout / stderr
Type: bool
Default: true
Description: Enable or disable forwarding of the process' STDOUT/STDERR streams to Cbox's logger.
processes:
queue-default:
command: ["php", "artisan", "queue:work"]
stdout: false # Silence STDOUT
stderr: true
Use these top-level flags as a shorthand for logging.stdout and logging.stderr.
env
Type: object
Default: {}
Description: Environment variables for the process.
processes:
queue:
command: ["php", "artisan", "queue:work"]
env:
QUEUE_CONNECTION: redis
QUEUE_NAME: default
REDIS_HOST: localhost
Inheritance:
- Process env vars override global environment
- System env vars are inherited by default
schedule
Type: string (cron expression)
Default: None
Description: Run process on a schedule (cron-like).
processes:
backup:
command: ["php", "artisan", "backup:run"]
schedule: "0 2 * * *" # Daily at 2 AM
Cron Format: minute hour day month weekday
"*/15 * * * *" # Every 15 minutes
"0 * * * *" # Every hour
"0 2 * * *" # Daily at 2 AM
"0 2 * * 1" # Mondays at 2 AM
"0 0 1 * *" # First day of month
See Scheduled Tasks for complete guide.
schedule_timeout
Type: string (duration)
Default: No timeout
Description: Maximum execution time for scheduled task. Process is killed if timeout exceeded.
processes:
backup:
command: ["php", "artisan", "backup:run"]
schedule: "0 2 * * *"
schedule_timeout: "30m" # Kill if runs longer than 30 minutes
Duration Format:
30s- 30 seconds5m- 5 minutes1h- 1 hour1h30m- 1 hour 30 minutes
Best Practice: Set timeout less than schedule interval to prevent overlap.
schedule_max_concurrent
Type: integer
Default: 0 (unlimited)
Description: Maximum concurrent executions of this scheduled task.
processes:
sync:
command: ["php", "artisan", "sync:external-api"]
schedule: "*/5 * * * *"
schedule_max_concurrent: 1 # Skip if previous run still active
Values:
0- Unlimited concurrent executions (default)1- No overlap (skip trigger if task still running)N- Allow up to N concurrent executions
Use cases:
1for tasks that shouldn't overlap (database sync, backups)0or higher for idempotent tasks that can run in parallel
schedule_timezone
Type: string
Default: Local
Description: Timezone for schedule interpretation.
processes:
report:
command: ["php", "artisan", "reports:generate"]
schedule: "0 9 * * 1-5" # 9 AM weekdays
schedule_timezone: "America/New_York" # In Eastern time
Values:
Local- System timezone (default)UTC- Coordinated Universal Time- Any IANA timezone:
America/New_York,Europe/London,Asia/Tokyo, etc.
Shutdown Configuration
Every stop — docker stop, cbox-init stop <name>, a scale-down, or a reload
that replaces the process — runs the same sequence for each instance:
shutdown.pre_stop_hook, if configured.shutdown.signalis sent to the instance's process group.- Up to
shutdown.timeoutseconds for the instance to exit. - If it has not,
shutdown.kill_signalis sent. - Up to
shutdown.kill_timeoutseconds for it to exit. - If
kill_signalwas not alreadySIGKILL,SIGKILLis sent and the instance gets 5 more seconds to be reaped.
During container shutdown, step 3 is also bounded by global.shutdown_timeout:
whichever runs out first moves the stop on to step 4. Steps 4–6 are never
skipped, so after its pre-stop hook a single instance can take up to
timeout + kill_timeout to stop (plus 5s when kill_signal is not SIGKILL).
Processes that depend on each other stop one after another, dependents first,
so their times add up. Give the container runtime more than the total — for
Docker, --stop-timeout or stop_grace_period; for Kubernetes,
terminationGracePeriodSeconds — or it kills the whole container before
cbox-init is done.
processes:
postgres:
command: ["docker-entrypoint.sh", "postgres"]
shutdown:
signal: SIGINT # PostgreSQL "fast" shutdown
timeout: 100 # room for the shutdown checkpoint
kill_signal: SIGQUIT # "immediate" shutdown instead of SIGKILL
kill_timeout: 10 # postgres SIGKILLs stuck backends itself after 5s
An instance counts as stopped when its own process has exited, even if something it started — a backgrounded helper, or a child in a session of its own — still holds its stdout or stderr. What the process wrote is drained for up to a second first; after that the exit is acted on, a warning is logged, and anything written later is still logged as it arrives. The same goes for a process that crashes: the exit is acted on, and the restart policy applied, within a second.
Signal names are case-insensitive and the SIG prefix is optional (SIGQUIT,
QUIT and quit are the same). Accepted: SIGTERM, SIGINT, SIGQUIT,
SIGKILL, SIGHUP, SIGUSR1, SIGUSR2, SIGWINCH, SIGCONT, SIGSTOP,
SIGTSTP, SIGABRT. Anything else fails config validation.
shutdown.signal
Type: string
Default: SIGTERM
Description: The signal that asks the process to stop gracefully.
Use the signal your program treats as "finish up and exit":
| Program | Signal | Why |
|---|---|---|
| Most programs, PHP-FPM | SIGTERM (default) |
Graceful stop |
| Nginx | SIGQUIT |
Graceful stop; SIGTERM is nginx's fast stop |
| PostgreSQL | SIGINT |
Fast shutdown: rolls back open transactions, disconnects clients, writes a shutdown checkpoint. SIGTERM is smart shutdown, which waits for every client to leave |
processes:
nginx:
command: ["nginx", "-g", "daemon off;"]
shutdown:
signal: SIGQUIT
shutdown.timeout
Type: integer (seconds)
Default: 30
Description: How long to wait after signal before sending kill_signal.
It does not inherit global.shutdown_timeout. During container shutdown the
global value is a deadline over the whole stop: a larger per-process timeout
is cut short by it, so raise both when a process needs long to drain.
global:
shutdown_timeout: 150
processes:
horizon:
command: ["php", "artisan", "horizon"]
shutdown:
timeout: 120 # Allow 2 minutes for graceful shutdown
shutdown.kill_signal
Type: string
Default: SIGKILL
Description: Sent when timeout expires and the process is still running.
The default ends the process at once. Some programs have a stronger stop that is
still not SIGKILL and is better for them. PostgreSQL's SIGQUIT (immediate
shutdown) makes the postmaster stop its own backends before it exits; a
SIGKILL on the postmaster leaves its backends running with nobody in charge.
If the process traps or ignores kill_signal, cbox-init still sends SIGKILL
after kill_timeout, so a stop always ends.
shutdown.kill_timeout
Type: integer (seconds)
Default: 5
Range: 0–600 (0 means the default)
Description: How long to wait after kill_signal before sending SIGKILL.
Raise it when kill_signal starts a shutdown that takes time of its own. With
kill_signal: SIGQUIT, PostgreSQL waits 5 seconds for its backends and then
SIGKILLs the ones still running; with the default kill_timeout: 5, cbox-init
can SIGKILL the postmaster first. kill_timeout: 10 lets PostgreSQL finish.
When kill_signal is SIGKILL, kill_timeout is how long to wait for the
process to be reaped before cbox-init reports it as not stopped.
shutdown.pre_stop_hook
Type: object
Description: Command to run before stopping the process.
processes:
horizon:
command: ["php", "artisan", "horizon"]
shutdown:
pre_stop_hook:
command: ["php", "artisan", "horizon:terminate"]
timeout: 60
Use cases:
- Laravel Horizon graceful termination
- Database connection cleanup
- File upload completion
- Cache flush operations
See Lifecycle Hooks for pre/post start hooks.
Health Check Configuration
processes:
nginx:
command: ["nginx", "-g", "daemon off;"]
health_check:
type: http
url: "http://127.0.0.1:80/health"
period: 10
timeout: 5
failure_threshold: 3
success_threshold: 2
Health Check Types:
tcp- TCP port connection (usesaddress)http- HTTP endpoint check (usesurl)exec- Execute command (usescommand)
See Health Checks Configuration for complete reference.
Complete Example
processes:
# Core Infrastructure
php-fpm:
enabled: true
command: ["php-fpm", "-F", "-R"]
restart: always
user: www-data # Run as www-data user
group: www-data # Run as www-data group
health_check:
type: tcp
address: "127.0.0.1:9000"
period: 10
# Web Server
nginx:
enabled: true
command: ["nginx", "-g", "daemon off;"]
restart: always
user: nginx # Run as nginx user
depends_on: [php-fpm]
health_check:
type: http
url: "http://127.0.0.1:80/health"
# Application Services
horizon:
enabled: true
command: ["php", "artisan", "horizon"]
restart: on-failure
depends_on: [php-fpm]
working_dir: /var/www/html
shutdown:
timeout: 120
pre_stop_hook:
command: ["php", "artisan", "horizon:terminate"]
timeout: 60
# Queue Workers
queue-default:
enabled: true
command: ["php", "artisan", "queue:work", "--tries=3"]
scale: 3
restart: always
depends_on: [php-fpm]
env:
QUEUE_CONNECTION: redis
QUEUE_NAME: default
# Scheduled Tasks
daily-backup:
enabled: true
command: ["php", "artisan", "backup:run"]
schedule: "0 2 * * *"
restart: never
Environment Variable Overrides
Override process settings via environment variables:
# Enable/disable process
CBOX_INIT_PROCESS_NGINX_ENABLED=false
# Change scale
CBOX_INIT_PROCESS_QUEUE_DEFAULT_SCALE=5
# Override command (JSON array)
CBOX_INIT_PROCESS_APP_COMMAND='["./my-app","--port=8080"]'
Pattern: CBOX_INIT_PROCESS_<NAME>_<SETTING>=<value>
See Environment Variables for complete reference.
See Also
- Health Checks - Configure health monitoring
- Lifecycle Hooks - Pre/post start/stop hooks
- Environment Variables - ENV var reference
- Examples - Real-world configurations