Deploying a Go service: the build that doesn't need a runtime
A Go build produces one statically linked binary, with nothing left to resolve at runtime — no interpreter version to pin, no gems or packages to fetch on the server that’s about to run the app. That’s a genuinely different thing to build than most interpreted-language apps, and it shows up as fewer places for a deploy to go wrong: once the binary compiles, it runs, full stop.
What gets detected
- Go itself is detected from
go.mod— including a module that lives in a subfolder (server/go.modnext to afrontend/directory), found and built from the right place automatically. - A known database driver import —
github.com/jackc/pgx,github.com/lib/pq,github.com/go-sql-driver/mysql,github.com/redis/go-redis— provisions the matching database and injectsDATABASE_URLorREDIS_URL, with no addon to add by hand first. - The build runs through Railpack by default, or your own Dockerfile if the repo has one.
railyard create my-app https://github.com/me/my-app --port 8080
or the same URL from the dashboard once GitHub’s connected. The one requirement worth stating plainly: the binary has to listen on $PORT, and on all interfaces, not just localhost.
Workers, since Go has no framework convention to detect
Unlike Rails or Django, Go doesn’t have one dominant job-queue library to look for — there’s nothing to auto-offer as a worker. That’s fine, because it’s one line either way:
web: ./server
worker: ./server -mode=worker
Both lines run the exact same built binary; main() reads a flag or env var to decide which loop to actually run. One build, several process types, each scaled independently:
railyard scale my-app worker=2
For anything that runs on a schedule rather than continuously, Cron runs the binary with a different flag at the scheduled time and keeps a run history — a cleaner model than a for { time.Sleep(...) } loop baked into the worker process.
Config and secrets
Everything read with os.Getenv is a plain env var under Variables, or set with railyard env:set my-app KEY=VALUE — never compiled into the binary, so the exact same build artifact runs unchanged from staging through production. No recompile per environment, which is the point: a config difference between staging and prod should be a variable, never a different build.
Building your own image, when you want to
railyard deploy my-app --local builds with your own Docker and pushes a single image directly, instead of using the shared team builder — useful when a build needs something specific to your local toolchain that the standard build path doesn’t need to know about.
Full reference: Go docs, Workers and scheduled jobs.