Pular para o conteúdo

Buscar na documentação

Buscar na documentação do Skiffly

Go

Deploy Go services on Skiffly with Railpack — module detection, the Go version, static binaries and CGO, choosing the `cmd/` to build, `PORT`, Postgres and Redis, migrations and health checks.

What Railpack detects#

Looks atEffect
go.mod, go.work or main.go in the root directorythe service is a Go app
mise.toml/.tool-versions, then the go directive in go.mod, then RAILPACK_GO_VERSIONthe toolchain version (default 1.23)
main package in the root, or the first cmd/* with main.gowhat gets compiled: go build -ldflags="-w -s" -o out
RAILPACK_GO_BIN=apibuild ./cmd/api instead of the first one found
RAILPACK_GO_WORKSPACE_MODULE=services/apiwith go.work, which module to build
CGO_ENABLED=1dynamic binary with gcc/libc available at build and runtime (needed by mattn/go-sqlite3, some image libraries); the default is a static binary with CGO_ENABLED=0

The final image contains the binary and little else; the start command is the compiled binary. Dependencies are downloaded with the Go module cache reused between builds. go generate, sqlc generate or templ steps that must run before compiling go into the build command (sqlc generate && go build -o out ./cmd/api), which replaces Railpack's build step entirely — keep the -o out so the start command still finds the binary, or set a start command of your own.

Port#

port := os.Getenv("PORT")
if port == "" {
    port = "8080"
}
log.Fatal(http.ListenAndServe(":"+port, mux))

":"+port binds to all interfaces; "localhost:"+port does not and the deployment fails its health check. Frameworks (Gin, Echo, Fiber, Chi) take the same address string. Skiffly sets PORT to the service port, 8080 when unset — so a Go service that defaults to 8080 needs no configuration at all.

Postgres and Redis#

skiffly deploy --template postgres
skiffly deploy --template redis
skiffly variables set 'DATABASE_URL=${{Postgres.DATABASE_URL}}' 'REDIS_URL=${{Redis.REDIS_URL}}'

pgx (pgxpool.New(ctx, os.Getenv("DATABASE_URL"))), database/sql with lib/pq, GORM and sqlc all accept the URL. go-redis parses REDIS_URL with redis.ParseURL. Both hosts are on the private network only.

Migrations#

No release phase on Skiffly. Options that work:

  • On boot, in codegolang-migrate or goose as a library, run before ListenAndServe (both take an advisory lock so replicas do not race). Startup has five minutes before the health check gives up.
  • Start command — install the CLI at build time and chain: build command go install github.com/pressly/goose/v3/cmd/goose@latest && go build -o out ./cmd/api, start command sh -c "goose -dir migrations postgres \"$DATABASE_URL\" up && ./out". The image needs sh; Railpack's runtime image has it.
  • One-offskiffly ssh -- ./out migrate if the binary has a subcommand, or run the migration tool from your machine through skiffly connect postgres --print.

Health check#

mux.HandleFunc("/healthz", func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusOK) })

Set /healthz as the healthcheck path. Handle SIGTERM with http.Server.Shutdown so rollouts and restarts finish in-flight requests (Skiffly waits for the container to exit after sending the signal).

Workers and cron#

A worker is a second service from the same repository with RAILPACK_GO_BIN=worker (or a build command targeting ./cmd/worker) and no domain. Scheduled jobs: a service with a cron schedule whose binary runs once and exits.

Private modules#

Railpack downloads modules with no credentials. For modules in private repositories either commit a vendor/ directory (go mod vendor; the build uses it automatically), or switch the service to a Dockerfile where you can mount a token as a BuildKit secret (RUN --mount=type=secret,id=GITHUB_TOKEN …, see Docker).

Common problems#

SymptomFix
[railpack] no main package foundmain.go is not in the root or cmd/*: set RAILPACK_GO_BIN or a build command
Deployment FAILED, log shows listening on localhost:8080bind to : + port
go: go.mod requires go >= 1.24Railpack honours the go directive; make sure it is a released version, or pin RAILPACK_GO_VERSION
undefined: sqlite3 / cgo: C compiler not foundCGO_ENABLED=1, or use a pure-Go driver (modernc.org/sqlite)
x509: certificate signed by unknown authority at runtimethe runtime image has CA certificates; this usually means a corporate proxy or an HTTP URL, not a missing bundle
Binary works locally, exec format error on Skifflycross-compiled for the wrong arch; let Railpack build it (nodes are linux/amd64)

Example#

.skiffly/skiffly.ts
import { defineSkiffly, github, postgres, project, service } from "@skiffly/config";
 
export default defineSkiffly(() => {
  const db = postgres("db");
  const api = service("api", {
    source: github("acme/api"),
    healthcheck: "/healthz",
    port: 8080,
    env: { DATABASE_URL: db.env.DATABASE_URL, RAILPACK_GO_BIN: "api" },
    domain: true,
  });
  return project("acme-api", { resources: [db, api] });
});

Landing: /deploy/go