Deployment
Docker, Railway, and Cloudflare deployment for GOaT apps.
A GOaT app compiles to a single binary. Deployment is copying that binary somewhere and running it.
Dockerfile
# Build stage
FROM golang:1.22-alpine AS build
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o server ./cmd/server
# Runtime stage
FROM scratch
COPY --from=build /app/server /server
COPY --from=build /app/templates /templates
COPY --from=build /app/static /static
EXPOSE 8080
CMD ["/server"]
The final image is ~15MB. No OS, no runtime, just your binary and assets.
Railway
Railway auto-detects Go projects. Add a Procfile or set the start command:
web: ./server
Set environment variables in the Railway dashboard:
DATABASE_URL=postgres://...
REDIS_URL=redis://...
PORT=8080
Railway provides managed PostgreSQL and Redis as add-ons.
Direct Deploy
For a VPS or bare metal:
GOOS=linux GOARCH=amd64 go build -o server ./cmd/server
scp server templates/ static/ yourserver:/app/
ssh yourserver "systemctl restart myapp"
Cloudflare (Static Sites)
For static sites (like this docs site), use Cloudflare Pages or Workers:
wrangler.jsonc:
{
"name": "myapp",
"compatibility_date": "2024-01-01",
"site": {
"bucket": "./public"
}
}
hugo && npx wrangler pages deploy public
Environment Variables
Keep config in environment variables:
func main() {
port := os.Getenv("PORT")
if port == "" { port = "8080" }
dbURL := os.Getenv("DATABASE_URL")
redisURL := os.Getenv("REDIS_URL") // Optional
pool, _ := pgxpool.New(ctx, dbURL)
sseHub := service.NewSSEHub(redisURL)
log.Fatal(http.ListenAndServe(":"+port, mux))
}
Health Checks
Always include a health endpoint:
mux.HandleFunc("GET /health", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("OK"))
})
Gotchas
FROM scratchmeans no shell. You can’texecinto the container. UseFROM alpineif you need debugging.- Copy templates and static files. The binary doesn’t embed them by default. Use
go:embedif you want a truly single-file deploy. - Set
CGO_ENABLED=0for static compilation. Without it, you’ll get dynamic linking errors onscratch. - Redis is optional. Your SSE hub works locally without it. Add Redis when you scale to multiple instances.