Deployment
Introduction
The terminal loop is great for development — you type, the agent replies, you iterate. But production is different. There's no friendly REPL keeping things alive; you need long-running processes, supervisors, and a couple of supporting services.
This page walks through everything you need running in production, and ends with a pre-launch checklist you can copy/paste into your runbook.
Let's get started!
Queue Worker
Almost every connector queues the agent invocation rather than running it during the HTTP request:
php artisan queue:work
In production, run it under Laravel Horizon, Supervisor, or systemd so it restarts on failure. Without a queue worker, messages arrive but are never processed — they just pile up in the queue.
!NOTE The API connector is the exception. It runs synchronously and returns the agent's reply inline, so it works without a queue worker. Every other connector (Telegram, Slack, Email, Terminal) needs one.
Job Retries
Laraclaw's jobs (SendReminder, SendHeartbeat, EmbedConversation, the per-connector message processing jobs) do not declare a $tries count and do not configure backoff. They get whatever your queue connection's default is — which on a fresh Laravel install is 1 attempt, fail to failed_jobs, no retry.
If you want Laraclaw jobs to retry on transient failures (network blips, provider rate limits), set a default on your queue connection or wrap a custom listener around the events you care about.
!IMPORTANT Monitor
failed_jobsin production. A stuck reminder or a backed-up embedding queue will sit there silently — nothing will tell you about it unless you're watching.
Reloading Skills, Personas, and Prompts
The SkillRegistry is a singleton scanned once per process. Personas and the base system prompt are read from disk on every agent turn, but the file paths are resolved once at registration.
So after editing anything under laraclaw/ (instructions, personas, or skills), restart the queue worker (and Horizon supervisors, if you use them) to pick up the changes.
The terminal connector and the API connector boot a fresh process per invocation, so they see edits immediately. No restart needed there.
Scheduler
Reminders and heartbeats are dispatched by scheduled commands. You need the Laravel scheduler running:
php artisan schedule:work
Or, in production, a cron entry:
* * * * * cd /path/to/app && php artisan schedule:run >> /dev/null 2>&1
The service provider registers two commands to run every minute:
laraclaw:send-due-reminders— fires any one-shot reminders whose time has come.laraclaw:process-heartbeats— evaluates each active heartbeat's cron expression and dispatches matches.
Without the scheduler, reminders and heartbeats accumulate but never fire. Don't skip this one.
Redis
Laraclaw uses Redis for two things:
- The confirmation flow. When a tool needs confirmation (like deleting a file), the pending state is stored in Redis until the user replies "yes" or "no" through the same connector.
- The default queue driver. You can use any queue driver, but Redis is the easiest in production.
The service provider also registers a blocking Redis connection used internally by the confirmation BLPOP calls. There's nothing to configure manually — but Redis itself must be running.
IMAP Listener
If you've enabled the Email connector, the inbound mail listener must be running:
php artisan imap:watch default --with=headers,body
It's a long-running command from directorytree/imapengine-laravel. Run it under Supervisor or systemd:
[program:laraclaw-imap]
command=php /var/www/app/artisan imap:watch default --with=headers,body
autostart=true
autorestart=true
user=www-data
stdout_logfile=/var/log/laraclaw-imap.log
If the listener stops, inbound email stops being delivered. Everything else (Slack, Telegram, API, terminal) keeps working — it's an isolated failure.
pgvector
If you've enabled memory and your database is PostgreSQL, install the pgvector extension on the server:
CREATE EXTENSION vector;
Run this before the Laraclaw migrations. The embeddings migration detects pgvector at install time and creates either a native vector column or a JSON fallback. Switching after the fact requires a manual migration — so get this right up front.
Without PostgreSQL or pgvector, the JSON fallback works on any database. It's slower past a few thousand embeddings but functionally equivalent.
Multiple Nodes
Now, I know what you might be thinking: can I run this across multiple web nodes? The honest answer: Laraclaw has not been validated against multi-node deployments, and several known sharp edges make it risky.
Here's what to watch for:
- Scheduler duplication. The package registers
laraclaw:send-due-remindersandlaraclaw:process-heartbeatswith noonOneServer()lock. If your cron runs on more than one host, every minute both hosts will dispatch the same reminders and heartbeats. Pin the scheduler to exactly one node as the simplest fix. imap:watchis single-instance. Two listeners against the same mailbox will both see every message and dispatch it twice.- Attachments must live on a shared disk. Inbound files written by a connector worker on one node need to be readable by an agent job on another. Point
LARACLAW_ATTACHMENTS_DISKat S3 or another network filesystem. - Confirmation flow assumes one Redis. The blocking
BLPOPand theawaiting_confirm:key both live on the same Redis connection. As long as every node points at the same Redis, this is fine.
In short: run on a single host until you have a real reason not to. The single-owner architecture means you almost certainly don't need horizontal scaling.
Production Checklist
Run through this before flipping the bot live:
-
LARACLAW_ADMIN_USER_IDis set and points to a real user -
AI_DEFAULTis set and your provider key is configured - At least one connector is enabled and its webhook is registered
-
php artisan queue:workis running under a process supervisor -
schedule:workor a cron entry is in place - Redis is running and reachable
- If email is enabled,
imap:watchis running under a supervisor - If memory is enabled, embeddings are configured and pgvector is installed (if applicable)
-
LARACLAW_LOG_AGENT_REQUESTS=trueif you've enabled Tinker -
LARACLAW_WEBHOOK_RATE_LIMITis set to a sane value for your traffic - You've sent a test message through every enabled connector and confirmed the agent replies
Tick all of those? You're ready!
Until next time!