On this page

Entry points

Application executables load the same typed settings and use one application assembly path. Each main chooses its processes explicitly in Go.

The separate cmd/labena installer creates new applications and is not part of their runtime. See creating an application.

Select a process#

Entry point Runs
go run ./cmd/app HTTP, queue workers, notification dispatch and local event consumers
go run ./cmd/worker Queue workers, notification dispatch and local event consumers
go run ./cmd/scheduler Central recurring schedules; workers may run elsewhere
go run ./cmd/cli <command> One command, then cleanup
go run ./cmd/cli serve HTTP and workers, as selected in cmd/cli/main.go
APP_ENV_FILE=.env.quickstart.example go run ./cmd/app

For recurring maintenance, run a separate terminal with the same settings:

APP_ENV_FILE=.env.quickstart.example go run ./cmd/scheduler

Run exactly one scheduler per schedule set. Asynq scheduling has no distributed leader election. Ordinary app and worker replicas do not start schedules.

Define a server entry point#

This is the complete cmd/app/main.go:

package main

import (
	"context"
	"fmt"
	"os"
	"os/signal"
	"syscall"

	"github.com/wisalgroup/labena/internal/app"
	"github.com/wisalgroup/labena/internal/app/config"
)

func main() {
	ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
	defer stop()
	if err := run(ctx); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

func run(ctx context.Context) error {
	cfg, err := config.Load()
	if err != nil {
		return err
	}
	return app.Serve(ctx, cfg, app.Processes{HTTP: true, Workers: true})
}

The executable owns configuration loading, signal handling and exit status. Serve validates, assembles, runs and cleans up before returning. To make this executable HTTP-only, change its explicit selection to:

return app.Serve(ctx, cfg, app.Processes{HTTP: true})

Then run cmd/worker separately against the same queue Redis. HTTP and workers include local event consumers automatically. GoChannel does not share events between processes; each producing process keeps its own consumers.

A zero Processes value is an error for serving. Environment settings cannot change the selection. Existing deployments should remove APP_ROLES, QUEUE_SCHEDULER_ENABLED and QUEUE_COMMAND_SCHEDULING_ENABLED, then choose the appropriate executable or explicit Processes value. Scheduled-command handling is always registered.

Keep process responsibilities distinct#

Work HTTP selection Workers selection Scheduler selection Ordinary CLI
Serve HTTP Yes No No No
Consume local events Yes Yes No No
Consume queued tasks No Yes No No
Dispatch notification intents No Yes No Explicit command only
Enqueue recurring work No No Yes No
Construct configured dependencies and register modules Yes Yes Yes Yes

Process selection controls execution. The module graph and command registrations remain complete for all executables.

Handle stop signals#

Interrupt and SIGTERM cancel the invocation context. App stops admission, drains work and releases clients. Do not call os.Exit in a service or handler. The main reports errors and chooses the exit code after app has completed cleanup.

See console, scheduling and application lifecycle for execution details.

Documentation

Search guides, sections, and API names.