Skip to Content
Greyship Docs

Services

How Greyship finds your services and decides what each one is.

A service is one main package in your module. Greyship finds them by reading your code at deploy time, with no config. Every deploy prints what it found and why:

  api      web     ./cmd/api      starts a server in cmd/api/main.go:22 (http.ListenAndServe)
  worker   worker  ./cmd/worker   no server start found from main()
  release  once    ./cmd/migrate  cmd/migrate is the release command

Where services come from

Where the main package isResult
the module root (main.go) or cmd/main.goone service, named after the project
cmd/<name>/a service named <name>
anywhere else (services/orders/, apps/billing/cmd/server/)named after its folder; if two would share a name, the nearest meaningful parent is added (orders-server, payments-server)
cmd/migrate/the release command, not a service
vendor/, testdata/, examples/, folders starting with _ or ., files excluded from a Linux build, mains only run by //go:generateskipped, with the reason printed
a folder with its own go.modskipped (multi-module repos are coming)

go.mod must be at the root of what you deploy. Service names are lowercase with dashes and unique within the project; rename or include anything with greyship.toml.

Web or worker

Greyship follows each main() through your own packages and looks for a server being started: http.ListenAndServe, net.Listen, or a framework's equivalent (gin Run, echo Start, fiber Listen, gRPC Serve, fasthttp, hertz, Connect).

  • Found one → web: it gets a public HTTPS URL and sleeps when idle.
  • None → worker: it runs in the background with no URL.

Detection is wrong for your project? Set the type in greyship.toml:

[services.consumer]
type = "worker"

Workers during the beta

Workers keep running while deployed. Waking them on events (a built-in queue, schedules) is coming soon.

Crons

A cron is never guessed, because in code it looks just like a worker. Declare it:

[services.cleanup]
type = "cron"
schedule = "0 3 * * *"   # standard 5-field cron, UTC

The schedule is checked on every deploy. Running crons on their schedule is coming soon; until then a cron service is deployed but not started.

Release command

A release command runs once per deploy, before traffic switches: database migrations, one-off setup. It's cmd/migrate if you have one, or any main package you set:

[release]
command = "./cmd/migrate"
  • It runs in the same sandbox, with the same env vars, as your services.
  • Exit code 0 → the deploy continues. Anything else → the deployment fails, its output is in the log, and the live version keeps serving.
  • Rollbacks and redeploys don't run it again.

Migrations and rollbacks

A rollback doesn't undo a migration. Write migrations that the previous version can live with: add a column, deploy the code that uses it, and remove old columns in a later deploy (expand, then contract).

Every instance gets

VariableValue
PORT8080 (reading it is optional, see Ports)
GREYSHIP_SERVICEthe service's name
GREYSHIP_DEPLOYMENT_IDthe deployment it belongs to
GREYSHIP_REGIONwhere it runs, e.g. eu-1
GOMAXPROCSthe CPU limit, rounded up
GOMEMLIMIT90% of the memory limit, so Go's GC works before the limit is hit

Plus your environment variables. PORT and GREYSHIP_* can't be overridden; GOMAXPROCS and GOMEMLIMIT can.

On stop, an instance gets SIGTERM and has 10 seconds to finish before SIGKILL.

On this page