Run gisila_queue workers
In this guide you run gisila_queue workers for a Gisila API on the
same panel node. The worker is a second app built from the same repository,
with exposure mode process: no port, no domain, kept alive by systemd.
Prerequisites
Section titled “Prerequisites”- An API already deployed on the panel (First deploy of a Gisila app).
- A Redis the worker and API can share. A superuser can install one as a managed service (Provision Postgres and Redis).
- The
developerrole on the team.
1. Let the server binary also run the worker
Section titled “1. Let the server binary also run the worker”The panel’s Dart runtime compiles bin/server.dart by default, and a custom
build command can’t chain steps with && (the agent refuses shell
metacharacters). The simplest setup is one binary with two modes: it runs the
worker when its first argument is worker, and the HTTP server otherwise.
Both apps then use the default build.
The WorkerCli helper reads REDIS_URL from the environment, takes the
namespace, queues and concurrency as flags, and stops gracefully on the
SIGTERM systemd sends on restart.
import 'package:gisila_queue/gisila_queue.dart';import 'package:gisila_queue/gisila_queue_cli.dart';
Future<void> main(List<String> argv) async { if (argv.isNotEmpty && argv.first == 'worker') { return runWorker(argv.sublist(1)); } // ...start the HTTP server as before...}
Future<void> runWorker(List<String> argv) async { final cli = WorkerCli.parse(argv); if (cli.help) return print(WorkerCli.usage); final app = cli.buildApp();
// Register the same tasks your API enqueues. app.task<void>('emails.send_welcome', (ctx) async { final userId = ctx.arg(0) as int; // ... });
await cli.run(app);}Use a namespace of your own (for example billing) in both the API and the
worker. Two apps that share a Redis database and a namespace take each
other’s tasks.
2. Create the worker app
Section titled “2. Create the worker app”Create a second app in the same project, pointing at the same repository,
with exposeMode: "process" and a start command that selects the worker
mode:
PANEL=https://panel.example.comH='Authorization: Bearer gsl_xxxxxxxxxxxxxxxx'J='Content-Type: application/json'
curl -sX POST $PANEL/apps/ -H "$H" -H "$J" -d '{ "projectId": 12, "name": "billing-worker", "runtime": "dart", "sourceType": "git", "gitUrl": "https://github.com/acme/billing-api.git", "gitBranch": "main", "exposeMode": "process", "startCommand": "app worker --namespace=billing --queues=default,emails --concurrency=8 --drain-timeout=25", "memoryMbLimit": 256}'processapps have nointernalPortand no domains.- The compiled binary is
<workDir>/current/app. In the start command, the bare nameappresolves to it. - systemd waits 30 seconds after
SIGTERMbefore it kills the process. A drain timeout below that lets running tasks finish on restart. - Start and build commands can’t contain
|,&,;,$,<,>or backticks.
3. Give it the Redis URL and the rest of its config
Section titled “3. Give it the Redis URL and the rest of its config”The worker needs the same env vars as the API for anything its tasks touch:
curl -sX POST $PANEL/apps/43/envs -H "$H" -H "$J" \ -d '{"name": "REDIS_URL", "value": "redis://:<password>@127.0.0.1:6380/0", "isSecret": true}'curl -sX POST $PANEL/apps/43/envs -H "$H" -H "$J" \ -d '{"name": "DATABASE_URL", "value": "postgresql://…", "isSecret": true}'Set the same REDIS_URL on the API so producers and workers share a broker.
4. Deploy
Section titled “4. Deploy”curl -sX POST $PANEL/apps/43/deployments/ -H "$H" -H "$J" -d '{"sourceType": "git"}'For a process app, the health check passes once the unit is active and
hasn’t restarted for 5 seconds.
5. Deploy both on every push
Section titled “5. Deploy both on every push”Set up a webhook on each app (Push-to-deploy webhooks). Each one gets its own secret and URL. Add both to the repository.
Scaling
Section titled “Scaling”- Raise
--concurrencyfor I/O-bound tasks. - For CPU-bound tasks, create more worker apps (each is its own process and
unit) instead, and give each a
cpuQuotaPercentthat fits the host. - A periodic scheduler (
Beat) is anotherprocessapp with its own entry point. Run exactly one.
Verify it works
Section titled “Verify it works”curl -s "$PANEL/apps/43/runtime-logs/?lines=50" -H "$H" | jq -r '.lines[]'# worker <id> → queues=default,emails concurrency=8 (redis 127.0.0.1:6380, namespace "billing")Enqueue a task from the API and watch it appear in the worker’s log.