Skip to content

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.

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.

bin/server.dart
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.

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:

Terminal window
PANEL=https://panel.example.com
H='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
}'
  • process apps have no internalPort and no domains.
  • The compiled binary is <workDir>/current/app. In the start command, the bare name app resolves to it.
  • systemd waits 30 seconds after SIGTERM before 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:

Terminal window
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.

Terminal window
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.

Set up a webhook on each app (Push-to-deploy webhooks). Each one gets its own secret and URL. Add both to the repository.

  • Raise --concurrency for I/O-bound tasks.
  • For CPU-bound tasks, create more worker apps (each is its own process and unit) instead, and give each a cpuQuotaPercent that fits the host.
  • A periodic scheduler (Beat) is another process app with its own entry point. Run exactly one.
Terminal window
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.