Skip to content

Self-Hosted VPS

Self-Hosted VPS

For servers you SSH into and control directly: DigitalOcean droplets, Hetzner, Linode, any bare-metal Ubuntu/Debian, etc.

Create /etc/systemd/system/queue-autoscale.service:

[Unit]
Description=Laravel Queue Autoscale manager
After=network.target redis-server.service

[Service]
Type=simple
User=forge
Group=forge
Restart=always
RestartSec=5s
WorkingDirectory=/home/forge/your-app.com/current
ExecStart=/usr/bin/php artisan queue:autoscale
StandardOutput=append:/var/log/queue-autoscale.log
StandardError=append:/var/log/queue-autoscale.err.log

# Graceful shutdown: SIGTERM, wait up to 60s, then SIGKILL.
# Match or exceed queue-autoscale.workers.shutdown_timeout_seconds.
TimeoutStopSec=60s
KillSignal=SIGTERM

[Install]
WantedBy=multi-user.target

Enable and start:

sudo systemctl daemon-reload
sudo systemctl enable queue-autoscale
sudo systemctl start queue-autoscale
sudo systemctl status queue-autoscale

On deploy, restart the daemon so it picks up new code:

sudo systemctl restart queue-autoscale

Option B — Supervisor

/etc/supervisor/conf.d/queue-autoscale.conf:

[program:queue-autoscale]
process_name=%(program_name)s
command=php /home/forge/your-app.com/current/artisan queue:autoscale
autostart=true
autorestart=true
user=forge
numprocs=1
redirect_stderr=true
stdout_logfile=/var/log/queue-autoscale.log
stopwaitsecs=60
stopsignal=TERM

Reload:

sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl status queue-autoscale

Zero-downtime deploys

Your deploy script should trigger exactly one restart path:

php artisan queue:autoscale:restart
# or
sudo systemctl restart queue-autoscale
# or
sudo supervisorctl restart queue-autoscale

php artisan queue:autoscale:restart works like Laravel's queue:restart: it writes a cache signal, the running manager notices it on the next evaluation tick, gracefully terminates all spawned queue:work processes, and exits. Your process supervisor then starts a fresh manager with the new code/config.

Direct systemctl / supervisorctl restarts are also fine; they send SIGTERM immediately, and the manager performs the same graceful shutdown path.

Do not also run php artisan queue:restart — that's for separately-supervised queue:work daemons. Our spawned workers are killed directly when the manager shuts down.

Log rotation

For systemd on a modern distro, journald handles rotation. If you append to a file (as shown above), drop a logrotate config:

/etc/logrotate.d/queue-autoscale:

/var/log/queue-autoscale*.log {
    daily
    missingok
    rotate 14
    compress
    delaycompress
    notifempty
    copytruncate
}

Troubleshooting

Manager keeps restarting every few seconds. Check the log — usually config validation failure or missing Redis. journalctl -u queue-autoscale -n 100.

Workers spawn but die immediately. Run php artisan queue:work redis --queue=default manually as the same user. The error will be obvious (missing .env, wrong path, bad permissions).

Manager runs but nothing scales. Verify metrics: php artisan queue-autoscale:debug-queue default. If metrics are empty, laravel-queue-metrics isn't collecting — check its storage backend.