Skip to content
Runtime commands

Runtime commands

Generated projects wire steward.CLI in main.go, which turns your app binary into its own operations tool — migrations are Go code compiled into the app, so runtime operations run in the app, not in the global steward generator.

steward.CLI(steward.App{
    Build:      app.Build,       // constructs the configured *steward.Admin
    Migrations: migrations.All,  // the app's own migrations
    Jobs:       registerJobs,    // optional: recurring jobs (worker only)
})

Commands

CommandDoes
serve [-addr :8080]start the panel (default command)
workerrun App.Jobs on the scheduler — no HTTP
migrate upapply pending migrations
migrate down [-steps N]roll back (default: the last batch)
migrate statuslist every migration with batch and timestamp
menu:syncre-sync menu entries from registered resources
admin:create-usercreate a panel account (prompts for the password)

The worker process

Background jobs deliberately do not run inside serve: if they did, every extra web replica would fire every cron job again, and panel deploys would kill mid-flight work. Instead, register jobs on App.Jobs and run the same binary twice:

Jobs: func(a *steward.Admin, s steward.Scheduler) error {
    err := s.Add("@every 10m", "cleanup-sessions", func(ctx context.Context) error {
        return a.DB().WithContext(ctx).Exec("DELETE FROM ...").Error
    })
    if err != nil {
        return err
    }
    return s.Add("0 3 * * *", "nightly-report", sendReport)
},
./app serve    # deployment 1: the panel, scale as you like
./app worker   # deployment 2: exactly one instance runs the jobs

One build artifact, two independently deployable processes. The worker stops gracefully on SIGINT/SIGTERM.

Schedule specs: @every <duration> (any time.ParseDuration string), @hourly, @daily, @weekly, or a five-field cron expression (30 2 * * 1-5, minute resolution).

Note

Run a single worker instance unless your jobs are idempotent — there is no distributed locking between workers.

Deployment sketch

FROM golang:1.26 AS build
WORKDIR /src
COPY . .
RUN CGO_ENABLED=0 go build -o /app .

FROM gcr.io/distroless/static
COPY --from=build /app /app
ENTRYPOINT ["/app"]
CMD ["serve"]          # worker deployment overrides: CMD ["worker"]

Set STEWARD_SECRET (scaffolded apps read it for the session key) and your database DSN via the environment; run /app migrate up as a release step.