Laravel on Askr — the recommended setup
This is the end-to-end guide to running a Laravel application on Askr with the
official kwhorne/askr-laravel
package. Follow it top to bottom and you get a single-box (or multi-box) stack
with no Redis, no Horizon, no separate WebSocket server, and no cron — session,
cache, locks, queue and broadcasting all served from the Askr binary.
New to Askr's model? Skim Worker mode first — Laravel boots once and serves many requests (Octane-style), so a few things behave differently from PHP-FPM.
What the package gives you
.env |
Replaces | Backed by |
|---|---|---|
SESSION_DRIVER=askr |
Redis / DB / file sessions |
shared-memory large region |
CACHE_STORE=askr |
Redis cache, counters, rate limiting, Cache::lock() |
shared-memory cache |
QUEUE_CONNECTION=askr |
Redis / DB queues + Horizon | shared-memory job queue + supervised workers |
BROADCAST_CONNECTION=askr |
Redis pub/sub + Reverb/Pusher | in-binary pub/sub + Pusher-compatible WS |
Each driver is registered automatically by Askr\Laravel\AskrServiceProvider
(Laravel package auto-discovery) — no manual wiring. They all call Askr's
askr_* builtins, which exist only when the app is served by Askr with the
matching shared-memory regions enabled (see Regions & sizing).
Two tiers are available per driver:
- L1 (default) — shared memory, mapped before fork, shared across every worker on the box. Fast, lock-free-ish, and gone on reboot.
- L2 (optional) — a durable, replicated backend over SQL Anywhere, selected at
runtime with
ASKR_*_DB. Same PHP API, only the backend differs. See Durable L2.
Requirements
- Askr ≥ 0.9.3 (broadcasting driver + the complete Laravel surface).
- PHP 8.5 (bundled in the Askr release/Docker image).
- Laravel 11, 12 or 13.
- A worker-mode deployment (
--worker-script). Per-request mode works too, but the session/cache/queue regions and the recommended performance all assume workers.
1. Install
composer require kwhorne/askr-laravel
That's it — the service provider is auto-discovered. Nothing to add to
config/app.php.
2. Configure .env
SESSION_DRIVER=askr
CACHE_STORE=askr
QUEUE_CONNECTION=askr
BROADCAST_CONNECTION=askr
3. Register the stores/connections
Add the matching definitions so Laravel knows what askr means. (Cache and
session usually resolve from the driver alone, but being explicit is clearer and
required for queue + broadcasting.)
// config/cache.php → 'stores'
'askr' => ['driver' => 'askr'],
// config/queue.php → 'connections'
'askr' => [
'driver' => 'askr',
'queue' => 'default',
'retry_after' => 90,
],
// config/broadcasting.php → 'connections'
'askr' => ['driver' => 'askr'],
Sessions ride on the cache large region, so no extra config is needed beyond
SESSION_DRIVER=askr — just make sure Askr runs with --cache-large-slots
(below).
4. The runner scripts
Askr serves your app through small runner scripts that boot Laravel once and then handle requests/jobs in a loop. Copy the three bundled examples into your app so they're versioned with your code:
mkdir -p askr
cp /opt/askr/examples/laravel-worker.php askr/worker.php # HTTP
cp /opt/askr/examples/askr-queue.php askr/queue.php # queue:work
cp /opt/askr/examples/askr-scheduler.php askr/schedule.php # schedule:run
(The Docker image ships them under /opt/askr/examples; the release tarball under
<prefix>/examples.) They read ASKR_APP_BASE to find your app root and reset
per-request state between requests. See Worker mode if you want
to customise the reset.
5. Run Askr
Development
ASKR_APP_BASE="$PWD" askr serve \
--root public \
--worker-script askr/worker.php \
--workers 2 \
--cache-slots 8192 --cache-large-slots 4096 \
--queue-slots 8192 \
--pusher \
--listen 127.0.0.1:8000
Production (the full stack in one command)
ASKR_APP_BASE=/srv/app askr serve \
--root public \
--worker-script askr/worker.php \
--workers auto \
--max-rss 400 \
--cache-slots 16384 --cache-large-slots 4096 \
--queue-slots 8192 \
--queue 1 --queue-max 8 --queue-script askr/queue.php \
--scheduler-script askr/schedule.php \
--pusher \
--admin 127.0.0.1:9090 \
--acme --acme-domain app.example.com --acme-email ops@example.com \
--listen 0.0.0.0:443
One process tree now covers: HTTP, cache, sessions, locks, the job queue and its
autoscaled workers, the scheduler, broadcasting, TLS, and a metrics/admin plane.
Prefer a config file in production — see Configuration for the
askr.toml equivalent, and Ubuntu setup for the systemd unit.
Regions & sizing
The shared-memory regions are fixed at startup and evict oldest-first when full. Size them for your peak concurrency:
| Flag | Backs | Guidance |
|---|---|---|
--cache-slots N |
cache, counters, rate limiting, Cache::lock() (≤ 4 KB values) |
a few × your working set of keys |
--cache-large-slots N |
sessions, cache fragments, serialized collections (≤ 64 KB) | ≈ your peak concurrent session count |
--queue-slots N |
the job queue (askr_queue_*) |
≥ your peak pending + delayed jobs (32 KB/slot) |
Integers/floats are stored unserialized, so Cache::increment() (the rate
limiter) is truly atomic across all workers.
Queue workers & autoscaling
--queue N runs N supervised queue-worker processes. Add --queue-max M to turn
it into a backlog-driven autoscaling range — Horizon's balance=auto, native,
with no extra daemon (Askr sees the backlog in shared memory and owns the worker
pool):
--queue 1 --queue-max 8 --queue-script askr/queue.php
On a burst the pool jumps toward the target (~1 worker per 10 ready jobs), then
drains one worker every couple of seconds as the backlog clears — gracefully
(scaled-down workers finish their current job, then exit). Watch it on
/metrics: askr_queue_workers, askr_queue_ready, askr_queue_total,
askr_queue_oldest_seconds.
Named queues / priority work as usual — set --queue-script to a runner that does
queue:work --queue=high,default,low.
Scheduler
--scheduler-script askr/schedule.php runs Laravel's scheduler in-process — no
system cron entry. It's a supervised sidecar; it's respawned if it dies.
Broadcasting (Laravel Echo)
With BROADCAST_CONNECTION=askr and --pusher, broadcast(new Event()) publishes
a Pusher-shaped frame through Askr's in-binary pub/sub, and Askr's
Pusher-compatible WebSocket fan-out delivers it to Echo clients — no Reverb, no
Pusher account, no Redis.
Point Laravel Echo at Askr (it speaks the Pusher protocol):
import Echo from 'laravel-echo';
import Pusher from 'pusher-js';
window.Pusher = Pusher;
window.Echo = new Echo({
broadcaster: 'pusher',
key: 'askr', // any non-empty key
wsHost: window.location.hostname,
wsPort: 443, // your Askr listen port
forceTLS: true,
enabledTransports: ['ws', 'wss'],
disableStats: true,
});
Public channels work out of the box. Private/presence channels use Laravel's
standard channel authorization (routes/channels.php) — no extra config. There's
also a plain SSE endpoint (GET /askr/events?channel=NAME) if you'd rather not
use Echo; see Broadcasting.
Durable L2 (optional)
For durability across restarts or multiple boxes, enable the SQL Anywhere L2 tier. It's behind a build feature, so the default build is unaffected:
# build once with the feature
cargo build --release --features sql-backend
# select per subsystem at runtime; unset falls back to L1 shared memory
ASKR_CACHE_DB=/var/lib/askr/cache.db
ASKR_QUEUE_DB=/var/lib/askr/queue.db
ASKR_BROADCAST_DB=/var/lib/askr/events.db
The PHP API and Laravel drivers don't change — only the backend does. When both L1 and L2 are on, L1 becomes a write-through read cache in front of L2: hot reads skip the database round-trip, writes go to L2 (the source of truth). Queue autoscaling reads its backlog from L2 automatically. See Storage backends.
Production checklist
- Sessions:
--cache-large-slots≥ peak concurrent sessions. Never useSESSION_DRIVER=arrayin workers — it leaks into the PHP heap until OOM. - Leak safety: set
--max-rss <MB>(well belowmemory_limit) so a worker is recycled gracefully before it OOMs, and/or use--cowfor ~ms warm respawns. - Config/route/view cache: run
php artisan config:cache route:cache view:cacheat deploy, then reload Askr (SIGHUP) so workers pick up the new code. - TLS:
--acmegets and renews Let's Encrypt certs with no proxy (Auto-TLS). - Hardening:
--sandbox(seccomp no-exec) and--sandbox-write storage /tmp(Landlock) shrink the blast radius of an RCE (Sandbox, Linux). - Observability:
--admin 127.0.0.1:9090exposes/metrics(Prometheus) and a dashboard (Admin).
Verify it works
# 1) the package resolves + installs from Packagist
composer require kwhorne/askr-laravel
# 2) drivers are live (with --admin on): the queue gauges appear
curl -s http://127.0.0.1:9090/metrics | grep askr_queue_
# 3) a cache round-trip from tinker (while served by Askr)
php artisan tinker --execute="Cache::put('k','v',60); echo Cache::get('k');"
doctor also checks the required extension set:
askr doctor --root public
Migrating from Redis
| You had | You now set | Notes |
|---|---|---|
CACHE_STORE=redis |
CACHE_STORE=askr |
counters/rate limiter/Cache::lock() all carry over |
SESSION_DRIVER=redis |
SESSION_DRIVER=askr |
needs --cache-large-slots |
QUEUE_CONNECTION=redis + Horizon |
QUEUE_CONNECTION=askr + --queue/--queue-max |
autoscaling replaces Horizon balance=auto |
BROADCAST_CONNECTION=reverb/pusher |
BROADCAST_CONNECTION=askr + --pusher |
Echo config points at Askr |
redis-server, horizon, reverb, cron |
(nothing) | all folded into the Askr process tree |
Drop Redis entirely on a single box; reach for the L2 SQL Anywhere tier when you need durability or more than one box.
Troubleshooting
Driver [askr] not supported— the app wasn't served by Askr (theaskr_*builtins are absent), or the region for that driver isn't enabled. Check you passed--cache-slots/--queue-slotsetc., and that you're running underaskr serve, notphp artisan serve.- Sessions not persisting / evicted —
--cache-large-slotsis too small for your concurrency; increase it. php artisan …outside Askr — CLI commands run under system PHP where theaskr_*builtins don't exist; the session driver degrades to a no-op and the cache/queue drivers will error if resolved. Don't point long-running CLI at theaskrdrivers — run queue/schedule through Askr's runner scripts instead.- Broadcasting silent — confirm
--pusheris set and the EchowsPort/host match your--listen.
See also: Worker mode · Shared cache · Broadcasting · Storage backends · Deployment.