Runway
Runway is built and operated in the EU!

Faster Builds

The first rule of “faster builds” is to have less dependencies. But that’s not always possible.

Vendoring

Add all your dependencies into the project to avoid the download in Runway’s builder. The downside of this approach is that your repository is a bit larger as it contains all the libraries needed to build, but if we disregard this, then this is the best approach.

Steps include:

go mod verify
go mod tidy
go mod vendor
git add vendor
git commit -m 'Chore(deps): updated vendors'

Then, add the following config:

Runway CLI
runway app config set \
  BP_GO_BUILD_FLAGS=-mod=vendor
project.toml
[build]
  [[build.env]]
    name="BP_GO_BUILD_FLAGS"
    value="-mod=vendor"

Optimize structure

As we alluded in the basic guide, it may make sense to combine your processes into one binary in order to leverage Golang’s build caching effectively.

Instead of building multiple binaries (cmd/server, cmd/worker), combine them in one and leverage the power of CLI libraries (such as urfave/cli or cobra) to combine features into one binary:

./app server
./app worker

The following is an example with urfave/cli:

$ tree
├── Procfile
├── go.mod
├── go.sum
└── main.go
main.go
package main

import (
	"fmt"
	"log"
	"os"
	"context"

	"github.com/urfave/cli/v3"
)

// http handler
func hello(w http.ResponseWriter, req *http.Request) {
	log.Print("hello from runway's logs")
	fmt.Fprintf(w, "hello from Runway!\n")
}

func main() {
	cmd := &cli.Command{
		Commands: []*cli.Command{
			{
				Name:  "server",
				Usage: "my server",
				Flags: []cli.Flag{
					&cli.StringFlag{
						Name:    "port",
						Value:   "5000", // Runway default
						Usage:   "server port",
						Sources: cli.EnvVars("PORT"),
					},
				},
				Action: func(_ context.Context, cmd *cli.Command) error {
					http.HandleFunc("/", hello)
					log.Printf("listening on %s", cmd.String("port"))
					return http.ListenAndServe(":" + cmd.String("port"), nil)
				},
			},
			{
				Name: "worker",
				Action: func(_ context.Context, cmd *cli.Command) error {
					fmt.Println("I am a worker")
					return nil
				},
			},
		},
	}

	if err := cmd.Run(context.Background(), os.Args); err != nil {
		log.Fatal(err)
	}
}
Procfile
web: your-app-name server