Go
Despliega servicios de Go en Skiffly con Railpack — detección del módulo, la versión de Go, binarios estáticos y CGO, elegir qué `cmd/` compilar, `PORT`, Postgres y Redis, migraciones y health checks.
Qué detecta Railpack#
| Mira | Efecto |
|---|---|
go.mod, go.work o main.go en el directorio raíz | el servicio es una app de Go |
mise.toml/.tool-versions, luego la directiva go de go.mod, luego RAILPACK_GO_VERSION | la versión del toolchain (por defecto 1.23) |
paquete main en la raíz, o el primer cmd/* con main.go | qué se compila: go build -ldflags="-w -s" -o out |
RAILPACK_GO_BIN=api | compila ./cmd/api en lugar del primero que encuentre |
RAILPACK_GO_WORKSPACE_MODULE=services/api | con go.work, qué módulo compilar |
CGO_ENABLED=1 | binario dinámico con gcc/libc disponibles en el build y en runtime (lo necesitan mattn/go-sqlite3 y algunas librerías de imágenes); por defecto es un binario estático con CGO_ENABLED=0 |
La imagen final contiene el binario y poco más; el comando de inicio es el binario compilado. Las dependencias se descargan reutilizando la caché de módulos de Go entre builds. Los pasos de go generate, sqlc generate o templ que deban ejecutarse antes de compilar van en el comando de build (sqlc generate && go build -o out ./cmd/api), que reemplaza por completo el paso de build de Railpack — conserva el -o out para que el comando de inicio siga encontrando el binario, o define tu propio comando de inicio.
Puerto#
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}
log.Fatal(http.ListenAndServe(":"+port, mux))":"+port escucha en todas las interfaces; "localhost:"+port no, y el despliegue falla en su health check. Los frameworks (Gin, Echo, Fiber, Chi) reciben la misma cadena de dirección. Skiffly asigna a PORT el puerto del servicio, 8080 cuando no se define — así que un servicio de Go que usa 8080 por defecto no necesita ninguna configuración.
Postgres y 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 con lib/pq, GORM y sqlc aceptan la URL. go-redis interpreta REDIS_URL con redis.ParseURL. Ambos hosts están solo en la red privada.
Migraciones#
En Skiffly no hay fase de release. Opciones que funcionan:
- Al arrancar, en el código —
golang-migrateogoosecomo librería, ejecutados antes deListenAndServe(ambos toman un advisory lock para que las réplicas no compitan). El arranque tiene cinco minutos antes de que el health check se rinda. - Comando de inicio — instala la CLI en el build y encadena: comando de build
go install github.com/pressly/goose/v3/cmd/goose@latest && go build -o out ./cmd/api, comando de iniciosh -c "goose -dir migrations postgres \"$DATABASE_URL\" up && ./out". La imagen necesitash; la imagen de runtime de Railpack lo incluye. - Comando puntual —
skiffly ssh -- ./out migratesi el binario tiene un subcomando, o ejecuta la herramienta de migraciones desde tu máquina a través deskiffly connect postgres --print.
Health check#
mux.HandleFunc("/healthz", func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusOK) })Define /healthz como ruta del health check. Maneja SIGTERM con http.Server.Shutdown para que los rollouts y reinicios terminen las solicitudes en curso (Skiffly espera a que el contenedor salga después de enviar la señal).
Workers y cron#
Un worker es un segundo servicio del mismo repositorio con RAILPACK_GO_BIN=worker (o un comando de build que apunte a ./cmd/worker) y sin dominio. Tareas programadas: un servicio con programación cron cuyo binario se ejecuta una vez y sale.
Módulos privados#
Railpack descarga los módulos sin credenciales. Para módulos en repositorios privados, haz commit de un directorio vendor/ (go mod vendor; el build lo usa automáticamente), o cambia el servicio a un Dockerfile donde puedas montar un token como secreto de BuildKit (RUN --mount=type=secret,id=GITHUB_TOKEN …, consulta Docker).
Problemas comunes#
| Síntoma | Solución |
|---|---|
[railpack] no main package found | main.go no está en la raíz ni en cmd/*: define RAILPACK_GO_BIN o un comando de build |
Despliegue FAILED, el log muestra listening on localhost:8080 | escucha en : + puerto |
go: go.mod requires go >= 1.24 | Railpack respeta la directiva go; asegúrate de que sea una versión publicada, o fija RAILPACK_GO_VERSION |
undefined: sqlite3 / cgo: C compiler not found | CGO_ENABLED=1, o usa un driver en Go puro (modernc.org/sqlite) |
x509: certificate signed by unknown authority en runtime | la imagen de runtime tiene certificados CA; esto suele indicar un proxy corporativo o una URL HTTP, no un bundle ausente |
El binario funciona en local, exec format error en Skiffly | compilado de forma cruzada para la arquitectura equivocada; deja que Railpack lo compile (los nodos son linux/amd64) |
Ejemplo#
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